diff --git a/Cargo.lock b/Cargo.lock
index aa21e00e91c0dd6ab1e984682c784470969547a4..efe43717b4a900c2d06bf47202bce7b4983235d2 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,24 +4,24 @@ version = 3
 
 [[package]]
 name = "accesskit"
-version = "0.11.0"
+version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02c98a5d094590335462354da402d754fe2cb78f0e6ce5024611c28ed539c1de"
+checksum = "ca8410747ed85a17c4a1e9ed3f5a74d3e7bdcc876cf9a18ff40ae21d645997b2"
 
 [[package]]
 name = "accesskit_consumer"
-version = "0.15.0"
+version = "0.16.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca541e0fdb600916d196a940228df99b86d804fd2e6ef13894d7814f2799db43"
+checksum = "8c17cca53c09fbd7288667b22a201274b9becaa27f0b91bf52a526db95de45e6"
 dependencies = [
  "accesskit",
 ]
 
 [[package]]
 name = "accesskit_macos"
-version = "0.7.1"
+version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4baea9413f0daf1cd4aab199bc09f8139cd726ce7673d523c27d186b8b878325"
+checksum = "cd3b6ae1eabbfbced10e840fd3fce8a93ae84f174b3e4ba892ab7bcb42e477a7"
 dependencies = [
  "accesskit",
  "accesskit_consumer",
@@ -31,23 +31,23 @@ dependencies = [
 
 [[package]]
 name = "accesskit_windows"
-version = "0.14.0"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e11c7f177739f23bd19bb856e4a64fdd96eb8638ec0a6a6dde9a7019a9e91c53"
+checksum = "afcae27ec0974fc7c3b0b318783be89fd1b2e66dd702179fe600166a38ff4a0b"
 dependencies = [
  "accesskit",
  "accesskit_consumer",
- "arrayvec",
  "once_cell",
  "paste",
- "windows",
+ "static_assertions",
+ "windows 0.48.0",
 ]
 
 [[package]]
 name = "accesskit_winit"
-version = "0.14.1"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14f1bd64cd0b480cafb7bdd91eb489a1ff50f0f5702437b9efa32a25b8bb82a1"
+checksum = "88e39fcec2e10971e188730b7a76bab60647dacc973d4591855ebebcadfaa738"
 dependencies = [
  "accesskit",
  "accesskit_macos",
@@ -162,21 +162,37 @@ dependencies = [
  "num-traits",
 ]
 
+[[package]]
+name = "arrayref"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545"
+
 [[package]]
 name = "arrayvec"
-version = "0.7.2"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
+checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
 
 [[package]]
 name = "ash"
-version = "0.37.2+1.3.238"
+version = "0.37.3+1.3.251"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28bf19c1f0a470be5fbf7522a308a05df06610252c5bcf5143e1b23f629a9a03"
+checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
 dependencies = [
  "libloading 0.7.4",
 ]
 
+[[package]]
+name = "async-broadcast"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b"
+dependencies = [
+ "event-listener",
+ "futures-core",
+]
+
 [[package]]
 name = "async-channel"
 version = "1.8.0"
@@ -197,16 +213,28 @@ dependencies = [
  "async-lock",
  "async-task",
  "concurrent-queue",
- "fastrand",
+ "fastrand 1.9.0",
  "futures-lite",
  "slab",
 ]
 
+[[package]]
+name = "async-fs"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06"
+dependencies = [
+ "async-lock",
+ "autocfg",
+ "blocking",
+ "futures-lite",
+]
+
 [[package]]
 name = "async-lock"
-version = "2.7.0"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7"
+checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
 dependencies = [
  "event-listener",
 ]
@@ -217,6 +245,12 @@ version = "4.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae"
 
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
 [[package]]
 name = "autocfg"
 version = "1.1.0"
@@ -246,18 +280,18 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
 
 [[package]]
 name = "bevy"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04a90fe8e9c03fa2d30acf39a5178a48526df00c1ccea2fc43fa6d9ca4d8a168"
+checksum = "329e344f835f5a9a4c46a6d1d57371f726aa2c482d1bd669b2b9c4eb1ee91fd7"
 dependencies = [
  "bevy_internal",
 ]
 
 [[package]]
 name = "bevy_a11y"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f758f437d9d862bf10a8e3a0f76b426095c19a87d118c945dcb935358d856076"
+checksum = "271b812e5734f5056a400f7d64592dd82d6c0e6179389c2f066f433ab8bc7692"
 dependencies = [
  "accesskit",
  "bevy_app",
@@ -267,9 +301,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_app"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1817e8d5b1146ea9e7730a7264d3470394840e0754d15abded26473f867967a0"
+checksum = "172d532ea812e5954fa814dae003c207f2a0b20c6e50431787c94a7159677ece"
 dependencies = [
  "bevy_derive",
  "bevy_ecs",
@@ -283,24 +317,29 @@ dependencies = [
 
 [[package]]
 name = "bevy_asset"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e12f951d4af2ad4ad230cd7bcb05248149c415eec17c34bf26731c4cd8b897f"
+checksum = "ccb2b67984088b23e223cfe9ec1befd89a110665a679acb06839bc4334ed37d6"
 dependencies = [
- "anyhow",
+ "async-broadcast",
+ "async-fs",
+ "async-lock",
  "bevy_app",
- "bevy_diagnostic",
+ "bevy_asset_macros",
  "bevy_ecs",
  "bevy_log",
  "bevy_reflect",
  "bevy_tasks",
  "bevy_utils",
  "bevy_winit",
+ "blake3",
  "crossbeam-channel",
  "downcast-rs",
- "fastrand",
+ "futures-io",
+ "futures-lite",
  "js-sys",
  "parking_lot",
+ "ron",
  "serde",
  "thiserror",
  "wasm-bindgen",
@@ -308,11 +347,23 @@ dependencies = [
  "web-sys",
 ]
 
+[[package]]
+name = "bevy_asset_macros"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b3245193e90fc8abcf1059a467cb224501dcda083d114c67c10ac66b7171e3a"
+dependencies = [
+ "bevy_macro_utils",
+ "proc-macro2 1.0.56",
+ "quote 1.0.26",
+ "syn 2.0.15",
+]
+
 [[package]]
 name = "bevy_core"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "263b6a943ecba176c8390a1100615021f61a3b2d7a87e8eecf4009b6ed4457e0"
+checksum = "025e6800b73048092a55c3611e9327ad4c4c17b60517ec1c0086bb40b4b19ea8"
 dependencies = [
  "bevy_app",
  "bevy_ecs",
@@ -325,15 +376,16 @@ dependencies = [
 
 [[package]]
 name = "bevy_core_pipeline"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50c70113b5c4106855b888f96d8574697eb9082713f976c9b6487c1f5ab28589"
+checksum = "2e4b08a2d53ba62d9ec1fca3f7f4e0f556e9f59e1c8e63a4b7c2a18c0701152c"
 dependencies = [
  "bevy_app",
  "bevy_asset",
  "bevy_core",
  "bevy_derive",
  "bevy_ecs",
+ "bevy_log",
  "bevy_math",
  "bevy_reflect",
  "bevy_render",
@@ -346,9 +398,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_derive"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1477347b17df781756ba0dfd677e2975e57e930752cd3cd42e6cdd8fdaa3223"
+checksum = "24bf40259be12a1a24d9fd536f5ff18d31eeb5665b77e2732899783be6edc5d6"
 dependencies = [
  "bevy_macro_utils",
  "quote 1.0.26",
@@ -357,9 +409,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_diagnostic"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37a594f970c261007cdd3edeccd61651c2cb4513de3d0b8b35d93f5d9c32c059"
+checksum = "41b5a99a9fb6cd7d1eb1714fad193944a0317f0887a15cccb8309c8d37951132"
 dependencies = [
  "bevy_app",
  "bevy_core",
@@ -372,9 +424,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_ecs"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "032c81ba7d919c1004b0abc33cc6c588c8f896a4d7c55a7c7aa1e46382242f43"
+checksum = "ae11a1f467c372b50e9d4b55e78370f5420c9db7416200cc441cc84f08174dd3"
 dependencies = [
  "async-channel",
  "bevy_ecs_macros",
@@ -393,9 +445,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_ecs_macros"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a15ff7fcafdb8fe464ddd300b4860a76d5c6f9d684472e4bf21852d6f0ff3991"
+checksum = "f642c2b67c4d0daf8edf15074f6351457eb487a34b3de1290c760d8f3ac9ec16"
 dependencies = [
  "bevy_macro_utils",
  "proc-macro2 1.0.56",
@@ -405,9 +457,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_encase_derive"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bdf808dbdc68a0c519e09026c627bda85250205a40ac02794866bff254d6b56"
+checksum = "65b9fb5a62c4e3ab70caaa839470d35fa932001b1b34b08bc7f7f1909bd2b3a7"
 dependencies = [
  "bevy_macro_utils",
  "encase_derive_impl",
@@ -415,9 +467,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_gizmos"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7938b43b4bdf9d039b7d3b310f871ed5ffa5a185e861a9c85731c40182019f8d"
+checksum = "87d1cc978b91f416b23eb16f00e69f95c3a04582021827d8082e92d4725cc510"
 dependencies = [
  "bevy_app",
  "bevy_asset",
@@ -434,9 +486,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_hierarchy"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba41e1bb0c367b31e59b53ab858de56764c78bee87c121843c1ff033efa0086c"
+checksum = "64fa240011fce8ee23f9b46e5a26a628a31d7860d6d2e4e0e361bb3ea6d5a703"
 dependencies = [
  "bevy_app",
  "bevy_core",
@@ -449,9 +501,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_input"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7221091c7b219a63a1f3f019512e8b72bed673230b97c3fcbca37ba566b1cffb"
+checksum = "9e86e241b3a10b79f65a69205552546723b855d3d4c1bd8261637c076144d32f"
 dependencies = [
  "bevy_app",
  "bevy_ecs",
@@ -463,9 +515,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_internal"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f232e7bd2566abd05656789e3c6278a5ca2a24f1232dff525e5b0233a99a610"
+checksum = "55124e486814c4d3632d5cfad9c4f4e46d052c028593ec46fef5bfbfb0f840b1"
 dependencies = [
  "bevy_a11y",
  "bevy_app",
@@ -494,9 +546,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_log"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "487dfd1fc75fada8f3f2f4773addf3fbba53a2a91cb913616e6dc6c26dd62995"
+checksum = "011417debf7868b45932bb97fc0d5bfdeaf9304e324aa94840e2f1e6deeed69d"
 dependencies = [
  "android_log-sys",
  "bevy_app",
@@ -510,21 +562,22 @@ dependencies = [
 
 [[package]]
 name = "bevy_macro_utils"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd3868e555723249fde3786891f35893b3001b2be4efb51f431467cb7fc378cd"
+checksum = "cf6fba87c6d069fcbcd8a48625ca8ab4392ad40d2b260863ce7d641a0f42986d"
 dependencies = [
+ "proc-macro2 1.0.56",
  "quote 1.0.26",
  "rustc-hash",
  "syn 2.0.15",
- "toml_edit",
+ "toml_edit 0.20.7",
 ]
 
 [[package]]
 name = "bevy_math"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25088c6598fe0b8ded992c781dc49e613993c7a4e6a731c0f2ab0408add6afdb"
+checksum = "752764558a1f429c20704c3b836a019fa308961c43fdfef4f08e339d456c96be"
 dependencies = [
  "glam",
  "serde",
@@ -532,24 +585,24 @@ dependencies = [
 
 [[package]]
 name = "bevy_mikktspace"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99dde80034660f7dbb473141c31f0a746acc7229f5a06ce769aba5f16fd592ab"
+checksum = "b596c41a56f2268ec7cde560edc588bc7b5886e4b49c8b27c4dcc9f7c743424c"
 dependencies = [
  "glam",
 ]
 
 [[package]]
 name = "bevy_ptr"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c74fcf37593a0053f539c3b088f34f268cbefed031d8eb8ff0fb10d175160242"
+checksum = "308a02679f6ce21ef71de20fae6d6a2016c07baa21d8e8d0558e6b7851e8adf2"
 
 [[package]]
 name = "bevy_reflect"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "362492a6b66f676176705cc06017b012320fa260a9cf4baf3513387e9c05693e"
+checksum = "cdd56914a8ad57621d7a1a099f7e6b1f7482c9c76cedc9c3d4c175a203939c5d"
 dependencies = [
  "bevy_math",
  "bevy_ptr",
@@ -558,8 +611,6 @@ dependencies = [
  "downcast-rs",
  "erased-serde",
  "glam",
- "once_cell",
- "parking_lot",
  "serde",
  "smallvec",
  "smol_str",
@@ -568,12 +619,11 @@ dependencies = [
 
 [[package]]
 name = "bevy_reflect_derive"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e974d78eaf1b45e1b4146711b5c16e37c24234e12f3a52f5f2e28332c969d3c"
+checksum = "25f627907c40ac552f798423447fc331fc1ddacd94c5f7a2a70942eb06bc8447"
 dependencies = [
  "bevy_macro_utils",
- "bit-set",
  "proc-macro2 1.0.56",
  "quote 1.0.26",
  "syn 2.0.15",
@@ -582,11 +632,10 @@ dependencies = [
 
 [[package]]
 name = "bevy_render"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46e4b6a82c3a2be1c0d0cbecf62debb8251b72c0ae76285f66265aabc5bf2d37"
+checksum = "90d777f4c51bd58e9e40777c6cb8dde0778df7e2c5298b3f9e3455bd12a9856c"
 dependencies = [
- "anyhow",
  "async-channel",
  "bevy_app",
  "bevy_asset",
@@ -616,8 +665,6 @@ dependencies = [
  "js-sys",
  "naga",
  "naga_oil",
- "parking_lot",
- "regex",
  "serde",
  "smallvec",
  "thiserror",
@@ -625,14 +672,13 @@ dependencies = [
  "wasm-bindgen",
  "web-sys",
  "wgpu",
- "wgpu-hal",
 ]
 
 [[package]]
 name = "bevy_render_macros"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07c4d937f966644f5e1e3c9157736acdd36286bcce06142ff9ad25cd71348c09"
+checksum = "35b00c3d0abff94a729460fc9aa95c2ceac71b49b3041166bb5ba3098e9657e7"
 dependencies = [
  "bevy_macro_utils",
  "proc-macro2 1.0.56",
@@ -642,11 +688,10 @@ dependencies = [
 
 [[package]]
 name = "bevy_scene"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1e00eb30e2053d9fff0802b2f557350b4e66bac58d531de30882048b4e3232"
+checksum = "ba6294396a6375f0b14341d8003408c10aa040e3f833ac8bd49677170ec55d73"
 dependencies = [
- "anyhow",
  "bevy_app",
  "bevy_asset",
  "bevy_derive",
@@ -664,9 +709,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_sprite"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03f64119444ef9788dcdd05012a60f0fa3b7ddb396d434ebcfc3edefd76c91b5"
+checksum = "b4f7d1f88a6e5497fdafd95c20984a1d1b5517bc39d51600b4988cd60c51837a"
 dependencies = [
  "bevy_app",
  "bevy_asset",
@@ -683,15 +728,16 @@ dependencies = [
  "bytemuck",
  "fixedbitset",
  "guillotiere",
+ "radsort",
  "rectangle-pack",
  "thiserror",
 ]
 
 [[package]]
 name = "bevy_tasks"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "faab904296a3d6976bb8a12bc0f42f6c98fb6cd87a96244e0151d359f684ec2d"
+checksum = "3a45be906618192515bc613e46546150089adbb4a82178dc462045acd1e89e92"
 dependencies = [
  "async-channel",
  "async-executor",
@@ -703,9 +749,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_time"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d09225ad2ffef14da000080143730b36ba225844ae479e4791cdb9d08066d06a"
+checksum = "b29709cadf22d318a0b7c79f763e9c5ac414292bd0e850066fa935959021b276"
 dependencies = [
  "bevy_app",
  "bevy_ecs",
@@ -717,28 +763,30 @@ dependencies = [
 
 [[package]]
 name = "bevy_transform"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da8a0cd3780e120e20be333cc48d41cb74620d798dc61bc18eb2a82d3545e184"
+checksum = "70262c51e915b6224129206d23823364e650cf5eb5f4b6ce3ee379f608c180d2"
 dependencies = [
  "bevy_app",
  "bevy_ecs",
  "bevy_hierarchy",
  "bevy_math",
  "bevy_reflect",
+ "thiserror",
 ]
 
 [[package]]
 name = "bevy_utils"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10bfde141f0cdd15e07bca72f4439a9db80877c283738f581d061972ef483b1b"
+checksum = "c8e75d4a34ef0b15dffd1ee9079ef1f0f5139527e192b9d5708b3e158777c753"
 dependencies = [
  "ahash 0.8.3",
  "bevy_utils_proc_macros",
  "getrandom",
  "hashbrown 0.14.0",
  "instant",
+ "nonmax",
  "petgraph",
  "thiserror",
  "tracing",
@@ -747,9 +795,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_utils_proc_macros"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e37f2e885b0e8af59dc19871c313d3cf2a2495db35bb4d4ae0a61b3f87d5401"
+checksum = "f7dfd3735a61a1b681ed1e176afe4eae731bbb03e51ad871e9eb39e76a2d170e"
 dependencies = [
  "proc-macro2 1.0.56",
  "quote 1.0.26",
@@ -758,10 +806,11 @@ dependencies = [
 
 [[package]]
 name = "bevy_window"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0528832361e3d942df287c90537ef6fafb726c4934468a7c3a5d53d659bfbf54"
+checksum = "e60d1830b3fbd7db5bfea7ac9fcd0f5e1d1af88c91ab469e697ab176d8b3140b"
 dependencies = [
+ "bevy_a11y",
  "bevy_app",
  "bevy_ecs",
  "bevy_input",
@@ -773,9 +822,9 @@ dependencies = [
 
 [[package]]
 name = "bevy_winit"
-version = "0.11.0"
+version = "0.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24c6709dc70cfee1eb94d5f125d29612c4a9345dfc1a70dd3189af927b2fd503"
+checksum = "7f8294e78c6a1f9c34d36501a377c5d20bf0fa23a0958187bb270187741448ba"
 dependencies = [
  "accesskit_winit",
  "approx",
@@ -823,6 +872,19 @@ version = "2.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
 
+[[package]]
+name = "blake3"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "cc",
+ "cfg-if",
+ "constant_time_eq",
+]
+
 [[package]]
 name = "block"
 version = "0.1.6"
@@ -848,6 +910,22 @@ dependencies = [
  "objc2-encode",
 ]
 
+[[package]]
+name = "blocking"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a"
+dependencies = [
+ "async-channel",
+ "async-lock",
+ "async-task",
+ "fastrand 2.0.1",
+ "futures-io",
+ "futures-lite",
+ "piper",
+ "tracing",
+]
+
 [[package]]
 name = "bumpalo"
 version = "3.12.1"
@@ -954,6 +1032,12 @@ version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "87ca1caa64ef4ed453e68bb3db612e51cf1b2f5b871337f0fcab1c8f87cc3dff"
 
+[[package]]
+name = "constant_time_eq"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
+
 [[package]]
 name = "constgebra"
 version = "0.1.3"
@@ -988,7 +1072,7 @@ dependencies = [
  "bitflags 1.3.2",
  "core-foundation",
  "core-graphics-types",
- "foreign-types",
+ "foreign-types 0.3.2",
  "libc",
 ]
 
@@ -1000,7 +1084,7 @@ checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
 dependencies = [
  "bitflags 1.3.2",
  "core-foundation",
- "foreign-types",
+ "foreign-types 0.3.2",
  "libc",
 ]
 
@@ -1025,12 +1109,12 @@ dependencies = [
 
 [[package]]
 name = "d3d12"
-version = "0.6.0"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8f0de2f5a8e7bd4a9eec0e3c781992a4ce1724f68aec7d7a3715344de8b39da"
+checksum = "e16e44ab292b1dddfdaf7be62cfd8877df52f2f3fde5858d95bab606be259f20"
 dependencies = [
- "bitflags 1.3.2",
- "libloading 0.7.4",
+ "bitflags 2.3.3",
+ "libloading 0.8.0",
  "winapi",
 ]
 
@@ -1144,6 +1228,12 @@ dependencies = [
  "syn 2.0.15",
 ]
 
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
 [[package]]
 name = "erased-serde"
 version = "0.3.25"
@@ -1177,6 +1267,12 @@ dependencies = [
  "instant",
 ]
 
+[[package]]
+name = "fastrand"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
+
 [[package]]
 name = "fixedbitset"
 version = "0.4.2"
@@ -1195,7 +1291,28 @@ version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
 dependencies = [
- "foreign-types-shared",
+ "foreign-types-shared 0.1.1",
+]
+
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared 0.3.1",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2 1.0.56",
+ "quote 1.0.26",
+ "syn 2.0.15",
 ]
 
 [[package]]
@@ -1204,6 +1321,12 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
 
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
 [[package]]
 name = "futures-core"
 version = "0.3.28"
@@ -1222,7 +1345,7 @@ version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
 dependencies = [
- "fastrand",
+ "fastrand 1.9.0",
  "futures-core",
  "futures-io",
  "memchr",
@@ -1262,9 +1385,9 @@ dependencies = [
 
 [[package]]
 name = "glow"
-version = "0.12.1"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e007a07a24de5ecae94160f141029e9a347282cfe25d1d58d85d845cf3130f1"
+checksum = "ca0fe580e4b60a8ab24a868bc08e2f03cbcb20d3d676601fa909386713333728"
 dependencies = [
  "js-sys",
  "slotmap",
@@ -1274,21 +1397,21 @@ dependencies = [
 
 [[package]]
 name = "gpu-alloc"
-version = "0.5.4"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22beaafc29b38204457ea030f6fb7a84c9e4dd1b86e311ba0542533453d87f62"
+checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.3.3",
  "gpu-alloc-types",
 ]
 
 [[package]]
 name = "gpu-alloc-types"
-version = "0.2.0"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5"
+checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.3.3",
 ]
 
 [[package]]
@@ -1301,7 +1424,7 @@ dependencies = [
  "log",
  "thiserror",
  "winapi",
- "windows",
+ "windows 0.44.0",
 ]
 
 [[package]]
@@ -1414,6 +1537,16 @@ dependencies = [
  "hashbrown 0.12.3",
 ]
 
+[[package]]
+name = "indexmap"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.14.0",
+]
+
 [[package]]
 name = "instant"
 version = "0.1.12"
@@ -1449,9 +1582,9 @@ dependencies = [
 
 [[package]]
 name = "js-sys"
-version = "0.3.61"
+version = "0.3.65"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
+checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -1544,16 +1677,17 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 
 [[package]]
 name = "metal"
-version = "0.24.0"
+version = "0.26.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de11355d1f6781482d027a3b4d4de7825dcedb197bf573e0596d00008402d060"
+checksum = "623b5e6cefd76e58f774bd3cc0c6f5c7615c58c03a97815245a25c3c9bdee318"
 dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.3.3",
  "block",
  "core-graphics-types",
- "foreign-types",
+ "foreign-types 0.5.0",
  "log",
  "objc",
+ "paste",
 ]
 
 [[package]]
@@ -1562,7 +1696,7 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "78eda07db196df5c028f17242e7f5e9fd26efadf83d1e10d4927b56754155996"
 dependencies = [
- "fastrand",
+ "fastrand 1.9.0",
 ]
 
 [[package]]
@@ -1591,15 +1725,15 @@ dependencies = [
 
 [[package]]
 name = "naga"
-version = "0.12.3"
+version = "0.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbcc2e0513220fd2b598e6068608d4462db20322c0e77e47f6f488dfcfc279cb"
+checksum = "c1ceaaa4eedaece7e4ec08c55c640ba03dbb73fb812a6570a59bcf1930d0f70e"
 dependencies = [
  "bit-set",
- "bitflags 1.3.2",
+ "bitflags 2.3.3",
  "codespan-reporting",
  "hexf-parse",
- "indexmap",
+ "indexmap 1.9.3",
  "log",
  "num-traits",
  "pp-rs",
@@ -1612,18 +1746,18 @@ dependencies = [
 
 [[package]]
 name = "naga_oil"
-version = "0.8.1"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d9c27fc9c84580434af75123d13ad98d9a56e16d033b16dcfa6940728c8c225"
+checksum = "a1fa9518ff79ae8a98c3abe3897d873a85561d1b5642981c2245c1c4b9b2429d"
 dependencies = [
  "bit-set",
  "codespan-reporting",
  "data-encoding",
- "indexmap",
+ "indexmap 1.9.3",
  "naga",
  "once_cell",
  "regex",
- "regex-syntax 0.6.29",
+ "regex-syntax 0.7.1",
  "rustc-hash",
  "thiserror",
  "tracing",
@@ -1659,6 +1793,12 @@ dependencies = [
  "jni-sys",
 ]
 
+[[package]]
+name = "nonmax"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99756f5493e135528f0cd660ac67b4c3a542bb65a3565efe92bb2c2317eb3669"
+
 [[package]]
 name = "ntapi"
 version = "0.4.1"
@@ -1904,7 +2044,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4"
 dependencies = [
  "fixedbitset",
- "indexmap",
+ "indexmap 1.9.3",
 ]
 
 [[package]]
@@ -1913,6 +2053,17 @@ version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
 
+[[package]]
+name = "piper"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4"
+dependencies = [
+ "atomic-waker",
+ "fastrand 2.0.1",
+ "futures-io",
+]
+
 [[package]]
 name = "pkg-config"
 version = "0.3.27"
@@ -1935,7 +2086,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919"
 dependencies = [
  "once_cell",
- "toml_edit",
+ "toml_edit 0.19.8",
 ]
 
 [[package]]
@@ -2297,9 +2448,9 @@ dependencies = [
 
 [[package]]
 name = "toml_datetime"
-version = "0.6.1"
+version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
+checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
 
 [[package]]
 name = "toml_edit"
@@ -2307,9 +2458,20 @@ version = "0.19.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
 dependencies = [
- "indexmap",
+ "indexmap 1.9.3",
+ "toml_datetime",
+ "winnow 0.4.6",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.20.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
+dependencies = [
+ "indexmap 2.0.1",
  "toml_datetime",
- "winnow",
+ "winnow 0.5.19",
 ]
 
 [[package]]
@@ -2445,9 +2607,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.84"
+version = "0.2.88"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
+checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
 dependencies = [
  "cfg-if",
  "wasm-bindgen-macro",
@@ -2455,16 +2617,16 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.84"
+version = "0.2.88"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
+checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
 dependencies = [
  "bumpalo",
  "log",
  "once_cell",
  "proc-macro2 1.0.56",
  "quote 1.0.26",
- "syn 1.0.109",
+ "syn 2.0.15",
  "wasm-bindgen-shared",
 ]
 
@@ -2482,9 +2644,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.84"
+version = "0.2.88"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
+checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
 dependencies = [
  "quote 1.0.26",
  "wasm-bindgen-macro-support",
@@ -2492,22 +2654,22 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.84"
+version = "0.2.88"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
+checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
 dependencies = [
  "proc-macro2 1.0.56",
  "quote 1.0.26",
- "syn 1.0.109",
+ "syn 2.0.15",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.84"
+version = "0.2.88"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
+checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
 
 [[package]]
 name = "wayland-scanner"
@@ -2522,9 +2684,9 @@ dependencies = [
 
 [[package]]
 name = "web-sys"
-version = "0.3.61"
+version = "0.3.65"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
+checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -2532,9 +2694,9 @@ dependencies = [
 
 [[package]]
 name = "wgpu"
-version = "0.16.3"
+version = "0.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "480c965c9306872eb6255fa55e4b4953be55a8b64d57e61d7ff840d3dcc051cd"
+checksum = "ed547920565c56c7a29afb4538ac5ae5048865a5d2f05bff3ad4fbeb921a9a2c"
 dependencies = [
  "arrayvec",
  "cfg-if",
@@ -2556,9 +2718,9 @@ dependencies = [
 
 [[package]]
 name = "wgpu-core"
-version = "0.16.1"
+version = "0.17.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f478237b4bf0d5b70a39898a66fa67ca3a007d79f2520485b8b0c3dfc46f8c2"
+checksum = "0f8a44dd301a30ceeed3c27d8c0090433d3da04d7b2a4042738095a424d12ae7"
 dependencies = [
  "arrayvec",
  "bit-vec",
@@ -2579,9 +2741,9 @@ dependencies = [
 
 [[package]]
 name = "wgpu-hal"
-version = "0.16.2"
+version = "0.17.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ecb3258078e936deee14fd4e0febe1cfe9bbb5ffef165cb60218d2ee5eb4448"
+checksum = "9a80bf0e3c77399bb52850cb0830af9bad073d5cfcb9dd8253bef8125c42db17"
 dependencies = [
  "android_system_properties",
  "arrayvec",
@@ -2591,7 +2753,6 @@ dependencies = [
  "block",
  "core-graphics-types",
  "d3d12",
- "foreign-types",
  "glow",
  "gpu-alloc",
  "gpu-allocator",
@@ -2621,9 +2782,9 @@ dependencies = [
 
 [[package]]
 name = "wgpu-types"
-version = "0.16.1"
+version = "0.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0c153280bb108c2979eb5c7391cb18c56642dd3c072e55f52065e13e2a1252a"
+checksum = "ee64d7398d0c2f9ca48922c902ef69c42d000c759f3db41e355f4a570b052b67"
 dependencies = [
  "bitflags 2.3.3",
  "js-sys",
@@ -2672,17 +2833,26 @@ name = "windows"
 version = "0.44.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
 dependencies = [
  "windows-implement",
  "windows-interface",
- "windows-targets 0.42.2",
+ "windows-targets 0.48.0",
 ]
 
 [[package]]
 name = "windows-implement"
-version = "0.44.0"
+version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ce87ca8e3417b02dc2a8a22769306658670ec92d78f1bd420d6310a67c245c6"
+checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c"
 dependencies = [
  "proc-macro2 1.0.56",
  "quote 1.0.26",
@@ -2691,9 +2861,9 @@ dependencies = [
 
 [[package]]
 name = "windows-interface"
-version = "0.44.0"
+version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "853f69a591ecd4f810d29f17e902d40e349fb05b0b11fff63b08b826bfe39c7f"
+checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7"
 dependencies = [
  "proc-macro2 1.0.56",
  "quote 1.0.26",
@@ -2834,9 +3004,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
 
 [[package]]
 name = "winit"
-version = "0.28.5"
+version = "0.28.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94c9651471cd576737671fbf7081edfea43de3e06846dd9bd4e49ea803c9f55f"
+checksum = "9596d90b45384f5281384ab204224876e8e8bf7d58366d9b795ad99aa9894b94"
 dependencies = [
  "android-activity",
  "bitflags 1.3.2",
@@ -2868,6 +3038,15 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "winnow"
+version = "0.5.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "xml-rs"
 version = "0.8.7"
diff --git a/Cargo.toml b/Cargo.toml
index c46f91f073c28767bba590862fa37b95af2a6459..f1e750d51d941ac90229a80069a4ac9871c5b337 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -28,7 +28,7 @@ autotile = ["micro_autotile"]
 no_panic = []
 
 [dependencies]
-bevy = { version = "0.11", default-features = false, features = ["bevy_render", "bevy_sprite", "bevy_asset"] }
+bevy = { version = "0.12", default-features = false, features = ["bevy_render", "bevy_sprite", "bevy_asset"] }
 
 anyhow = "1.0"
 thiserror = "1.0"
diff --git a/README.md b/README.md
index 872653af9e1261d5c2afb3def7bb99c9f1240992..f581f7d78326284db1f70975eb5d0c3ad375dda6 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ select the schema version you need:
 
 ```toml
 [dependencies]
-micro_ldtk = { version = "0.7.0", default-features = false, features = ["ldtk_1_3_0", "autotile"] }
+micro_ldtk = { version = "0.8.0", default-features = false, features = ["ldtk_1_3_0", "autotile"] }
 ```
 
 ### Features
diff --git a/src/assets/asset_events.rs b/src/assets/asset_events.rs
index c7acc8ae2a88c5a5baa2a2b54c17083e6165a3dc..f4d4aa00df8e24c75284ef1be37fb17f508a4cf4 100644
--- a/src/assets/asset_events.rs
+++ b/src/assets/asset_events.rs
@@ -13,9 +13,10 @@ pub fn handle_ldtk_project_events(
 	mut tilset_index: ResMut<TilesetIndex>,
 	mut update_events: EventWriter<LevelDataUpdated>,
 ) {
-	for event in events.iter() {
+	for event in events.read() {
 		match event {
-			AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
+			AssetEvent::Added { id } | AssetEvent::Modified { id } => {
+				let handle = Handle::Weak(*id);
 				if let Some(project) = assets.get(handle) {
 					for level in project.get_all_levels() {
 						if level.external_rel_path.is_none() {
@@ -50,7 +51,8 @@ pub fn handle_ldtk_level_events(
 ) {
 	for event in events.iter() {
 		match event {
-			AssetEvent::Created { handle } | AssetEvent::Modified { handle } => {
+			AssetEvent::Added { id } | AssetEvent::Modified { id } => {
+				let handle = Handle::Weak(*id);
 				if let Some(level) = assets.get(handle) {
 					level_index.insert(level.identifier.clone(), LdtkLevel::from(level.clone()));
 					update_events.send(LevelDataUpdated(level.identifier.clone()));
diff --git a/src/ldtk/data_1_4_0.rs b/src/ldtk/data_1_4_0.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f78ed5cb114aa39be2871fcf5a3eefa17e64ba13
--- /dev/null
+++ b/src/ldtk/data_1_4_0.rs
@@ -0,0 +1,1650 @@
+// Example code that deserializes and serializes the model.
+// extern crate serde;
+// #[macro_use]
+// extern crate serde_derive;
+// extern crate serde_json;
+//
+// use generated_module::Project;
+//
+// fn main() {
+//     let json = r#"{"answer": 42}"#;
+//     let model: Project = serde_json::from_str(&json).unwrap();
+// }
+
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+
+/// This file is a JSON schema of files created by LDtk level editor (https://ldtk.io).
+///
+/// This is the root of any Project JSON file. It contains:  - the project settings, - an
+/// array of levels, - a group of definitions (that can probably be safely ignored for most
+/// users).
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct Project {
+	/// This object is not actually used by LDtk. It ONLY exists to force explicit references to
+	/// all types, to make sure QuickType finds them and integrate all of them. Otherwise,
+	/// Quicktype will drop types that are not explicitely used.
+	#[serde(rename = "__FORCED_REFS")]
+	pub forced_refs: Option<ForcedRefs>,
+
+	/// LDtk application build identifier.<br/>  This is only used to identify the LDtk version
+	/// that generated this particular project file, which can be useful for specific bug fixing.
+	/// Note that the build identifier is just the date of the release, so it's not unique to
+	/// each user (one single global ID per LDtk public release), and as a result, completely
+	/// anonymous.
+	pub app_build_id: f64,
+
+	/// Number of backup files to keep, if the `backupOnSave` is TRUE
+	pub backup_limit: i64,
+
+	/// If TRUE, an extra copy of the project will be created in a sub folder, when saving.
+	pub backup_on_save: bool,
+
+	/// Target relative path to store backup files
+	pub backup_rel_path: Option<String>,
+
+	/// Project background color
+	pub bg_color: String,
+
+	/// An array of command lines that can be ran manually by the user
+	pub custom_commands: Vec<LdtkCustomCommand>,
+
+	/// Default height for new entities
+	pub default_entity_height: i64,
+
+	/// Default width for new entities
+	pub default_entity_width: i64,
+
+	/// Default grid size for new layers
+	pub default_grid_size: i64,
+
+	/// Default background color of levels
+	pub default_level_bg_color: String,
+
+	/// **WARNING**: this field will move to the `worlds` array after the "multi-worlds" update.
+	/// It will then be `null`. You can enable the Multi-worlds advanced project option to enable
+	/// the change immediately.<br/><br/>  Default new level height
+	pub default_level_height: Option<i64>,
+
+	/// **WARNING**: this field will move to the `worlds` array after the "multi-worlds" update.
+	/// It will then be `null`. You can enable the Multi-worlds advanced project option to enable
+	/// the change immediately.<br/><br/>  Default new level width
+	pub default_level_width: Option<i64>,
+
+	/// Default X pivot (0 to 1) for new entities
+	pub default_pivot_x: f64,
+
+	/// Default Y pivot (0 to 1) for new entities
+	pub default_pivot_y: f64,
+
+	/// A structure containing all the definitions of this project
+	pub defs: Definitions,
+
+	/// If the project isn't in MultiWorlds mode, this is the IID of the internal "dummy" World.
+	pub dummy_world_iid: String,
+
+	/// If TRUE, the exported PNGs will include the level background (color or image).
+	pub export_level_bg: bool,
+
+	/// **WARNING**: this deprecated value is no longer exported since version 0.9.3  Replaced
+	/// by: `imageExportMode`
+	pub export_png: Option<bool>,
+
+	/// If TRUE, a Tiled compatible file will also be generated along with the LDtk JSON file
+	/// (default is FALSE)
+	pub export_tiled: bool,
+
+	/// If TRUE, one file will be saved for the project (incl. all its definitions) and one file
+	/// in a sub-folder for each level.
+	pub external_levels: bool,
+
+	/// An array containing various advanced flags (ie. options or other states). Possible
+	/// values: `DiscardPreCsvIntGrid`, `ExportPreCsvIntGridFormat`, `IgnoreBackupSuggest`,
+	/// `PrependIndexToLevelFileNames`, `MultiWorlds`, `UseMultilinesType`
+	pub flags: Vec<Flag>,
+
+	/// Naming convention for Identifiers (first-letter uppercase, full uppercase etc.) Possible
+	/// values: `Capitalize`, `Uppercase`, `Lowercase`, `Free`
+	pub identifier_style: IdentifierStyle,
+
+	/// Unique project identifier
+	pub iid: String,
+
+	/// "Image export" option when saving project. Possible values: `None`, `OneImagePerLayer`,
+	/// `OneImagePerLevel`, `LayersAndLevels`
+	pub image_export_mode: ImageExportMode,
+
+	/// File format version
+	pub json_version: String,
+
+	/// The default naming convention for level identifiers.
+	pub level_name_pattern: String,
+
+	/// All levels. The order of this array is only relevant in `LinearHorizontal` and
+	/// `linearVertical` world layouts (see `worldLayout` value).<br/>  Otherwise, you should
+	/// refer to the `worldX`,`worldY` coordinates of each Level.
+	pub levels: Vec<Level>,
+
+	/// If TRUE, the Json is partially minified (no indentation, nor line breaks, default is
+	/// FALSE)
+	pub minify_json: bool,
+
+	/// Next Unique integer ID available
+	pub next_uid: i64,
+
+	/// File naming pattern for exported PNGs
+	pub png_file_pattern: Option<String>,
+
+	/// If TRUE, a very simplified will be generated on saving, for quicker & easier engine
+	/// integration.
+	pub simplified_export: bool,
+
+	/// All instances of entities that have their `exportToToc` flag enabled are listed in this
+	/// array.
+	pub toc: Vec<LdtkTableOfContentEntry>,
+
+	/// This optional description is used by LDtk Samples to show up some informations and
+	/// instructions.
+	pub tutorial_desc: Option<String>,
+
+	/// **WARNING**: this field will move to the `worlds` array after the "multi-worlds" update.
+	/// It will then be `null`. You can enable the Multi-worlds advanced project option to enable
+	/// the change immediately.<br/><br/>  Height of the world grid in pixels.
+	pub world_grid_height: Option<i64>,
+
+	/// **WARNING**: this field will move to the `worlds` array after the "multi-worlds" update.
+	/// It will then be `null`. You can enable the Multi-worlds advanced project option to enable
+	/// the change immediately.<br/><br/>  Width of the world grid in pixels.
+	pub world_grid_width: Option<i64>,
+
+	/// **WARNING**: this field will move to the `worlds` array after the "multi-worlds" update.
+	/// It will then be `null`. You can enable the Multi-worlds advanced project option to enable
+	/// the change immediately.<br/><br/>  An enum that describes how levels are organized in
+	/// this project (ie. linearly or in a 2D space). Possible values: &lt;`null`&gt;, `Free`,
+	/// `GridVania`, `LinearHorizontal`, `LinearVertical`
+	pub world_layout: Option<WorldLayout>,
+
+	/// This array will be empty, unless you enable the Multi-Worlds in the project advanced
+	/// settings.<br/><br/> - in current version, a LDtk project file can only contain a single
+	/// world with multiple levels in it. In this case, levels and world layout related settings
+	/// are stored in the root of the JSON.<br/> - with "Multi-worlds" enabled, there will be a
+	/// `worlds` array in root, each world containing levels and layout settings. Basically, it's
+	/// pretty much only about moving the `levels` array to the `worlds` array, along with world
+	/// layout related values (eg. `worldGridWidth` etc).<br/><br/>If you want to start
+	/// supporting this future update easily, please refer to this documentation:
+	/// https://github.com/deepnight/ldtk/issues/231
+	pub worlds: Vec<World>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct LdtkCustomCommand {
+	pub command: String,
+
+	/// Possible values: `Manual`, `AfterLoad`, `BeforeSave`, `AfterSave`
+	pub when: When,
+}
+
+/// Possible values: `Manual`, `AfterLoad`, `BeforeSave`, `AfterSave`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum When {
+	#[serde(rename = "AfterLoad")]
+	AfterLoad,
+
+	#[serde(rename = "AfterSave")]
+	AfterSave,
+
+	#[serde(rename = "BeforeSave")]
+	BeforeSave,
+
+	Manual,
+}
+
+/// If you're writing your own LDtk importer, you should probably just ignore *most* stuff in
+/// the `defs` section, as it contains data that are mostly important to the editor. To keep
+/// you away from the `defs` section and avoid some unnecessary JSON parsing, important data
+/// from definitions is often duplicated in fields prefixed with a double underscore (eg.
+/// `__identifier` or `__type`).  The 2 only definition types you might need here are
+/// **Tilesets** and **Enums**.
+///
+/// A structure containing all the definitions of this project
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct Definitions {
+	/// All entities definitions, including their custom fields
+	pub entities: Vec<EntityDefinition>,
+
+	/// All internal enums
+	pub enums: Vec<EnumDefinition>,
+
+	/// Note: external enums are exactly the same as `enums`, except they have a `relPath` to
+	/// point to an external source file.
+	pub external_enums: Vec<EnumDefinition>,
+
+	/// All layer definitions
+	pub layers: Vec<LayerDefinition>,
+
+	/// All custom fields available to all levels.
+	pub level_fields: Vec<FieldDefinition>,
+
+	/// All tilesets
+	pub tilesets: Vec<TilesetDefinition>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct EntityDefinition {
+	/// Base entity color
+	pub color: String,
+
+	/// User defined documentation for this element to provide help/tips to level designers.
+	pub doc: Option<String>,
+
+	/// If enabled, all instances of this entity will be listed in the project "Table of content"
+	/// object.
+	pub export_to_toc: bool,
+
+	/// Array of field definitions
+	pub field_defs: Vec<FieldDefinition>,
+
+	pub fill_opacity: f64,
+
+	/// Pixel height
+	pub height: i64,
+
+	pub hollow: bool,
+
+	/// User defined unique identifier
+	pub identifier: String,
+
+	/// Only applies to entities resizable on both X/Y. If TRUE, the entity instance width/height
+	/// will keep the same aspect ratio as the definition.
+	pub keep_aspect_ratio: bool,
+
+	/// Possible values: `DiscardOldOnes`, `PreventAdding`, `MoveLastOne`
+	pub limit_behavior: LimitBehavior,
+
+	/// If TRUE, the maxCount is a "per world" limit, if FALSE, it's a "per level". Possible
+	/// values: `PerLayer`, `PerLevel`, `PerWorld`
+	pub limit_scope: LimitScope,
+
+	pub line_opacity: f64,
+
+	/// Max instances count
+	pub max_count: i64,
+
+	/// Max pixel height (only applies if the entity is resizable on Y)
+	pub max_height: Option<i64>,
+
+	/// Max pixel width (only applies if the entity is resizable on X)
+	pub max_width: Option<i64>,
+
+	/// Min pixel height (only applies if the entity is resizable on Y)
+	pub min_height: Option<i64>,
+
+	/// Min pixel width (only applies if the entity is resizable on X)
+	pub min_width: Option<i64>,
+
+	/// An array of 4 dimensions for the up/right/down/left borders (in this order) when using
+	/// 9-slice mode for `tileRenderMode`.<br/>  If the tileRenderMode is not NineSlice, then
+	/// this array is empty.<br/>  See: https://en.wikipedia.org/wiki/9-slice_scaling
+	pub nine_slice_borders: Vec<i64>,
+
+	/// Pivot X coordinate (from 0 to 1.0)
+	pub pivot_x: f64,
+
+	/// Pivot Y coordinate (from 0 to 1.0)
+	pub pivot_y: f64,
+
+	/// Possible values: `Rectangle`, `Ellipse`, `Tile`, `Cross`
+	pub render_mode: RenderMode,
+
+	/// If TRUE, the entity instances will be resizable horizontally
+	pub resizable_x: bool,
+
+	/// If TRUE, the entity instances will be resizable vertically
+	pub resizable_y: bool,
+
+	/// Display entity name in editor
+	pub show_name: bool,
+
+	/// An array of strings that classifies this entity
+	pub tags: Vec<String>,
+
+	/// **WARNING**: this deprecated value is no longer exported since version 1.2.0  Replaced
+	/// by: `tileRect`
+	pub tile_id: Option<i64>,
+
+	pub tile_opacity: f64,
+
+	/// An object representing a rectangle from an existing Tileset
+	pub tile_rect: Option<TilesetRectangle>,
+
+	/// An enum describing how the the Entity tile is rendered inside the Entity bounds. Possible
+	/// values: `Cover`, `FitInside`, `Repeat`, `Stretch`, `FullSizeCropped`,
+	/// `FullSizeUncropped`, `NineSlice`
+	pub tile_render_mode: TileRenderMode,
+
+	/// Tileset ID used for optional tile display
+	pub tileset_id: Option<i64>,
+
+	/// Unique Int identifier
+	pub uid: i64,
+
+	/// This tile overrides the one defined in `tileRect` in the UI
+	pub ui_tile_rect: Option<TilesetRectangle>,
+
+	/// Pixel width
+	pub width: i64,
+}
+
+/// This section is mostly only intended for the LDtk editor app itself. You can safely
+/// ignore it.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct FieldDefinition {
+	/// Human readable value type. Possible values: `Int, Float, String, Bool, Color,
+	/// ExternEnum.XXX, LocalEnum.XXX, Point, FilePath`.<br/>  If the field is an array, this
+	/// field will look like `Array<...>` (eg. `Array<Int>`, `Array<Point>` etc.)<br/>  NOTE: if
+	/// you enable the advanced option **Use Multilines type**, you will have "*Multilines*"
+	/// instead of "*String*" when relevant.
+	#[serde(rename = "__type")]
+	pub field_definition_type: String,
+
+	/// Optional list of accepted file extensions for FilePath value type. Includes the dot:
+	/// `.ext`
+	pub accept_file_types: Option<Vec<String>>,
+
+	/// Possible values: `Any`, `OnlySame`, `OnlyTags`, `OnlySpecificEntity`
+	pub allowed_refs: AllowedRefs,
+
+	pub allowed_refs_entity_uid: Option<i64>,
+
+	pub allowed_ref_tags: Vec<String>,
+
+	pub allow_out_of_level_ref: bool,
+
+	/// Array max length
+	pub array_max_length: Option<i64>,
+
+	/// Array min length
+	pub array_min_length: Option<i64>,
+
+	pub auto_chain_ref: bool,
+
+	/// TRUE if the value can be null. For arrays, TRUE means it can contain null values
+	/// (exception: array of Points can't have null values).
+	pub can_be_null: bool,
+
+	/// Default value if selected value is null or invalid.
+	pub default_override: Option<serde_json::Value>,
+
+	/// User defined documentation for this field to provide help/tips to level designers about
+	/// accepted values.
+	pub doc: Option<String>,
+
+	pub editor_always_show: bool,
+
+	pub editor_cut_long_values: bool,
+
+	pub editor_display_color: Option<String>,
+
+	/// Possible values: `Hidden`, `ValueOnly`, `NameAndValue`, `EntityTile`, `LevelTile`,
+	/// `Points`, `PointStar`, `PointPath`, `PointPathLoop`, `RadiusPx`, `RadiusGrid`,
+	/// `ArrayCountWithLabel`, `ArrayCountNoLabel`, `RefLinkBetweenPivots`,
+	/// `RefLinkBetweenCenters`
+	pub editor_display_mode: EditorDisplayMode,
+
+	/// Possible values: `Above`, `Center`, `Beneath`
+	pub editor_display_pos: EditorDisplayPos,
+
+	pub editor_display_scale: f64,
+
+	/// Possible values: `ZigZag`, `StraightArrow`, `CurvedArrow`, `ArrowsLine`, `DashedLine`
+	pub editor_link_style: EditorLinkStyle,
+
+	pub editor_show_in_world: bool,
+
+	pub editor_text_prefix: Option<String>,
+
+	pub editor_text_suffix: Option<String>,
+
+	/// User defined unique identifier
+	pub identifier: String,
+
+	/// TRUE if the value is an array of multiple values
+	pub is_array: bool,
+
+	/// Max limit for value, if applicable
+	pub max: Option<f64>,
+
+	/// Min limit for value, if applicable
+	pub min: Option<f64>,
+
+	/// Optional regular expression that needs to be matched to accept values. Expected format:
+	/// `/some_reg_ex/g`, with optional "i" flag.
+	pub regex: Option<String>,
+
+	pub symmetrical_ref: bool,
+
+	/// Possible values: &lt;`null`&gt;, `LangPython`, `LangRuby`, `LangJS`, `LangLua`, `LangC`,
+	/// `LangHaxe`, `LangMarkdown`, `LangJson`, `LangXml`, `LangLog`
+	pub text_language_mode: Option<TextLanguageMode>,
+
+	/// UID of the tileset used for a Tile
+	pub tileset_uid: Option<i64>,
+
+	/// Internal enum representing the possible field types. Possible values: F_Int, F_Float,
+	/// F_String, F_Text, F_Bool, F_Color, F_Enum(...), F_Point, F_Path, F_EntityRef, F_Tile
+	#[serde(rename = "type")]
+	pub purple_type: String,
+
+	/// Unique Int identifier
+	pub uid: i64,
+
+	/// If TRUE, the color associated with this field will override the Entity or Level default
+	/// color in the editor UI. For Enum fields, this would be the color associated to their
+	/// values.
+	pub use_for_smart_color: bool,
+}
+
+/// Possible values: `Any`, `OnlySame`, `OnlyTags`, `OnlySpecificEntity`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum AllowedRefs {
+	Any,
+
+	#[serde(rename = "OnlySame")]
+	OnlySame,
+
+	#[serde(rename = "OnlySpecificEntity")]
+	OnlySpecificEntity,
+
+	#[serde(rename = "OnlyTags")]
+	OnlyTags,
+}
+
+/// Possible values: `Hidden`, `ValueOnly`, `NameAndValue`, `EntityTile`, `LevelTile`,
+/// `Points`, `PointStar`, `PointPath`, `PointPathLoop`, `RadiusPx`, `RadiusGrid`,
+/// `ArrayCountWithLabel`, `ArrayCountNoLabel`, `RefLinkBetweenPivots`,
+/// `RefLinkBetweenCenters`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum EditorDisplayMode {
+	#[serde(rename = "ArrayCountNoLabel")]
+	ArrayCountNoLabel,
+
+	#[serde(rename = "ArrayCountWithLabel")]
+	ArrayCountWithLabel,
+
+	#[serde(rename = "EntityTile")]
+	EntityTile,
+
+	Hidden,
+
+	#[serde(rename = "LevelTile")]
+	LevelTile,
+
+	#[serde(rename = "NameAndValue")]
+	NameAndValue,
+
+	#[serde(rename = "PointPath")]
+	PointPath,
+
+	#[serde(rename = "PointPathLoop")]
+	PointPathLoop,
+
+	#[serde(rename = "PointStar")]
+	PointStar,
+
+	Points,
+
+	#[serde(rename = "RadiusGrid")]
+	RadiusGrid,
+
+	#[serde(rename = "RadiusPx")]
+	RadiusPx,
+
+	#[serde(rename = "RefLinkBetweenCenters")]
+	RefLinkBetweenCenters,
+
+	#[serde(rename = "RefLinkBetweenPivots")]
+	RefLinkBetweenPivots,
+
+	#[serde(rename = "ValueOnly")]
+	ValueOnly,
+}
+
+/// Possible values: `Above`, `Center`, `Beneath`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum EditorDisplayPos {
+	Above,
+
+	Beneath,
+
+	Center,
+}
+
+/// Possible values: `ZigZag`, `StraightArrow`, `CurvedArrow`, `ArrowsLine`, `DashedLine`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum EditorLinkStyle {
+	#[serde(rename = "ArrowsLine")]
+	ArrowsLine,
+
+	#[serde(rename = "CurvedArrow")]
+	CurvedArrow,
+
+	#[serde(rename = "DashedLine")]
+	DashedLine,
+
+	#[serde(rename = "StraightArrow")]
+	StraightArrow,
+
+	#[serde(rename = "ZigZag")]
+	ZigZag,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum TextLanguageMode {
+	#[serde(rename = "LangC")]
+	LangC,
+
+	#[serde(rename = "LangHaxe")]
+	LangHaxe,
+
+	#[serde(rename = "LangJS")]
+	LangJs,
+
+	#[serde(rename = "LangJson")]
+	LangJson,
+
+	#[serde(rename = "LangLog")]
+	LangLog,
+
+	#[serde(rename = "LangLua")]
+	LangLua,
+
+	#[serde(rename = "LangMarkdown")]
+	LangMarkdown,
+
+	#[serde(rename = "LangPython")]
+	LangPython,
+
+	#[serde(rename = "LangRuby")]
+	LangRuby,
+
+	#[serde(rename = "LangXml")]
+	LangXml,
+}
+
+/// Possible values: `DiscardOldOnes`, `PreventAdding`, `MoveLastOne`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum LimitBehavior {
+	#[serde(rename = "DiscardOldOnes")]
+	DiscardOldOnes,
+
+	#[serde(rename = "MoveLastOne")]
+	MoveLastOne,
+
+	#[serde(rename = "PreventAdding")]
+	PreventAdding,
+}
+
+/// If TRUE, the maxCount is a "per world" limit, if FALSE, it's a "per level". Possible
+/// values: `PerLayer`, `PerLevel`, `PerWorld`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum LimitScope {
+	#[serde(rename = "PerLayer")]
+	PerLayer,
+
+	#[serde(rename = "PerLevel")]
+	PerLevel,
+
+	#[serde(rename = "PerWorld")]
+	PerWorld,
+}
+
+/// Possible values: `Rectangle`, `Ellipse`, `Tile`, `Cross`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum RenderMode {
+	Cross,
+
+	Ellipse,
+
+	Rectangle,
+
+	Tile,
+}
+
+/// This object represents a custom sub rectangle in a Tileset image.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct TilesetRectangle {
+	/// Height in pixels
+	pub h: i64,
+
+	/// UID of the tileset
+	pub tileset_uid: i64,
+
+	/// Width in pixels
+	pub w: i64,
+
+	/// X pixels coordinate of the top-left corner in the Tileset image
+	pub x: i64,
+
+	/// Y pixels coordinate of the top-left corner in the Tileset image
+	pub y: i64,
+}
+
+/// An enum describing how the the Entity tile is rendered inside the Entity bounds. Possible
+/// values: `Cover`, `FitInside`, `Repeat`, `Stretch`, `FullSizeCropped`,
+/// `FullSizeUncropped`, `NineSlice`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum TileRenderMode {
+	Cover,
+
+	#[serde(rename = "FitInside")]
+	FitInside,
+
+	#[serde(rename = "FullSizeCropped")]
+	FullSizeCropped,
+
+	#[serde(rename = "FullSizeUncropped")]
+	FullSizeUncropped,
+
+	#[serde(rename = "NineSlice")]
+	NineSlice,
+
+	Repeat,
+
+	Stretch,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct EnumDefinition {
+	pub external_file_checksum: Option<String>,
+
+	/// Relative path to the external file providing this Enum
+	pub external_rel_path: Option<String>,
+
+	/// Tileset UID if provided
+	pub icon_tileset_uid: Option<i64>,
+
+	/// User defined unique identifier
+	pub identifier: String,
+
+	/// An array of user-defined tags to organize the Enums
+	pub tags: Vec<String>,
+
+	/// Unique Int identifier
+	pub uid: i64,
+
+	/// All possible enum values, with their optional Tile infos.
+	pub values: Vec<EnumValueDefinition>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct EnumValueDefinition {
+	/// **WARNING**: this deprecated value is no longer exported since version 1.4.0  Replaced
+	/// by: `tileRect`
+	#[serde(rename = "__tileSrcRect")]
+	pub tile_src_rect: Option<Vec<i64>>,
+
+	/// Optional color
+	pub color: i64,
+
+	/// Enum value
+	pub id: String,
+
+	/// **WARNING**: this deprecated value is no longer exported since version 1.4.0  Replaced
+	/// by: `tileRect`
+	pub tile_id: Option<i64>,
+
+	/// Optional tileset rectangle to represents this value
+	pub tile_rect: Option<TilesetRectangle>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct LayerDefinition {
+	/// Type of the layer (*IntGrid, Entities, Tiles or AutoLayer*)
+	#[serde(rename = "__type")]
+	pub layer_definition_type: String,
+
+	/// Contains all the auto-layer rule definitions.
+	pub auto_rule_groups: Vec<AutoLayerRuleGroup>,
+
+	pub auto_source_layer_def_uid: Option<i64>,
+
+	/// **WARNING**: this deprecated value is no longer exported since version 1.2.0  Replaced
+	/// by: `tilesetDefUid`
+	pub auto_tileset_def_uid: Option<i64>,
+
+	/// Allow editor selections when the layer is not currently active.
+	pub can_select_when_inactive: bool,
+
+	/// Opacity of the layer (0 to 1.0)
+	pub display_opacity: f64,
+
+	/// User defined documentation for this element to provide help/tips to level designers.
+	pub doc: Option<String>,
+
+	/// An array of tags to forbid some Entities in this layer
+	pub excluded_tags: Vec<String>,
+
+	/// Width and height of the grid in pixels
+	pub grid_size: i64,
+
+	/// Height of the optional "guide" grid in pixels
+	pub guide_grid_hei: i64,
+
+	/// Width of the optional "guide" grid in pixels
+	pub guide_grid_wid: i64,
+
+	pub hide_fields_when_inactive: bool,
+
+	/// Hide the layer from the list on the side of the editor view.
+	pub hide_in_list: bool,
+
+	/// User defined unique identifier
+	pub identifier: String,
+
+	/// Alpha of this layer when it is not the active one.
+	pub inactive_opacity: f64,
+
+	/// An array that defines extra optional info for each IntGrid value.<br/>  WARNING: the
+	/// array order is not related to actual IntGrid values! As user can re-order IntGrid values
+	/// freely, you may value "2" before value "1" in this array.
+	pub int_grid_values: Vec<IntGridValueDefinition>,
+
+	/// Group informations for IntGrid values
+	pub int_grid_values_groups: Vec<IntGridValueGroupDefinition>,
+
+	/// Parallax horizontal factor (from -1 to 1, defaults to 0) which affects the scrolling
+	/// speed of this layer, creating a fake 3D (parallax) effect.
+	pub parallax_factor_x: f64,
+
+	/// Parallax vertical factor (from -1 to 1, defaults to 0) which affects the scrolling speed
+	/// of this layer, creating a fake 3D (parallax) effect.
+	pub parallax_factor_y: f64,
+
+	/// If true (default), a layer with a parallax factor will also be scaled up/down accordingly.
+	pub parallax_scaling: bool,
+
+	/// X offset of the layer, in pixels (IMPORTANT: this should be added to the `LayerInstance`
+	/// optional offset)
+	pub px_offset_x: i64,
+
+	/// Y offset of the layer, in pixels (IMPORTANT: this should be added to the `LayerInstance`
+	/// optional offset)
+	pub px_offset_y: i64,
+
+	/// If TRUE, the content of this layer will be used when rendering levels in a simplified way
+	/// for the world view
+	pub render_in_world_view: bool,
+
+	/// An array of tags to filter Entities that can be added to this layer
+	pub required_tags: Vec<String>,
+
+	/// If the tiles are smaller or larger than the layer grid, the pivot value will be used to
+	/// position the tile relatively its grid cell.
+	pub tile_pivot_x: f64,
+
+	/// If the tiles are smaller or larger than the layer grid, the pivot value will be used to
+	/// position the tile relatively its grid cell.
+	pub tile_pivot_y: f64,
+
+	/// Reference to the default Tileset UID being used by this layer definition.<br/>
+	/// **WARNING**: some layer *instances* might use a different tileset. So most of the time,
+	/// you should probably use the `__tilesetDefUid` value found in layer instances.<br/>  Note:
+	/// since version 1.0.0, the old `autoTilesetDefUid` was removed and merged into this value.
+	pub tileset_def_uid: Option<i64>,
+
+	/// Type of the layer as Haxe Enum Possible values: `IntGrid`, `Entities`, `Tiles`,
+	/// `AutoLayer`
+	#[serde(rename = "type")]
+	pub purple_type: Type,
+
+	/// User defined color for the UI
+	pub ui_color: Option<String>,
+
+	/// Unique Int identifier
+	pub uid: i64,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct AutoLayerRuleGroup {
+	pub active: bool,
+
+	/// *This field was removed in 1.0.0 and should no longer be used.*
+	pub collapsed: Option<bool>,
+
+	pub color: Option<String>,
+
+	pub icon: Option<TilesetRectangle>,
+
+	pub is_optional: bool,
+
+	pub name: String,
+
+	pub rules: Vec<AutoLayerRuleDefinition>,
+
+	pub uid: i64,
+
+	pub uses_wizard: bool,
+}
+
+/// This complex section isn't meant to be used by game devs at all, as these rules are
+/// completely resolved internally by the editor before any saving. You should just ignore
+/// this part.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct AutoLayerRuleDefinition {
+	/// If FALSE, the rule effect isn't applied, and no tiles are generated.
+	pub active: bool,
+
+	pub alpha: f64,
+
+	/// When TRUE, the rule will prevent other rules to be applied in the same cell if it matches
+	/// (TRUE by default).
+	pub break_on_match: bool,
+
+	/// Chances for this rule to be applied (0 to 1)
+	pub chance: f64,
+
+	/// Checker mode Possible values: `None`, `Horizontal`, `Vertical`
+	pub checker: Checker,
+
+	/// If TRUE, allow rule to be matched by flipping its pattern horizontally
+	pub flip_x: bool,
+
+	/// If TRUE, allow rule to be matched by flipping its pattern vertically
+	pub flip_y: bool,
+
+	/// Default IntGrid value when checking cells outside of level bounds
+	pub out_of_bounds_value: Option<i64>,
+
+	/// Rule pattern (size x size)
+	pub pattern: Vec<i64>,
+
+	/// If TRUE, enable Perlin filtering to only apply rule on specific random area
+	pub perlin_active: bool,
+
+	pub perlin_octaves: f64,
+
+	pub perlin_scale: f64,
+
+	pub perlin_seed: f64,
+
+	/// X pivot of a tile stamp (0-1)
+	pub pivot_x: f64,
+
+	/// Y pivot of a tile stamp (0-1)
+	pub pivot_y: f64,
+
+	/// Pattern width & height. Should only be 1,3,5 or 7.
+	pub size: i64,
+
+	/// Array of all the tile IDs. They are used randomly or as stamps, based on `tileMode` value.
+	pub tile_ids: Vec<i64>,
+
+	/// Defines how tileIds array is used Possible values: `Single`, `Stamp`
+	pub tile_mode: TileMode,
+
+	/// Max random offset for X tile pos
+	pub tile_random_x_max: i64,
+
+	/// Min random offset for X tile pos
+	pub tile_random_x_min: i64,
+
+	/// Max random offset for Y tile pos
+	pub tile_random_y_max: i64,
+
+	/// Min random offset for Y tile pos
+	pub tile_random_y_min: i64,
+
+	/// Tile X offset
+	pub tile_x_offset: i64,
+
+	/// Tile Y offset
+	pub tile_y_offset: i64,
+
+	/// Unique Int identifier
+	pub uid: i64,
+
+	/// X cell coord modulo
+	pub x_modulo: i64,
+
+	/// X cell start offset
+	pub x_offset: i64,
+
+	/// Y cell coord modulo
+	pub y_modulo: i64,
+
+	/// Y cell start offset
+	pub y_offset: i64,
+}
+
+/// Checker mode Possible values: `None`, `Horizontal`, `Vertical`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum Checker {
+	Horizontal,
+
+	None,
+
+	Vertical,
+}
+
+/// Defines how tileIds array is used Possible values: `Single`, `Stamp`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum TileMode {
+	Single,
+
+	Stamp,
+}
+
+/// IntGrid value definition
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct IntGridValueDefinition {
+	pub color: String,
+
+	/// Parent group identifier (0 if none)
+	pub group_uid: i64,
+
+	/// User defined unique identifier
+	pub identifier: Option<String>,
+
+	pub tile: Option<TilesetRectangle>,
+
+	/// The IntGrid value itself
+	pub value: i64,
+}
+
+/// IntGrid value group definition
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct IntGridValueGroupDefinition {
+	/// User defined color
+	pub color: Option<String>,
+
+	/// User defined string identifier
+	pub identifier: Option<String>,
+
+	/// Group unique ID
+	pub uid: i64,
+}
+
+/// Type of the layer as Haxe Enum Possible values: `IntGrid`, `Entities`, `Tiles`,
+/// `AutoLayer`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum Type {
+	#[serde(rename = "AutoLayer")]
+	AutoLayer,
+
+	Entities,
+
+	#[serde(rename = "IntGrid")]
+	IntGrid,
+
+	Tiles,
+}
+
+/// The `Tileset` definition is the most important part among project definitions. It
+/// contains some extra informations about each integrated tileset. If you only had to parse
+/// one definition section, that would be the one.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct TilesetDefinition {
+	/// Grid-based height
+	#[serde(rename = "__cHei")]
+	pub c_hei: i64,
+
+	/// Grid-based width
+	#[serde(rename = "__cWid")]
+	pub c_wid: i64,
+
+	/// The following data is used internally for various optimizations. It's always synced with
+	/// source image changes.
+	pub cached_pixel_data: Option<HashMap<String, Option<serde_json::Value>>>,
+
+	/// An array of custom tile metadata
+	pub custom_data: Vec<TileCustomMetadata>,
+
+	/// If this value is set, then it means that this atlas uses an internal LDtk atlas image
+	/// instead of a loaded one. Possible values: &lt;`null`&gt;, `LdtkIcons`
+	pub embed_atlas: Option<EmbedAtlas>,
+
+	/// Tileset tags using Enum values specified by `tagsSourceEnumId`. This array contains 1
+	/// element per Enum value, which contains an array of all Tile IDs that are tagged with it.
+	pub enum_tags: Vec<EnumTagValue>,
+
+	/// User defined unique identifier
+	pub identifier: String,
+
+	/// Distance in pixels from image borders
+	pub padding: i64,
+
+	/// Image height in pixels
+	pub px_hei: i64,
+
+	/// Image width in pixels
+	pub px_wid: i64,
+
+	/// Path to the source file, relative to the current project JSON file<br/>  It can be null
+	/// if no image was provided, or when using an embed atlas.
+	pub rel_path: Option<String>,
+
+	/// Array of group of tiles selections, only meant to be used in the editor
+	pub saved_selections: Vec<HashMap<String, Option<serde_json::Value>>>,
+
+	/// Space in pixels between all tiles
+	pub spacing: i64,
+
+	/// An array of user-defined tags to organize the Tilesets
+	pub tags: Vec<String>,
+
+	/// Optional Enum definition UID used for this tileset meta-data
+	pub tags_source_enum_uid: Option<i64>,
+
+	pub tile_grid_size: i64,
+
+	/// Unique Intidentifier
+	pub uid: i64,
+}
+
+/// In a tileset definition, user defined meta-data of a tile.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct TileCustomMetadata {
+	pub data: String,
+
+	pub tile_id: i64,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum EmbedAtlas {
+	#[serde(rename = "LdtkIcons")]
+	LdtkIcons,
+}
+
+/// In a tileset definition, enum based tag infos
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct EnumTagValue {
+	pub enum_value_id: String,
+
+	pub tile_ids: Vec<i64>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum Flag {
+	#[serde(rename = "DiscardPreCsvIntGrid")]
+	DiscardPreCsvIntGrid,
+
+	#[serde(rename = "ExportPreCsvIntGridFormat")]
+	ExportPreCsvIntGridFormat,
+
+	#[serde(rename = "IgnoreBackupSuggest")]
+	IgnoreBackupSuggest,
+
+	#[serde(rename = "MultiWorlds")]
+	MultiWorlds,
+
+	#[serde(rename = "PrependIndexToLevelFileNames")]
+	PrependIndexToLevelFileNames,
+
+	#[serde(rename = "UseMultilinesType")]
+	UseMultilinesType,
+}
+
+/// This object is not actually used by LDtk. It ONLY exists to force explicit references to
+/// all types, to make sure QuickType finds them and integrate all of them. Otherwise,
+/// Quicktype will drop types that are not explicitely used.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "PascalCase")]
+pub struct ForcedRefs {
+	pub auto_layer_rule_group: Option<AutoLayerRuleGroup>,
+
+	pub auto_rule_def: Option<AutoLayerRuleDefinition>,
+
+	pub custom_command: Option<LdtkCustomCommand>,
+
+	pub definitions: Option<Definitions>,
+
+	pub entity_def: Option<EntityDefinition>,
+
+	pub entity_instance: Option<EntityInstance>,
+
+	pub entity_reference_infos: Option<ReferenceToAnEntityInstance>,
+
+	pub enum_def: Option<EnumDefinition>,
+
+	pub enum_def_values: Option<EnumValueDefinition>,
+
+	pub enum_tag_value: Option<EnumTagValue>,
+
+	pub field_def: Option<FieldDefinition>,
+
+	pub field_instance: Option<FieldInstance>,
+
+	pub grid_point: Option<GridPoint>,
+
+	pub int_grid_value_def: Option<IntGridValueDefinition>,
+
+	pub int_grid_value_group_def: Option<IntGridValueGroupDefinition>,
+
+	pub int_grid_value_instance: Option<IntGridValueInstance>,
+
+	pub layer_def: Option<LayerDefinition>,
+
+	pub layer_instance: Option<LayerInstance>,
+
+	pub level: Option<Level>,
+
+	pub level_bg_pos_infos: Option<LevelBackgroundPosition>,
+
+	pub neighbour_level: Option<NeighbourLevel>,
+
+	pub table_of_content_entry: Option<LdtkTableOfContentEntry>,
+
+	pub tile: Option<TileInstance>,
+
+	pub tile_custom_metadata: Option<TileCustomMetadata>,
+
+	pub tileset_def: Option<TilesetDefinition>,
+
+	pub tileset_rect: Option<TilesetRectangle>,
+
+	pub world: Option<World>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct EntityInstance {
+	/// Grid-based coordinates (`[x,y]` format)
+	#[serde(rename = "__grid")]
+	pub grid: Vec<i64>,
+
+	/// Entity definition identifier
+	#[serde(rename = "__identifier")]
+	pub identifier: String,
+
+	/// Pivot coordinates  (`[x,y]` format, values are from 0 to 1) of the Entity
+	#[serde(rename = "__pivot")]
+	pub pivot: Vec<f64>,
+
+	/// The entity "smart" color, guessed from either Entity definition, or one its field
+	/// instances.
+	#[serde(rename = "__smartColor")]
+	pub smart_color: String,
+
+	/// Array of tags defined in this Entity definition
+	#[serde(rename = "__tags")]
+	pub tags: Vec<String>,
+
+	/// Optional TilesetRect used to display this entity (it could either be the default Entity
+	/// tile, or some tile provided by a field value, like an Enum).
+	#[serde(rename = "__tile")]
+	pub tile: Option<TilesetRectangle>,
+
+	/// X world coordinate in pixels
+	#[serde(rename = "__worldX")]
+	pub world_x: i64,
+
+	/// Y world coordinate in pixels
+	#[serde(rename = "__worldY")]
+	pub world_y: i64,
+
+	/// Reference of the **Entity definition** UID
+	pub def_uid: i64,
+
+	/// An array of all custom fields and their values.
+	pub field_instances: Vec<FieldInstance>,
+
+	/// Entity height in pixels. For non-resizable entities, it will be the same as Entity
+	/// definition.
+	pub height: i64,
+
+	/// Unique instance identifier
+	pub iid: String,
+
+	/// Pixel coordinates (`[x,y]` format) in current level coordinate space. Don't forget
+	/// optional layer offsets, if they exist!
+	pub px: Vec<i64>,
+
+	/// Entity width in pixels. For non-resizable entities, it will be the same as Entity
+	/// definition.
+	pub width: i64,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct FieldInstance {
+	/// Field definition identifier
+	#[serde(rename = "__identifier")]
+	pub identifier: String,
+
+	/// Optional TilesetRect used to display this field (this can be the field own Tile, or some
+	/// other Tile guessed from the value, like an Enum).
+	#[serde(rename = "__tile")]
+	pub tile: Option<TilesetRectangle>,
+
+	/// Type of the field, such as `Int`, `Float`, `String`, `Enum(my_enum_name)`, `Bool`,
+	/// etc.<br/>  NOTE: if you enable the advanced option **Use Multilines type**, you will have
+	/// "*Multilines*" instead of "*String*" when relevant.
+	#[serde(rename = "__type")]
+	pub field_instance_type: String,
+
+	/// Actual value of the field instance. The value type varies, depending on `__type`:<br/>
+	/// - For **classic types** (ie. Integer, Float, Boolean, String, Text and FilePath), you
+	/// just get the actual value with the expected type.<br/>   - For **Color**, the value is an
+	/// hexadecimal string using "#rrggbb" format.<br/>   - For **Enum**, the value is a String
+	/// representing the selected enum value.<br/>   - For **Point**, the value is a
+	/// [GridPoint](#ldtk-GridPoint) object.<br/>   - For **Tile**, the value is a
+	/// [TilesetRect](#ldtk-TilesetRect) object.<br/>   - For **EntityRef**, the value is an
+	/// [EntityReferenceInfos](#ldtk-EntityReferenceInfos) object.<br/><br/>  If the field is an
+	/// array, then this `__value` will also be a JSON array.
+	#[serde(rename = "__value")]
+	pub value: Option<serde_json::Value>,
+
+	/// Reference of the **Field definition** UID
+	pub def_uid: i64,
+
+	/// Editor internal raw values
+	pub real_editor_values: Vec<Option<serde_json::Value>>,
+}
+
+/// This object describes the "location" of an Entity instance in the project worlds.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ReferenceToAnEntityInstance {
+	/// IID of the refered EntityInstance
+	pub entity_iid: String,
+
+	/// IID of the LayerInstance containing the refered EntityInstance
+	pub layer_iid: String,
+
+	/// IID of the Level containing the refered EntityInstance
+	pub level_iid: String,
+
+	/// IID of the World containing the refered EntityInstance
+	pub world_iid: String,
+}
+
+/// This object is just a grid-based coordinate used in Field values.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct GridPoint {
+	/// X grid-based coordinate
+	pub cx: i64,
+
+	/// Y grid-based coordinate
+	pub cy: i64,
+}
+
+/// IntGrid value instance
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct IntGridValueInstance {
+	/// Coordinate ID in the layer grid
+	pub coord_id: i64,
+
+	/// IntGrid value
+	pub v: i64,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct LayerInstance {
+	/// Grid-based height
+	#[serde(rename = "__cHei")]
+	pub c_hei: i64,
+
+	/// Grid-based width
+	#[serde(rename = "__cWid")]
+	pub c_wid: i64,
+
+	/// Grid size
+	#[serde(rename = "__gridSize")]
+	pub grid_size: i64,
+
+	/// Layer definition identifier
+	#[serde(rename = "__identifier")]
+	pub identifier: String,
+
+	/// Layer opacity as Float [0-1]
+	#[serde(rename = "__opacity")]
+	pub opacity: f64,
+
+	/// Total layer X pixel offset, including both instance and definition offsets.
+	#[serde(rename = "__pxTotalOffsetX")]
+	pub px_total_offset_x: i64,
+
+	/// Total layer Y pixel offset, including both instance and definition offsets.
+	#[serde(rename = "__pxTotalOffsetY")]
+	pub px_total_offset_y: i64,
+
+	/// The definition UID of corresponding Tileset, if any.
+	#[serde(rename = "__tilesetDefUid")]
+	pub tileset_def_uid: Option<i64>,
+
+	/// The relative path to corresponding Tileset, if any.
+	#[serde(rename = "__tilesetRelPath")]
+	pub tileset_rel_path: Option<String>,
+
+	/// Layer type (possible values: IntGrid, Entities, Tiles or AutoLayer)
+	#[serde(rename = "__type")]
+	pub layer_instance_type: String,
+
+	/// An array containing all tiles generated by Auto-layer rules. The array is already sorted
+	/// in display order (ie. 1st tile is beneath 2nd, which is beneath 3rd etc.).<br/><br/>
+	/// Note: if multiple tiles are stacked in the same cell as the result of different rules,
+	/// all tiles behind opaque ones will be discarded.
+	pub auto_layer_tiles: Vec<TileInstance>,
+
+	pub entity_instances: Vec<EntityInstance>,
+
+	pub grid_tiles: Vec<TileInstance>,
+
+	/// Unique layer instance identifier
+	pub iid: String,
+
+	/// **WARNING**: this deprecated value is no longer exported since version 1.0.0  Replaced
+	/// by: `intGridCsv`
+	pub int_grid: Option<Vec<IntGridValueInstance>>,
+
+	/// A list of all values in the IntGrid layer, stored in CSV format (Comma Separated
+	/// Values).<br/>  Order is from left to right, and top to bottom (ie. first row from left to
+	/// right, followed by second row, etc).<br/>  `0` means "empty cell" and IntGrid values
+	/// start at 1.<br/>  The array size is `__cWid` x `__cHei` cells.
+	pub int_grid_csv: Vec<i64>,
+
+	/// Reference the Layer definition UID
+	pub layer_def_uid: i64,
+
+	/// Reference to the UID of the level containing this layer instance
+	pub level_id: i64,
+
+	/// An Array containing the UIDs of optional rules that were enabled in this specific layer
+	/// instance.
+	pub optional_rules: Vec<i64>,
+
+	/// This layer can use another tileset by overriding the tileset UID here.
+	pub override_tileset_uid: Option<i64>,
+
+	/// X offset in pixels to render this layer, usually 0 (IMPORTANT: this should be added to
+	/// the `LayerDef` optional offset, so you should probably prefer using `__pxTotalOffsetX`
+	/// which contains the total offset value)
+	pub px_offset_x: i64,
+
+	/// Y offset in pixels to render this layer, usually 0 (IMPORTANT: this should be added to
+	/// the `LayerDef` optional offset, so you should probably prefer using `__pxTotalOffsetX`
+	/// which contains the total offset value)
+	pub px_offset_y: i64,
+
+	/// Random seed used for Auto-Layers rendering
+	pub seed: i64,
+
+	/// Layer instance visibility
+	pub visible: bool,
+}
+
+/// This structure represents a single tile from a given Tileset.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct TileInstance {
+	/// Alpha/opacity of the tile (0-1, defaults to 1)
+	pub a: f64,
+
+	/// Internal data used by the editor.<br/>  For auto-layer tiles: `[ruleId, coordId]`.<br/>
+	/// For tile-layer tiles: `[coordId]`.
+	pub d: Vec<i64>,
+
+	/// "Flip bits", a 2-bits integer to represent the mirror transformations of the tile.<br/>
+	/// - Bit 0 = X flip<br/>   - Bit 1 = Y flip<br/>   Examples: f=0 (no flip), f=1 (X flip
+	/// only), f=2 (Y flip only), f=3 (both flips)
+	pub f: i64,
+
+	/// Pixel coordinates of the tile in the **layer** (`[x,y]` format). Don't forget optional
+	/// layer offsets, if they exist!
+	pub px: Vec<i64>,
+
+	/// Pixel coordinates of the tile in the **tileset** (`[x,y]` format)
+	pub src: Vec<i64>,
+
+	/// The *Tile ID* in the corresponding tileset.
+	pub t: i64,
+}
+
+/// This section contains all the level data. It can be found in 2 distinct forms, depending
+/// on Project current settings:  - If "*Separate level files*" is **disabled** (default):
+/// full level data is *embedded* inside the main Project JSON file, - If "*Separate level
+/// files*" is **enabled**: level data is stored in *separate* standalone `.ldtkl` files (one
+/// per level). In this case, the main Project JSON file will still contain most level data,
+/// except heavy sections, like the `layerInstances` array (which will be null). The
+/// `externalRelPath` string points to the `ldtkl` file.  A `ldtkl` file is just a JSON file
+/// containing exactly what is described below.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct Level {
+	/// Background color of the level (same as `bgColor`, except the default value is
+	/// automatically used here if its value is `null`)
+	#[serde(rename = "__bgColor")]
+	pub bg_color: String,
+
+	/// Position informations of the background image, if there is one.
+	#[serde(rename = "__bgPos")]
+	pub bg_pos: Option<LevelBackgroundPosition>,
+
+	/// An array listing all other levels touching this one on the world map. Since 1.4.0, this
+	/// includes levels that overlap in the same world layer, or in nearby world layers.<br/>
+	/// Only relevant for world layouts where level spatial positioning is manual (ie. GridVania,
+	/// Free). For Horizontal and Vertical layouts, this array is always empty.
+	#[serde(rename = "__neighbours")]
+	pub neighbours: Vec<NeighbourLevel>,
+
+	/// The "guessed" color for this level in the editor, decided using either the background
+	/// color or an existing custom field.
+	#[serde(rename = "__smartColor")]
+	pub smart_color: String,
+
+	/// Background color of the level. If `null`, the project `defaultLevelBgColor` should be
+	/// used.
+	#[serde(rename = "bgColor")]
+	pub level_bg_color: Option<String>,
+
+	/// Background image X pivot (0-1)
+	pub bg_pivot_x: f64,
+
+	/// Background image Y pivot (0-1)
+	pub bg_pivot_y: f64,
+
+	/// An enum defining the way the background image (if any) is positioned on the level. See
+	/// `__bgPos` for resulting position info. Possible values: &lt;`null`&gt;, `Unscaled`,
+	/// `Contain`, `Cover`, `CoverDirty`, `Repeat`
+	#[serde(rename = "bgPos")]
+	pub level_bg_pos: Option<BgPos>,
+
+	/// The *optional* relative path to the level background image.
+	pub bg_rel_path: Option<String>,
+
+	/// This value is not null if the project option "*Save levels separately*" is enabled. In
+	/// this case, this **relative** path points to the level Json file.
+	pub external_rel_path: Option<String>,
+
+	/// An array containing this level custom field values.
+	pub field_instances: Vec<FieldInstance>,
+
+	/// User defined unique identifier
+	pub identifier: String,
+
+	/// Unique instance identifier
+	pub iid: String,
+
+	/// An array containing all Layer instances. **IMPORTANT**: if the project option "*Save
+	/// levels separately*" is enabled, this field will be `null`.<br/>  This array is **sorted
+	/// in display order**: the 1st layer is the top-most and the last is behind.
+	pub layer_instances: Option<Vec<LayerInstance>>,
+
+	/// Height of the level in pixels
+	pub px_hei: i64,
+
+	/// Width of the level in pixels
+	pub px_wid: i64,
+
+	/// Unique Int identifier
+	pub uid: i64,
+
+	/// If TRUE, the level identifier will always automatically use the naming pattern as defined
+	/// in `Project.levelNamePattern`. Becomes FALSE if the identifier is manually modified by
+	/// user.
+	pub use_auto_identifier: bool,
+
+	/// Index that represents the "depth" of the level in the world. Default is 0, greater means
+	/// "above", lower means "below".<br/>  This value is mostly used for display only and is
+	/// intended to make stacking of levels easier to manage.
+	pub world_depth: i64,
+
+	/// World X coordinate in pixels.<br/>  Only relevant for world layouts where level spatial
+	/// positioning is manual (ie. GridVania, Free). For Horizontal and Vertical layouts, the
+	/// value is always -1 here.
+	pub world_x: i64,
+
+	/// World Y coordinate in pixels.<br/>  Only relevant for world layouts where level spatial
+	/// positioning is manual (ie. GridVania, Free). For Horizontal and Vertical layouts, the
+	/// value is always -1 here.
+	pub world_y: i64,
+}
+
+/// Level background image position info
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct LevelBackgroundPosition {
+	/// An array of 4 float values describing the cropped sub-rectangle of the displayed
+	/// background image. This cropping happens when original is larger than the level bounds.
+	/// Array format: `[ cropX, cropY, cropWidth, cropHeight ]`
+	pub crop_rect: Vec<f64>,
+
+	/// An array containing the `[scaleX,scaleY]` values of the **cropped** background image,
+	/// depending on `bgPos` option.
+	pub scale: Vec<f64>,
+
+	/// An array containing the `[x,y]` pixel coordinates of the top-left corner of the
+	/// **cropped** background image, depending on `bgPos` option.
+	pub top_left_px: Vec<i64>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum BgPos {
+	Contain,
+
+	Cover,
+
+	#[serde(rename = "CoverDirty")]
+	CoverDirty,
+
+	Repeat,
+
+	Unscaled,
+}
+
+/// Nearby level info
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct NeighbourLevel {
+	/// A single lowercase character tipping on the level location (`n`orth, `s`outh, `w`est,
+	/// `e`ast).<br/>  Since 1.4.0, this character value can also be `<` (neighbour depth is
+	/// lower), `>` (neighbour depth is greater) or `o` (levels overlap and share the same world
+	/// depth).
+	pub dir: String,
+
+	/// Neighbour Instance Identifier
+	pub level_iid: String,
+
+	/// **WARNING**: this deprecated value is no longer exported since version 1.2.0  Replaced
+	/// by: `levelIid`
+	pub level_uid: Option<i64>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct LdtkTableOfContentEntry {
+	pub identifier: String,
+
+	pub instances: Vec<ReferenceToAnEntityInstance>,
+}
+
+/// **IMPORTANT**: this type is available as a preview. You can rely on it to update your
+/// importers, for when it will be officially available.  A World contains multiple levels,
+/// and it has its own layout settings.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct World {
+	/// Default new level height
+	pub default_level_height: i64,
+
+	/// Default new level width
+	pub default_level_width: i64,
+
+	/// User defined unique identifier
+	pub identifier: String,
+
+	/// Unique instance identifer
+	pub iid: String,
+
+	/// All levels from this world. The order of this array is only relevant in
+	/// `LinearHorizontal` and `linearVertical` world layouts (see `worldLayout` value).
+	/// Otherwise, you should refer to the `worldX`,`worldY` coordinates of each Level.
+	pub levels: Vec<Level>,
+
+	/// Height of the world grid in pixels.
+	pub world_grid_height: i64,
+
+	/// Width of the world grid in pixels.
+	pub world_grid_width: i64,
+
+	/// An enum that describes how levels are organized in this project (ie. linearly or in a 2D
+	/// space). Possible values: `Free`, `GridVania`, `LinearHorizontal`, `LinearVertical`, `null`
+	pub world_layout: Option<WorldLayout>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum WorldLayout {
+	Free,
+
+	#[serde(rename = "GridVania")]
+	GridVania,
+
+	#[serde(rename = "LinearHorizontal")]
+	LinearHorizontal,
+
+	#[serde(rename = "LinearVertical")]
+	LinearVertical,
+}
+
+/// Naming convention for Identifiers (first-letter uppercase, full uppercase etc.) Possible
+/// values: `Capitalize`, `Uppercase`, `Lowercase`, `Free`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum IdentifierStyle {
+	Capitalize,
+
+	Free,
+
+	Lowercase,
+
+	Uppercase,
+}
+
+/// "Image export" option when saving project. Possible values: `None`, `OneImagePerLayer`,
+/// `OneImagePerLevel`, `LayersAndLevels`
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum ImageExportMode {
+	#[serde(rename = "LayersAndLevels")]
+	LayersAndLevels,
+
+	None,
+
+	#[serde(rename = "OneImagePerLayer")]
+	OneImagePerLayer,
+
+	#[serde(rename = "OneImagePerLevel")]
+	OneImagePerLevel,
+}
diff --git a/src/ldtk/mod.rs b/src/ldtk/mod.rs
index d7e55590b415221a8c266bd843b942e579f49050..cd212df6228775fb54ce71074a6423cd32a20ea2 100644
--- a/src/ldtk/mod.rs
+++ b/src/ldtk/mod.rs
@@ -14,10 +14,18 @@ mod data_1_2_4;
 mod data_1_2_5;
 #[cfg(feature = "ldtk_1_3_0")]
 mod data_1_3_0;
-
-use bevy::asset::{AssetLoader, AssetPath, BoxedFuture, LoadContext, LoadedAsset};
+#[cfg(feature = "ldtk_1_4_0")]
+mod data_1_4_0;
+
+use bevy::asset::io::Reader;
+use bevy::asset::{
+	AssetLoader, AssetPath, AsyncReadExt, BoxedFuture, LoadContext, LoadedAsset, UntypedAssetId,
+	VisitAssetDependencies,
+};
+use bevy::prelude::Asset;
 use bevy::reflect::{TypePath, TypeUuid, Uuid};
 
+use crate::{ldtk, LdtkLevel};
 #[cfg(feature = "ldtk_1_0_0")]
 pub use data_1_0_0::*;
 #[cfg(any(feature = "ldtk_1_1_1", feature = "ldtk_1_1_0"))]
@@ -34,6 +42,8 @@ pub use data_1_2_4::*;
 pub use data_1_2_5::*;
 #[cfg(feature = "ldtk_1_3_0")]
 pub use data_1_3_0::*;
+#[cfg(feature = "ldtk_1_4_0")]
+pub use data_1_4_0::*;
 use serde::Deserialize;
 
 #[derive(thiserror::Error, Debug)]
@@ -91,6 +101,12 @@ impl TypePath for Project {
 	}
 }
 
+impl VisitAssetDependencies for Project {
+	fn visit_dependencies(&self, _visit: &mut impl FnMut(UntypedAssetId)) {}
+}
+
+impl Asset for Project {}
+
 impl TypeUuid for Level {
 	const TYPE_UUID: Uuid = Uuid::from_u128(18486863672600588966868281477384349187);
 }
@@ -105,6 +121,12 @@ impl TypePath for Level {
 	}
 }
 
+impl VisitAssetDependencies for Level {
+	fn visit_dependencies(&self, _visit: &mut impl FnMut(UntypedAssetId)) {}
+}
+
+impl Asset for Level {}
+
 impl Project {
 	pub fn get_all_levels(&self) -> Vec<&Level> {
 		if !self.worlds.is_empty() {
@@ -134,7 +156,7 @@ impl Project {
 		vec![]
 	}
 
-	#[cfg(any(feature = "ldtk_1_3_0",))]
+	#[cfg(any(feature = "ldtk_1_3_0", feature = "ldtk_1_4_0"))]
 	pub fn get_world_levels(&self, identifier: impl ToString) -> Vec<&Level> {
 		let id = identifier.to_string();
 		self.worlds
@@ -145,58 +167,56 @@ impl Project {
 	}
 }
 
+#[derive(Debug, thiserror::Error)]
+pub enum LdtkLoadError {
+	#[error(transparent)]
+	Io(#[from] std::io::Error),
+	#[error(transparent)]
+	Serde(#[from] serde_json::Error),
+	#[error(transparent)]
+	Ldtk(#[from] ldtk::ParseError),
+}
+
 pub type LdtkProject = Project;
 
 #[derive(Default)]
 pub struct LdtkLoader;
 impl AssetLoader for LdtkLoader {
+	type Asset = Project;
+	type Settings = ();
+	type Error = LdtkLoadError;
+
 	fn load<'a>(
 		&'a self,
-		bytes: &'a [u8],
+		reader: &'a mut Reader,
+		_settings: &'a Self::Settings,
 		load_context: &'a mut LoadContext,
-	) -> BoxedFuture<'a, anyhow::Result<(), anyhow::Error>> {
+	) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
 		Box::pin(async move {
-			log::debug!(
-				"Loading ldtk project file {}",
-				load_context.path().display()
-			);
-
-			let project = Project::from_bytes(bytes)?;
+			let mut bytes = Vec::new();
+			reader.read_to_end(&mut bytes).await?;
+			let project = Project::from_bytes(bytes.as_slice())?;
+
+			let mut levels = project.levels.iter().flat_map(|level| {
+				log::debug!(
+					"Checking if level is external: {} [{}]",
+					level.identifier,
+					level.external_rel_path.is_some()
+				);
+
+				level
+					.external_rel_path
+					.as_ref()
+					.map(|path| (level.identifier.clone(), path))
+			});
+
+			for (id, path) in levels {
+				load_context.labeled_asset_scope(id, |lc| {
+					lc.load::<Level>(path);
+				});
+			}
 
-			let sub_levels = project
-				.levels
-				.iter()
-				.flat_map(|level| {
-					log::debug!(
-						"Checking if level is external: {} [{}]",
-						level.identifier,
-						level.external_rel_path.is_some()
-					);
-
-					level
-						.external_rel_path
-						.as_ref()
-						.map(|rel_path| (level.identifier.clone(), rel_path.clone()))
-				})
-				.collect::<Vec<(String, String)>>();
-
-			let asset = LoadedAsset::new(project).with_dependencies(
-				sub_levels
-					.into_iter()
-					.flat_map(|(id, path)| {
-						log::debug!(
-							"Checking for file {}",
-							load_context.path().parent()?.join(&path).display()
-						);
-
-						let path = load_context.path().parent()?.join(path);
-						Some(AssetPath::new(path, Some(id)))
-					})
-					.collect(),
-			);
-
-			load_context.set_default_asset(asset);
-			Ok(())
+			Ok(project)
 		})
 	}
 
@@ -204,21 +224,27 @@ impl AssetLoader for LdtkLoader {
 		&["ldtk"]
 	}
 }
+
 #[derive(Default)]
 pub struct LdtkLevelLoader;
 impl AssetLoader for LdtkLevelLoader {
+	type Asset = Level;
+	type Settings = ();
+	type Error = LdtkLoadError;
+
 	fn load<'a>(
 		&'a self,
-		bytes: &'a [u8],
-		load_context: &'a mut LoadContext,
-	) -> BoxedFuture<'a, anyhow::Result<(), anyhow::Error>> {
+		reader: &'a mut Reader,
+		_settings: &'a Self::Settings,
+		_load_context: &'a mut LoadContext,
+	) -> BoxedFuture<'a, Result<Self::Asset, Self::Error>> {
 		Box::pin(async move {
-			log::debug!("Loading ldtkl level file {}", load_context.path().display());
-			load_context.set_default_asset(LoadedAsset::new(Level::from_bytes(bytes)?));
-			Ok(())
+			let mut bytes = Vec::new();
+			reader.read_to_end(&mut bytes).await?;
+			let level = Level::from_bytes(bytes.as_slice())?;
+			Ok(level)
 		})
 	}
-
 	fn extensions(&self) -> &[&str] {
 		&["ldtkl"]
 	}
diff --git a/src/lib.rs b/src/lib.rs
index fb70b85650b46937e8237c264c147516a10240ae..40020d397e02a808720df13ab0ef900d1c3a0489 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -74,10 +74,10 @@ mod __plugin {
 			))]
 			{
 				app.add_event::<super::system::LevelDataUpdated>()
-					.add_asset::<super::ldtk::Project>()
-					.add_asset::<super::ldtk::Level>()
-					.add_asset_loader(super::ldtk::LdtkLoader)
-					.add_asset_loader(super::ldtk::LdtkLevelLoader)
+					.init_asset::<super::ldtk::Project>()
+					.init_asset::<super::ldtk::Level>()
+					.init_asset_loader::<super::ldtk::LdtkLoader>()
+					.init_asset_loader::<super::ldtk::LdtkLevelLoader>()
 					.init_resource::<super::assets::TilesetIndex>()
 					.init_resource::<super::assets::LevelIndex>()
 					.add_systems(Update, super::assets::handle_ldtk_project_events)