From dc5c678f23603301ed019e22adc06687c23124cd Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Sat, 10 Apr 2021 13:44:37 -0700 Subject: [PATCH 01/14] Refactor to new ItemType (with a bunch of placeholders) --- Cargo.lock | 879 ++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/global_events.rs | 11 +- src/item.rs | 59 +++ src/main.rs | 2 + src/net_syncronizer.rs | 29 +- src/pickup.rs | 48 +-- src/player.rs | 329 ++++----------- src/plugin.rs | 32 ++ src/remote_player.rs | 44 +-- 10 files changed, 1075 insertions(+), 359 deletions(-) create mode 100644 src/item.rs create mode 100644 src/plugin.rs diff --git a/Cargo.lock b/Cargo.lock index 73738d6..56c6a4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,20 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "addr2line" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" +dependencies = [ + "gimli 0.23.0", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "adler32" version = "1.2.0" @@ -18,12 +33,35 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "backtrace" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" +dependencies = [ + "addr2line", + "cfg-if 1.0.0", + "libc", + "miniz_oxide 0.4.4", + "object 0.23.0", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitfield" version = "0.13.2" @@ -94,6 +132,71 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "cranelift-bforest" +version = "0.68.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9221545c0507dc08a62b2d8b5ffe8e17ac580b0a74d1813b496b8d70b070fbd0" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.68.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9936ea608b6cd176f107037f6adbb4deac933466fc7231154f96598b2d3ab1" +dependencies = [ + "byteorder", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "gimli 0.22.0", + "log", + "regalloc", + "smallvec", + "target-lexicon", + "thiserror", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.68.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef2b2768568306540f4c8db3acce9105534d34c4a1e440529c1e702d7f8c8d7" +dependencies = [ + "cranelift-codegen-shared", + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.68.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6759012d6d19c4caec95793f052613e9d4113e925e7f14154defbac0f1d4c938" + +[[package]] +name = "cranelift-entity" +version = "0.68.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86badbce14e15f52a45b666b38abe47b204969dd7f8fb7488cb55dd46b361fa6" +dependencies = [ + "serde", +] + +[[package]] +name = "cranelift-frontend" +version = "0.68.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b608bb7656c554d0a4cf8f50c7a10b857e80306f6ff829ad6d468a7e2323c8d8" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + [[package]] name = "crc32fast" version = "1.2.1" @@ -103,6 +206,86 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "darling" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9d6ddad5866bb2170686ed03f6839d31a76e5407d80b1c334a2c24618543ffa" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9ced1fd13dc386d5a8315899de465708cf34ee2a6d9394654515214e67bb846" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a7a1445d54b2f9792e3b31a3e715feabbace393f38dc4ffd49d94ee9bc487d5" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "deflate" version = "0.8.6" @@ -113,6 +296,39 @@ dependencies = [ "byteorder", ] +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "enumset" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbd795df6708a599abf1ee10eacc72efd052b7a5f70fdf0715e4d5151a6db9c3" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e19c52f9ec503c8a68dc04daf71a04b07e690c32ab1a8b68e33897f255269d47" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fishgame" version = "0.1.0" @@ -126,15 +342,22 @@ dependencies = [ "nanoserde", "physics-platformer", "sapp-jsutils", + "wasm_plugin_host", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "fontdue" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3203e88e086474e56b0785255804a0c23542f76253584d3f43152cd2bb95d0b7" dependencies = [ - "hashbrown", + "hashbrown 0.8.2", "ttf-parser", ] @@ -185,6 +408,34 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" + [[package]] name = "glam" version = "0.10.2" @@ -204,12 +455,33 @@ dependencies = [ "autocfg", ] +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + [[package]] name = "httparse" version = "1.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.1.5" @@ -247,6 +519,17 @@ dependencies = [ "png", ] +[[package]] +name = "indexmap" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +dependencies = [ + "autocfg", + "hashbrown 0.9.1", + "serde", +] + [[package]] name = "iovec" version = "0.1.4" @@ -287,12 +570,28 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "leb128" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a" + [[package]] name = "libc" version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" +[[package]] +name = "libloading" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +dependencies = [ + "cfg-if 1.0.0", + "winapi 0.3.9", +] + [[package]] name = "log" version = "0.4.14" @@ -302,6 +601,15 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + [[package]] name = "macroquad" version = "0.3.0-alpha.17" @@ -345,6 +653,24 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +[[package]] +name = "memmap2" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397d1a6d6d0563c0f5462bbdae662cf6c784edf5e828e40c7257f85d82bf56dd" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d" +dependencies = [ + "autocfg", +] + [[package]] name = "miniquad" version = "0.3.0-alpha.29" @@ -369,6 +695,16 @@ dependencies = [ "adler32", ] +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + [[package]] name = "mio" version = "0.6.23" @@ -412,6 +748,12 @@ dependencies = [ "ws2_32-sys", ] +[[package]] +name = "more-asserts" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238" + [[package]] name = "nakama-rs" version = "0.1.0" @@ -489,6 +831,32 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" +dependencies = [ + "crc32fast", + "indexmap", +] + +[[package]] +name = "object" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" + [[package]] name = "once_cell" version = "1.7.2" @@ -542,6 +910,12 @@ dependencies = [ "macroquad", ] +[[package]] +name = "pin-project-lite" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" + [[package]] name = "pkg-config" version = "0.3.19" @@ -557,7 +931,37 @@ dependencies = [ "bitflags", "crc32fast", "deflate", - "miniz_oxide", + "miniz_oxide 0.3.7", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", ] [[package]] @@ -608,6 +1012,28 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.2", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.2", +] + [[package]] name = "rand_core" version = "0.3.1" @@ -623,6 +1049,49 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core 0.6.2", +] + +[[package]] +name = "rayon" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -632,6 +1101,47 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "redox_syscall" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regalloc" +version = "0.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5" +dependencies = [ + "log", + "rustc-hash", + "smallvec", +] + +[[package]] +name = "region" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi 0.3.9", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "ring" version = "0.16.20" @@ -647,6 +1157,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rustc-demangle" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustls" version = "0.19.0" @@ -726,6 +1248,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "sct" version = "0.6.0" @@ -736,6 +1264,35 @@ dependencies = [ "untrusted", ] +[[package]] +name = "serde" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "sha1" version = "0.6.0" @@ -748,12 +1305,30 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "1.0.65" @@ -765,6 +1340,46 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "target-lexicon" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "422045212ea98508ae3d28025bc5aaa2bd4a9cdaecd442a08da2ee620ee9ea95" + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "rand 0.8.3", + "redox_syscall", + "remove_dir_all", + "winapi 0.3.9", +] + +[[package]] +name = "thiserror" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinyvec" version = "1.1.1" @@ -780,6 +1395,38 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +[[package]] +name = "tracing" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +dependencies = [ + "lazy_static", +] + [[package]] name = "ttf-parser" version = "0.8.3" @@ -867,6 +1514,12 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "wasm-bindgen" version = "0.2.72" @@ -921,6 +1574,216 @@ version = "0.2.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa" +[[package]] +name = "wasm_plugin_host" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb37dc28122424ed9a8149a16a94bd8c2e7cbb775a207b8569929900bd6d7470" +dependencies = [ + "bincode", + "getrandom", + "serde", + "wasmer", +] + +[[package]] +name = "wasmer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a70cfae554988d904d64ca17ab0e7cd652ee5c8a0807094819c1ea93eb9d6866" +dependencies = [ + "cfg-if 0.1.10", + "indexmap", + "more-asserts", + "target-lexicon", + "thiserror", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-derive", + "wasmer-engine", + "wasmer-engine-jit", + "wasmer-engine-native", + "wasmer-types", + "wasmer-vm", + "wat", + "winapi 0.3.9", +] + +[[package]] +name = "wasmer-compiler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b7732a9cab472bd921d5a0c422f45b3d03f62fa2c40a89e0770cef6d47e383e" +dependencies = [ + "enumset", + "serde", + "serde_bytes", + "smallvec", + "target-lexicon", + "thiserror", + "wasmer-types", + "wasmer-vm", + "wasmparser", +] + +[[package]] +name = "wasmer-compiler-cranelift" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb9395f094e1d81534f4c5e330ed4cdb424e8df870d29ad585620284f5fddb" +dependencies = [ + "cranelift-codegen", + "cranelift-frontend", + "gimli 0.22.0", + "more-asserts", + "rayon", + "serde", + "smallvec", + "tracing", + "wasmer-compiler", + "wasmer-types", + "wasmer-vm", +] + +[[package]] +name = "wasmer-derive" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b86dcd2c3efdb8390728a2b56f762db07789aaa5aa872a9dc776ba3a7912ed" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "wasmer-engine" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efe4667d6bd888f26ae8062a63a9379fa697415b4b4e380f33832e8418fd71b5" +dependencies = [ + "backtrace", + "bincode", + "lazy_static", + "memmap2", + "more-asserts", + "rustc-demangle", + "serde", + "serde_bytes", + "target-lexicon", + "thiserror", + "wasmer-compiler", + "wasmer-types", + "wasmer-vm", +] + +[[package]] +name = "wasmer-engine-jit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26770be802888011b4a3072f2a282fc2faa68aa48c71b3db6252a3937a85f3da" +dependencies = [ + "bincode", + "cfg-if 0.1.10", + "region", + "serde", + "serde_bytes", + "wasmer-compiler", + "wasmer-engine", + "wasmer-types", + "wasmer-vm", + "winapi 0.3.9", +] + +[[package]] +name = "wasmer-engine-native" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb4083a6c69f2cd4b000b82a80717f37c6cc2e536aee3a8ffe9af3edc276a8b" +dependencies = [ + "bincode", + "cfg-if 0.1.10", + "leb128", + "libloading", + "serde", + "tempfile", + "tracing", + "wasmer-compiler", + "wasmer-engine", + "wasmer-object", + "wasmer-types", + "wasmer-vm", + "which", +] + +[[package]] +name = "wasmer-object" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf8e0c12b82ff81ebecd30d7e118be5fec871d6de885a90eeb105df0a769a7b" +dependencies = [ + "object 0.22.0", + "thiserror", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-types" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f4ac28c2951cd792c18332f03da523ed06b170f5cf6bb5b1bdd7e36c2a8218" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror", +] + +[[package]] +name = "wasmer-vm" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7635ba0b6d2fd325f588d69a950ad9fa04dddbf6ad08b6b2a183146319bf6ae" +dependencies = [ + "backtrace", + "cc", + "cfg-if 0.1.10", + "indexmap", + "libc", + "memoffset", + "more-asserts", + "region", + "serde", + "thiserror", + "wasmer-types", + "winapi 0.3.9", +] + +[[package]] +name = "wasmparser" +version = "0.65.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc2fe6350834b4e528ba0901e7aa405d78b89dc1fa3145359eb4de0e323fcf" + +[[package]] +name = "wast" +version = "35.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ef140f1b49946586078353a453a1d28ba90adfc54dde75710bc1931de204d68" +dependencies = [ + "leb128", +] + +[[package]] +name = "wat" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec280a739b69173e0ffd12c1658507996836ba4e992ed9bc1e5385a0bd72a02" +dependencies = [ + "wast", +] + [[package]] name = "web-sys" version = "0.3.49" @@ -950,6 +1813,16 @@ dependencies = [ "webpki", ] +[[package]] +name = "which" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55551e42cbdf2ce2bedd2203d0cc08dba002c27510f86dab6d0ce304cba3dfe" +dependencies = [ + "either", + "libc", +] + [[package]] name = "winapi" version = "0.2.8" @@ -996,7 +1869,7 @@ dependencies = [ "mio", "mio-extras", "openssl", - "rand", + "rand 0.4.6", "sha1", "slab", "url 1.7.2", diff --git a/Cargo.toml b/Cargo.toml index 4e8ae88..8fd4d0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ macroquad-particles = { git = "https://github.com/not-fl3/macroquad.git", featur bitfield = "0.13" nakama-rs = { git = "https://github.com/not-fl3/nakama-rs.git" } base64 = "0.13" +wasm_plugin_host = "0.1.2" [target.'cfg(target_arch = "wasm32")'.dependencies] sapp-jsutils = "0.1" diff --git a/src/global_events.rs b/src/global_events.rs index f33a4a1..ad9c768 100644 --- a/src/global_events.rs +++ b/src/global_events.rs @@ -6,7 +6,7 @@ use macroquad::{ prelude::*, }; -use crate::{pickup::ItemType, NetSyncronizer, Pickup, Player, RemotePlayer, Resources}; +use crate::{item::{ItemType, ItemImplementationRegistry}, NetSyncronizer, Pickup, Player, RemotePlayer, Resources}; pub struct GlobalEvents { last_spawn_time: f64, @@ -72,11 +72,10 @@ impl scene::Node for GlobalEvents { node.last_spawn_time = get_time(); - let item_type = if rand::gen_range(0, 2) == 0 { - ItemType::Gun - } else { - ItemType::Sword - }; + let item_registry = storage::get::().unwrap(); + let item_types = item_registry.item_types(); + let idx = rand::gen_range(0, item_types.len()); + let item_type = item_types[idx]; let item_id = node.uid; node.spawned_items .push((item_id, scene::add_node(Pickup::new(pos, item_type)))); diff --git a/src/item.rs b/src/item.rs new file mode 100644 index 0000000..c5f7400 --- /dev/null +++ b/src/item.rs @@ -0,0 +1,59 @@ +use std::collections::HashMap; + +use macroquad::{ + prelude::*, + experimental::{ + animation::AnimatedSprite + } +}; + +use crate::plugin::ModId; + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct ItemType(u64); +impl From for u64 { + fn from(item_type: ItemType) -> u64 { + item_type.0 + } +} +impl From for ItemType { + fn from(item_type: u64) -> ItemType { + ItemType(item_type) + } +} + +struct ItemImplementation { + display_name: String, + pub texture: Texture2D, + pub sprite: AnimatedSprite, + pub fx_sprite: AnimatedSprite, + implementing_mod: ModId, +} +pub struct ItemImplementationRegistry(HashMap); +impl ItemImplementationRegistry { + pub fn get_implementation(&self, item_type: ItemType) -> Option<&ItemImplementation> { + self.0.get(&item_type) + } + + pub fn item_types(&self) -> Vec { + self.0.keys().copied().collect() + } +} + +pub struct ItemIdSource(u64); +impl Default for ItemIdSource { + fn default() -> Self { + Self(1) + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct ItemInstanceId(u64); + +impl ItemIdSource { + pub fn next_id(&mut self) -> ItemInstanceId { + let new_id = self.0; + self.0 += 1; + ItemInstanceId(new_id) + } +} diff --git a/src/main.rs b/src/main.rs index 0744063..10cd4ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,6 +26,8 @@ mod net_syncronizer; mod pickup; mod player; mod remote_player; +mod plugin; +mod item; use bullets::Bullets; use camera::Camera; diff --git a/src/net_syncronizer.rs b/src/net_syncronizer.rs index 3de0e53..e02ae52 100644 --- a/src/net_syncronizer.rs +++ b/src/net_syncronizer.rs @@ -13,7 +13,7 @@ use std::collections::{BTreeMap, BTreeSet}; use crate::{ consts, nakama::{self, ApiClient}, - pickup::ItemType, + item::ItemType, GameType, Pickup, Player, RemotePlayer, Resources, }; @@ -37,7 +37,7 @@ bitfield::bitfield! { y, set_y: 19, 10; facing, set_facing: 20; shooting, set_shooting: 21; - weapon, set_weapon: 30, 22; + u64, weapon, set_weapon: 30, 22; dead, set_dead: 31; } @@ -83,7 +83,7 @@ mod message { pub id: u32, pub x: u16, pub y: u16, - pub item_type: u8, + pub item_type: u64, } impl SpawnItem { pub const OPCODE: i32 = 4; @@ -172,7 +172,7 @@ impl NetSyncronizer { id: id as _, x: pos.x as _, y: pos.y as _, - item_type: item_type as _, + item_type: item_type.into(), }, ); } @@ -304,12 +304,7 @@ impl scene::Node for NetSyncronizer { state.set_y(player.pos().y as u32); state.set_facing(player.facing()); state.set_shooting(shooting); - let weapon = match player.weapon() { - None => 0, - Some(ItemType::Gun) => 1, - Some(ItemType::Sword) => 2, - }; - state.set_weapon(weapon); + state.set_weapon(player.weapon().map_or(0, |weapon| weapon.into())); state.set_dead(player.is_dead()); if node.network_cache.sent_position != state.0 { @@ -391,14 +386,10 @@ impl scene::Node for NetSyncronizer { resources.disarm_fxses.spawn(pos + vec2(16., 33.)); other.disarm(); } - if other.weapon().map_or(0, |weapon| weapon as u32) + if other.weapon().map_or(0, |weapon| weapon.into() ) != state.weapon() { - match state.weapon() { - 1 => other.pick_weapon(ItemType::Gun), - 2 => other.pick_weapon(ItemType::Sword), - _ => unreachable!(), - } + other.pick_weapon(state.weapon().into()); } if state.shooting() { let handle = other.handle(); @@ -430,11 +421,7 @@ impl scene::Node for NetSyncronizer { let new_node = scene::add_node(Pickup::new( pos, - match item_type { - x if x == ItemType::Sword as u8 => ItemType::Sword, - x if x == ItemType::Gun as u8 => ItemType::Gun, - _ => unreachable!(), - }, + item_type.into() )); if let Some(pickup) = node.pickups.insert(id as _, new_node) { if let Some(node) = scene::get_node(pickup) { diff --git a/src/pickup.rs b/src/pickup.rs index 6ec0667..2c23b42 100644 --- a/src/pickup.rs +++ b/src/pickup.rs @@ -7,14 +7,7 @@ use macroquad::{ prelude::*, }; -use crate::Resources; - -#[derive(Debug, Clone, Copy, PartialEq)] -#[repr(u8)] -pub enum ItemType { - Gun = 1, - Sword = 2, -} +use crate::{Resources, item::{ItemType, ItemImplementationRegistry}}; pub struct Pickup { pub pos: Vec2, @@ -80,29 +73,20 @@ impl scene::Node for Pickup { ), ); - match node.item_type { - ItemType::Gun => draw_texture_ex( - resources.gun, - node.pos.x, - node.pos.y + 8., - WHITE, - DrawTextureParams { - source: Some(Rect::new(0.0, 0.0, 64., 32.)), - dest_size: Some(vec2(32., 16.)), - ..Default::default() - }, - ), - ItemType::Sword => draw_texture_ex( - resources.sword, - node.pos.x + 4., - node.pos.y - 4., - WHITE, - DrawTextureParams { - source: Some(Rect::new(195.0 + 5., 93.0 + 5., 65. - 10., 93. - 10.)), - dest_size: Some(vec2(32., 32.)), - ..Default::default() - }, - ), - } + let item_registry = storage::get::().unwrap(); + let item_impl = item_registry.get_implementation(node.item_type).expect("Invalid ItemType"); + + //TODO: position sprite + draw_texture_ex( + item_impl.texture, + node.pos.x, + node.pos.y + 8., + WHITE, + DrawTextureParams { + source: Some(Rect::new(0.0, 0.0, 64., 32.)), + dest_size: Some(vec2(32., 16.)), + ..Default::default() + }, + ); } } diff --git a/src/player.rs b/src/player.rs index a65f495..00ad24d 100644 --- a/src/player.rs +++ b/src/player.rs @@ -12,7 +12,7 @@ use macroquad::{ }; use physics_platformer::Actor; -use crate::{consts, pickup::ItemType, Pickup, Resources}; +use crate::{consts, item::{ItemType, ItemInstanceId, ItemIdSource, ItemImplementationRegistry}, Pickup, Resources}; use crate::nakama::ApiClient; #[derive(Default, Debug, Clone)] @@ -23,17 +23,22 @@ pub struct Input { right: bool, } -pub enum Weapon { - Gun { bullets: i32 }, - Sword, +pub struct Weapon { + pub item_type: ItemType, + item_id: ItemInstanceId, + sprite: AnimatedSprite, + fx_sprite: AnimatedSprite, + fx: bool, +} + +impl Weapon { + fn uses_remaining(&self) -> Option<(u32, u32)> { + todo!("delegate to plugin") + } } pub struct Fish { fish_sprite: AnimatedSprite, - gun_sprite: AnimatedSprite, - pub sword_sprite: AnimatedSprite, - gun_fx_sprite: AnimatedSprite, - gun_fx: bool, pub collider: Actor, pos: Vec2, speed: Vec2, @@ -79,61 +84,9 @@ impl Fish { ], true, ); - let gun_sprite = AnimatedSprite::new( - 92, - 32, - &[ - Animation { - name: "idle".to_string(), - row: 0, - frames: 1, - fps: 1, - }, - Animation { - name: "shoot".to_string(), - row: 1, - frames: 3, - fps: 15, - }, - ], - false, - ); - let sword_sprite = AnimatedSprite::new( - 65, - 93, - &[ - Animation { - name: "idle".to_string(), - row: 0, - frames: 1, - fps: 1, - }, - Animation { - name: "shoot".to_string(), - row: 1, - frames: 4, - fps: 15, - }, - ], - false, - ); - let gun_fx_sprite = AnimatedSprite::new( - 92, - 32, - &[Animation { - name: "shoot".to_string(), - row: 2, - frames: 3, - fps: 15, - }], - false, - ); + Fish { fish_sprite, - gun_fx_sprite, - gun_fx: false, - gun_sprite, - sword_sprite, collider: resources.collision_world.add_actor(spawner_pos, 30, 54), on_ground: false, dead: false, @@ -170,12 +123,16 @@ impl Fish { } pub fn pick_weapon(&mut self, item_type: ItemType) { - match item_type { - ItemType::Gun => { - self.weapon = Some(Weapon::Gun { bullets: 3 }); - } - ItemType::Sword => self.weapon = Some(Weapon::Sword), - } + let item_id_source = storage::get_mut::().unwrap(); + let item_registry = storage::get::().unwrap(); + let item_impl = item_registry.get_implementation(item_type).expect("Invalid ItemType"); + self.weapon = Some(Weapon { + item_type, + item_id: item_id_source.next_id(), + sprite: item_impl.sprite.clone(), + fx_sprite: item_impl.fx_sprite.clone(), + fx: false, + }); } pub fn facing_dir(&self) -> f32 { @@ -204,62 +161,45 @@ impl Fish { }, ); - if self.dead == false && matches!(self.weapon, Some(Weapon::Gun { .. })) { - let gun_mount_pos = if self.facing { - vec2(0., 16.) - } else { - vec2(-60., 16.) - }; - self.gun_sprite.update(); - draw_texture_ex( - resources.gun, - self.pos.x + gun_mount_pos.x, - self.pos.y + gun_mount_pos.y, - color::WHITE, - DrawTextureParams { - source: Some(self.gun_sprite.frame().source_rect), - dest_size: Some(self.gun_sprite.frame().dest_size), - flip_x: !self.facing, - ..Default::default() - }, - ); - - if self.gun_fx { - self.gun_fx_sprite.update(); + if self.dead == false { + if let Some(weapon) = &mut self.weapon { + let item_registry = storage::get::().unwrap(); + let item_impl = item_registry.get_implementation(weapon.item_type).expect("Invalid ItemType"); + let mount_pos = if self.facing { + vec2(0., 16.) + } else { + vec2(-60., 16.) + }; + weapon.sprite.update(); draw_texture_ex( - resources.gun, - self.pos.x + gun_mount_pos.x, - self.pos.y + gun_mount_pos.y, + item_impl.texture, + self.pos.x + mount_pos.x, + self.pos.y + mount_pos.y, color::WHITE, DrawTextureParams { - source: Some(self.gun_fx_sprite.frame().source_rect), - dest_size: Some(self.gun_fx_sprite.frame().dest_size), + source: Some(weapon.sprite.frame().source_rect), + dest_size: Some(weapon.sprite.frame().dest_size), flip_x: !self.facing, ..Default::default() }, ); - } - } - if self.dead == false && matches!(self.weapon, Some(Weapon::Sword)) { - let sword_mount_pos = if self.facing { - vec2(10., -35.) - } else { - vec2(-50., -35.) - }; - self.sword_sprite.update(); - draw_texture_ex( - resources.sword, - self.pos.x + sword_mount_pos.x, - self.pos.y + sword_mount_pos.y, - color::WHITE, - DrawTextureParams { - source: Some(self.sword_sprite.frame().source_rect), - dest_size: Some(self.sword_sprite.frame().dest_size), - flip_x: !self.facing, - ..Default::default() - }, - ); + if weapon.fx { + weapon.fx_sprite.update(); + draw_texture_ex( + item_impl.texture, + self.pos.x + mount_pos.x, + self.pos.y + mount_pos.y, + color::WHITE, + DrawTextureParams { + source: Some(weapon.fx_sprite.frame().source_rect), + dest_size: Some(weapon.fx_sprite.frame().dest_size), + flip_x: !self.facing, + ..Default::default() + }, + ); + } + } } } } @@ -278,8 +218,7 @@ impl Player { const ST_NORMAL: usize = 0; const ST_DEATH: usize = 1; const ST_SHOOT: usize = 2; - const ST_SWORD_SHOOT: usize = 3; - const ST_AFTERMATCH: usize = 4; + const ST_AFTERMATCH: usize = 3; pub fn new(deathmatch: bool) -> Player { let spawner_pos = { @@ -301,13 +240,6 @@ impl Player { Self::ST_SHOOT, State::new() .update(Self::update_shoot) - .coroutine(Self::shoot_coroutine), - ); - state_machine.add_state( - Self::ST_SWORD_SHOOT, - State::new() - .update(Self::update_sword_shoot) - .coroutine(Self::sword_shoot_coroutine), ); state_machine.add_state( Self::ST_AFTERMATCH, @@ -334,7 +266,6 @@ impl Player { pub fn pick_weapon(&mut self, item_type: ItemType) { if self.state_machine.state() != Self::ST_SHOOT - && self.state_machine.state() != Self::ST_SWORD_SHOOT { self.fish.pick_weapon(item_type); } @@ -350,11 +281,7 @@ impl Player { } pub fn weapon(&self) -> Option { - match self.fish.weapon { - None => None, - Some(Weapon::Gun { .. }) => Some(ItemType::Gun), - Some(Weapon::Sword) => Some(ItemType::Sword), - } + self.fish.weapon.map(|weapon| weapon.item_type) } fn death_coroutine(node: &mut RefMut) -> Coroutine { @@ -423,118 +350,8 @@ impl Player { start_coroutine(coroutine) } - fn shoot_coroutine(node: &mut RefMut) -> Coroutine { - let handle = node.handle(); - let coroutine = async move { - { - let mut node = &mut *scene::get_node(handle).unwrap(); - - node.fish.gun_fx = true; - let mut net_syncronizer = - scene::find_node_by_type::().unwrap(); - net_syncronizer.shoot(); - - let mut bullets = scene::find_node_by_type::().unwrap(); - bullets.spawn_bullet(node.fish.pos, node.fish.facing); - node.fish.speed.x = -consts::GUN_THROWBACK * node.fish.facing_dir(); - } - { - let node = &mut *scene::get_node(handle).unwrap(); - node.fish.gun_sprite.set_animation(1); - } - for i in 0u32..3 { - { - let node = &mut *scene::get_node(handle).unwrap(); - node.fish.gun_sprite.set_frame(i); - node.fish.gun_fx_sprite.set_frame(i); - } - - wait_seconds(0.08).await; - } - { - let mut node = scene::get_node(handle).unwrap(); - node.fish.gun_sprite.set_animation(0); - } - - let mut node = &mut *scene::get_node(handle).unwrap(); - - node.fish.gun_fx = false; - - if let Weapon::Gun { bullets } = node.fish.weapon.as_mut().unwrap() { - *bullets -= 1; - - if *bullets <= 0 { - let mut resources = storage::get_mut::().unwrap(); - resources.disarm_fxses.spawn(node.fish.pos + vec2(16., 33.)); - node.fish.weapon = None; - } - } - - // node.weapon_animation.play(0, 0..5).await; - // node.weapon_animation.play(0, 5..).await; - node.state_machine.set_state(Self::ST_NORMAL); - }; - - start_coroutine(coroutine) - } - fn update_shoot(node: &mut RefMut, _dt: f32) { - node.fish.speed.x *= 0.9; - } - - fn sword_shoot_coroutine(node: &mut RefMut) -> Coroutine { - let handle = node.handle(); - let coroutine = async move { - { - let node = &mut *scene::get_node(handle).unwrap(); - node.fish.sword_sprite.set_animation(1); - - let mut net_syncronizer = - scene::find_node_by_type::().unwrap(); - net_syncronizer.shoot(); - } - - { - let node = &mut *scene::get_node(handle).unwrap(); - let others = scene::find_nodes_by_type::(); - let sword_hit_box = if node.fish.facing { - Rect::new(node.pos().x + 35., node.pos().y - 5., 40., 60.) - } else { - Rect::new(node.pos().x - 50., node.pos().y - 5., 40., 60.) - }; - - for player in others { - if Rect::new(player.pos().x, player.pos().y, 20., 64.).overlaps(&sword_hit_box) - { - let mut net = scene::find_node_by_type::().unwrap(); - net.kill(&player.id, !node.fish.facing); - } - } - } - - for i in 0u32..3 { - { - let node = &mut *scene::get_node(handle).unwrap(); - node.fish.sword_sprite.set_frame(i); - } - - wait_seconds(0.08).await; - } - - { - let mut node = scene::get_node(handle).unwrap(); - node.fish.sword_sprite.set_animation(0); - } - - let node = &mut *scene::get_node(handle).unwrap(); - node.state_machine.set_state(Self::ST_NORMAL); - }; - - start_coroutine(coroutine) - } - - fn update_sword_shoot(node: &mut RefMut, _dt: f32) { - node.fish.speed.x *= 0.9; + todo!("actual update"); } fn update_aftermatch(node: &mut RefMut, _dt: f32) { @@ -599,10 +416,8 @@ impl Player { } if fish.input.fire { - match fish.weapon { - Some(Weapon::Gun { .. }) => node.state_machine.set_state(Self::ST_SHOOT), - Some(Weapon::Sword) => node.state_machine.set_state(Self::ST_SWORD_SHOOT), - None => {} + if fish.weapon.is_some() { + node.state_machine.set_state(Self::ST_SHOOT); } } } @@ -611,17 +426,19 @@ impl Player { if self.is_dead() { return; } - if let Some(Weapon::Gun { bullets }) = self.fish.weapon { - let full_color = Color::new(0.8, 0.9, 1.0, 1.0); - let empty_color = Color::new(0.8, 0.9, 1.0, 0.8); - for i in 0..3 { - let x = self.fish.pos.x + 15.0 * i as f32; - - if i >= bullets { - draw_circle_lines(x, self.fish.pos.y - 4.0, 4.0, 2., empty_color); - } else { - draw_circle(x, self.fish.pos.y - 4.0, 4.0, full_color); - }; + if let Some(weapon) = self.fish.weapon { + if let Some((remaining, max_uses)) = weapon.uses_remaining() { + let full_color = Color::new(0.8, 0.9, 1.0, 1.0); + let empty_color = Color::new(0.8, 0.9, 1.0, 0.8); + for i in 0..max_uses { + let x = self.fish.pos.x + 15.0 * i as f32; + + if i >= remaining { + draw_circle_lines(x, self.fish.pos.y - 4.0, 4.0, 2., empty_color); + } else { + draw_circle(x, self.fish.pos.y - 4.0, 4.0, full_color); + }; + } } } } diff --git a/src/plugin.rs b/src/plugin.rs new file mode 100644 index 0000000..8d2a58e --- /dev/null +++ b/src/plugin.rs @@ -0,0 +1,32 @@ +use std::collections::HashMap; + +use macroquad::{ + experimental::{ + animation::AnimatedSprite + } +}; +use wasm_plugin_host::WasmPlugin; + +use crate::item::ItemType; + + +pub struct ModId(u64); + +pub struct ModDescription { + mod_id: ModId, + display_name: String, + items: Vec, +} + +pub struct ItemDescription { + item_type: ItemType, + display_name: String, + texture_data: Vec, + sprite: AnimatedSprite, + fx_sprite: AnimatedSprite, +} + +struct GameApiContext { +} + +pub struct ModRegistry(HashMap); diff --git a/src/remote_player.rs b/src/remote_player.rs index ef1533a..bba587e 100644 --- a/src/remote_player.rs +++ b/src/remote_player.rs @@ -8,7 +8,7 @@ use macroquad::{ }; use crate::{ - pickup::ItemType, + item::ItemType, player::{Fish, Weapon}, Resources, }; @@ -48,11 +48,7 @@ impl RemotePlayer { } pub fn weapon(&self) -> Option { - match self.fish.weapon { - None => None, - Some(Weapon::Gun { .. }) => Some(ItemType::Gun), - Some(Weapon::Sword) => Some(ItemType::Sword), - } + self.fish.weapon.map(|weapon| weapon.item_type) } pub fn set_pos(&mut self, pos: Vec2) { @@ -74,41 +70,7 @@ impl RemotePlayer { } pub fn shoot(&mut self, handle: Handle) { - match self.fish.weapon { - Some(Weapon::Gun { .. }) => { - let mut bullets = scene::find_node_by_type::().unwrap(); - bullets.spawn_bullet(self.pos(), self.fish.facing()); - } - Some(Weapon::Sword) => { - let swing = async move { - { - if let Some(mut node) = scene::get_node(handle) { - node.fish.sword_sprite.set_animation(1); - } - } - - for i in 0u32..3 { - { - if let Some(mut node) = scene::get_node(handle) { - node.fish.sword_sprite.set_frame(i); - } - } - - wait_seconds(0.08).await; - } - - { - if let Some(mut node) = scene::get_node(handle) { - node.fish.sword_sprite.set_animation(0); - } - } - }; - start_coroutine(swing); - } - None => { - println!("well"); - } - } + todo!("remote weapon handling"); } } impl scene::Node for RemotePlayer { From 4b6da4cd9ef3c8c5c6e3efc8ec32423e6481cbea Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Sat, 10 Apr 2021 14:57:45 -0700 Subject: [PATCH 02/14] Load plugins. Broken due to missing serializer support in wasm-plugin-host --- src/item.rs | 26 +++++++++++++++++++++--- src/main.rs | 7 +++++++ src/plugin.rs | 56 ++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 72 insertions(+), 17 deletions(-) diff --git a/src/item.rs b/src/item.rs index c5f7400..6d3b16e 100644 --- a/src/item.rs +++ b/src/item.rs @@ -4,10 +4,10 @@ use macroquad::{ prelude::*, experimental::{ animation::AnimatedSprite - } + }, }; -use crate::plugin::ModId; +use crate::plugin::{PluginId, ItemDescription}; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct ItemType(u64); @@ -27,10 +27,30 @@ struct ItemImplementation { pub texture: Texture2D, pub sprite: AnimatedSprite, pub fx_sprite: AnimatedSprite, - implementing_mod: ModId, + implementing_plugin: PluginId, +} + +impl ItemImplementation { + pub fn from_description(description: &ItemDescription, plugin: PluginId) -> Self { + let texture = load_texture_from_image(&description.image); + Self { + display_name: description.display_name, + texture, + sprite: description.sprite, + fx_sprite: description.fx_sprite, + implementing_plugin: plugin + } + } } + +#[derive(Default)] pub struct ItemImplementationRegistry(HashMap); + impl ItemImplementationRegistry { + pub fn add(&mut self, description: &ItemDescription, implementing_plugin: PluginId) { + self.0.insert(description.item_type, ItemImplementation::from_description(description, implementing_plugin)); + } + pub fn get_implementation(&self, item_type: ItemType) -> Option<&ItemImplementation> { self.0.get(&item_type) } diff --git a/src/main.rs b/src/main.rs index 10cd4ea..4480dd5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,6 +40,8 @@ use net_syncronizer::NetSyncronizer; use pickup::Pickup; use player::Player; use remote_player::RemotePlayer; +use plugin::PluginRegistry; +use item::ItemImplementationRegistry; pub mod consts { pub const GRAVITY: f32 = 900.0; @@ -237,6 +239,11 @@ async fn network_game(game_type: GameType, network_id: String) { storage::store(resources); + let mut item_registry = ItemImplementationRegistry::default(); + let plugin_registry = PluginRegistry::load("plugins/", &mut item_registry); + storage::store(item_registry); + storage::store(plugin_registry); + scene::add_node(LevelBackground::new()); for object in &storage::get::().unwrap().tiled_map.layers["decorations"].objects { diff --git a/src/plugin.rs b/src/plugin.rs index 8d2a58e..caa322b 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,32 +1,60 @@ -use std::collections::HashMap; +use std::{ + path::Path, + collections::HashMap, +}; use macroquad::{ - experimental::{ - animation::AnimatedSprite - } + texture::Image, + experimental::animation::AnimatedSprite, }; + use wasm_plugin_host::WasmPlugin; -use crate::item::ItemType; +use crate::item::{ItemType, ItemImplementationRegistry}; -pub struct ModId(u64); +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct PluginId(u64); -pub struct ModDescription { - mod_id: ModId, +#[derive(Clone)] +pub struct PluginDescription { + plugin_id: PluginId, display_name: String, items: Vec, } +#[derive(Clone)] pub struct ItemDescription { - item_type: ItemType, - display_name: String, - texture_data: Vec, - sprite: AnimatedSprite, - fx_sprite: AnimatedSprite, + pub item_type: ItemType, + pub display_name: String, + pub image: Image, + pub sprite: AnimatedSprite, + pub fx_sprite: AnimatedSprite, } struct GameApiContext { } -pub struct ModRegistry(HashMap); +pub struct PluginRegistry(HashMap); + +impl PluginRegistry { + pub fn load(path: impl AsRef, item_registry: &mut ItemImplementationRegistry) -> Self { + let mut plugins = HashMap::new(); + for entry in path.as_ref().read_dir().expect("Unable to read plugins directory") { + if let Ok(entry) = entry { + if entry.path().to_str().unwrap().contains(".wasm") { + let mut plugin = wasm_plugin_host::WasmPlugin::load(entry.path()).expect(&format!("Failed to load plugin {:?}", entry.path())); + let description: PluginDescription = plugin.call_function("plugin_description").expect(&format!("Failed to call 'plugin_description' on plugin {:?}", entry.path())); + + for item in &description.items { + item_registry.add(item, description.plugin_id); + } + + plugins.insert(description.plugin_id, (plugin, GameApiContext {})); + } + } + } + + PluginRegistry(plugins) + } +} From d09395f19da148efbb312cfcd716cf64259c8d51 Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Thu, 22 Apr 2021 11:18:03 -0700 Subject: [PATCH 03/14] plugin api crate and basic core plugin stub --- .gitignore | 2 +- Cargo.lock | 8 +++ Cargo.toml | 1 + core_plugin/Cargo.lock | 87 ++++++++++++++++++++++++ core_plugin/Cargo.toml | 12 ++++ core_plugin/src/lib.rs | 10 +++ plugin_api/Cargo.lock | 23 +++++++ plugin_api/Cargo.toml | 8 +++ plugin_api/src/lib.rs | 62 +++++++++++++++++ src/gui.rs | 4 +- src/main.rs | 2 +- src/nodes.rs | 2 +- src/nodes/global_events.rs | 3 +- src/nodes/item.rs | 35 +++------- src/nodes/nakama/nakama_realtime_game.rs | 3 +- src/nodes/pickup.rs | 3 +- src/nodes/player.rs | 9 +-- src/nodes/remote_player.rs | 4 +- src/plugin.rs | 45 ++++++------ 19 files changed, 265 insertions(+), 58 deletions(-) create mode 100644 core_plugin/Cargo.lock create mode 100644 core_plugin/Cargo.toml create mode 100644 core_plugin/src/lib.rs create mode 100644 plugin_api/Cargo.lock create mode 100644 plugin_api/Cargo.toml create mode 100644 plugin_api/src/lib.rs diff --git a/.gitignore b/.gitignore index ea8c4bf..b60de5b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/target +**/target diff --git a/Cargo.lock b/Cargo.lock index 99740cd..9f9fa11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -523,6 +523,7 @@ dependencies = [ "macroquad-tiled", "nakama-rs", "nanoserde", + "plugin_api", "wasm_plugin_host", ] @@ -1356,6 +1357,13 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +[[package]] +name = "plugin_api" +version = "0.1.0" +dependencies = [ + "nanoserde", +] + [[package]] name = "png" version = "0.16.8" diff --git a/Cargo.toml b/Cargo.toml index f9c2407..7cf68e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,5 @@ bitfield = "0.13" nakama-rs = "0.1" base64 = "0.13" wasm_plugin_host = { version = "0.1.4", default-features = false, features = ["serialize_nanoserde_json"] } +plugin_api = { path = "./plugin_api" } diff --git a/core_plugin/Cargo.lock b/core_plugin/Cargo.lock new file mode 100644 index 0000000..8b2e588 --- /dev/null +++ b/core_plugin/Cargo.lock @@ -0,0 +1,87 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "core_plugin" +version = "0.1.0" +dependencies = [ + "plugin_api", + "wasm_plugin_guest", +] + +[[package]] +name = "nanoserde" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae5894adc81d64b16f75ce8a67c994a6aa4b6e1c99f2920d22e0e575755d7e9" +dependencies = [ + "nanoserde-derive", +] + +[[package]] +name = "nanoserde-derive" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c29c140bb2a43bdc554ca5a34ecfe678bb6e6c6a3dc8d6856852ba2c74f20f" + +[[package]] +name = "plugin_api" +version = "0.1.0" +dependencies = [ + "nanoserde", +] + +[[package]] +name = "proc-macro2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "wasm_plugin_guest" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb984f9537bc14cfce0113e376141bc0e9f707c2182feca30a7bb60eba537e94" +dependencies = [ + "nanoserde", + "wasm_plugin_guest_derive", +] + +[[package]] +name = "wasm_plugin_guest_derive" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9731db24d89817f194b45cc3c5e7474a2f98b6842d1a6f7687bf246f565ebc09" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/core_plugin/Cargo.toml b/core_plugin/Cargo.toml new file mode 100644 index 0000000..fc265b2 --- /dev/null +++ b/core_plugin/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "core_plugin" +version = "0.1.0" +authors = ["Alec Deason "] +edition = "2018" + +[lib] + crate-type = ["cdylib"] + +[dependencies] +plugin_api = { path = "../plugin_api" } +wasm_plugin_guest = { version = "0.1.3", default-features = false, features = ["serialize_nanoserde_json"] } diff --git a/core_plugin/src/lib.rs b/core_plugin/src/lib.rs new file mode 100644 index 0000000..d330667 --- /dev/null +++ b/core_plugin/src/lib.rs @@ -0,0 +1,10 @@ +use plugin_api::{ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId}; + +#[wasm_plugin_guest::export_function] +fn plugin_description() -> PluginDescription { + PluginDescription { + plugin_id: PluginId(11229058760733382699), + display_name: "basic weapons".to_string(), + items: vec![], + } +} diff --git a/plugin_api/Cargo.lock b/plugin_api/Cargo.lock new file mode 100644 index 0000000..635caca --- /dev/null +++ b/plugin_api/Cargo.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "nanoserde" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae5894adc81d64b16f75ce8a67c994a6aa4b6e1c99f2920d22e0e575755d7e9" +dependencies = [ + "nanoserde-derive", +] + +[[package]] +name = "nanoserde-derive" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c29c140bb2a43bdc554ca5a34ecfe678bb6e6c6a3dc8d6856852ba2c74f20f" + +[[package]] +name = "plugin_api" +version = "0.1.0" +dependencies = [ + "nanoserde", +] diff --git a/plugin_api/Cargo.toml b/plugin_api/Cargo.toml new file mode 100644 index 0000000..86d2203 --- /dev/null +++ b/plugin_api/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "plugin_api" +version = "0.1.0" +authors = ["Alec Deason "] +edition = "2018" + +[dependencies] +nanoserde = "0.1" diff --git a/plugin_api/src/lib.rs b/plugin_api/src/lib.rs new file mode 100644 index 0000000..5122a01 --- /dev/null +++ b/plugin_api/src/lib.rs @@ -0,0 +1,62 @@ +use nanoserde::{DeJson, SerJson}; + +#[derive(Copy, Clone, PartialEq, Eq, Hash, DeJson, SerJson)] +pub struct ItemType(u64); +impl From for u64 { + fn from(item_type: ItemType) -> u64 { + item_type.0 + } +} +impl From for ItemType { + fn from(item_type: u64) -> ItemType { + ItemType(item_type) + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, DeJson, SerJson)] +pub struct PluginId(pub u64); + +#[derive(Clone, DeJson, SerJson)] +pub struct PluginDescription { + pub plugin_id: PluginId, + pub display_name: String, + pub items: Vec, +} + +#[derive(Clone, DeJson, SerJson)] +pub struct ItemDescription { + pub item_type: ItemType, + pub display_name: String, + pub image: ImageDescription, + pub sprite: AnimatedSpriteDescription, + pub fx_sprite: AnimatedSpriteDescription, +} + +// TODO: All these *Description structs could be replaced by adding the +// DeJson and SerJson derives inside macroquad, maybe behind a feature gate. +#[derive(Clone, DeJson, SerJson)] +pub struct ImageDescription { + pub bytes: Vec, + pub width: u16, + pub height: u16, +} + +#[derive(Clone, DeJson, SerJson)] +pub struct AnimatedSpriteDescription { + pub tile_width: u32, + pub tile_height: u32, + pub animations: Vec, + pub playing: bool, +} + +#[derive(Clone, Debug, DeJson, SerJson)] +pub struct AnimationDescription { + pub name: String, + pub row: u32, + pub frames: u32, + pub fps: u32, +} + +pub struct GameApiContext { +} + diff --git a/src/gui.rs b/src/gui.rs index a20eaf7..94b4158 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -9,14 +9,14 @@ mod lobby; mod main_menu; mod style; mod waitscreen; -mod credits; +//mod credits; pub use authentication::authentication; pub use lobby::matchmaking_lobby; pub use main_menu::main_menu; pub use style::GuiResources; pub use waitscreen::waitscreen; -pub use credits::credits; +//pub use credits::credits; const WINDOW_WIDTH: f32 = 700.0; const WINDOW_HEIGHT: f32 = 300.0; diff --git a/src/main.rs b/src/main.rs index 9d89524..c899671 100644 --- a/src/main.rs +++ b/src/main.rs @@ -421,7 +421,7 @@ async fn main() { next_scene = gui::waitscreen(nakama, private).await; } Scene::Credits => { - next_scene = gui::credits().await; + //next_scene = gui::credits().await; } } } diff --git a/src/nodes.rs b/src/nodes.rs index fedd4e3..80d83a8 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -20,4 +20,4 @@ pub use nakama::{Nakama, NakamaRealtimeGame}; pub use pickup::Pickup; pub use player::Player; pub use remote_player::RemotePlayer; -pub use item::{ItemType, ItemImplementationRegistry}; +pub use item::{ItemImplementationRegistry}; diff --git a/src/nodes/global_events.rs b/src/nodes/global_events.rs index 51e68b2..c958e6e 100644 --- a/src/nodes/global_events.rs +++ b/src/nodes/global_events.rs @@ -7,9 +7,10 @@ use macroquad::{ }; use crate::{ - nodes::{item::{ItemType, ItemImplementationRegistry}, NakamaRealtimeGame, Pickup, Player, RemotePlayer}, + nodes::{item::{ItemImplementationRegistry}, NakamaRealtimeGame, Pickup, Player, RemotePlayer}, Resources, }; +use plugin_api::ItemType; pub struct GlobalEvents { last_spawn_time: f64, diff --git a/src/nodes/item.rs b/src/nodes/item.rs index a09a348..fca990a 100644 --- a/src/nodes/item.rs +++ b/src/nodes/item.rs @@ -2,27 +2,14 @@ use std::collections::HashMap; use macroquad::{ prelude::*, - experimental::{ - animation::AnimatedSprite - }, + experimental::animation::AnimatedSprite, }; -use crate::plugin::{PluginId, ItemDescription}; +use plugin_api::{ItemType, PluginId, ItemDescription}; +use crate::plugin::{image_from_desc, animated_sprite_from_desc}; -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct ItemType(u64); -impl From for u64 { - fn from(item_type: ItemType) -> u64 { - item_type.0 - } -} -impl From for ItemType { - fn from(item_type: u64) -> ItemType { - ItemType(item_type) - } -} -struct ItemImplementation { +pub(crate) struct ItemImplementation { display_name: String, pub texture: Texture2D, pub sprite: AnimatedSprite, @@ -31,13 +18,13 @@ struct ItemImplementation { } impl ItemImplementation { - pub fn from_description(description: &ItemDescription, plugin: PluginId) -> Self { - let texture = Texture2D::from_image(&description.image); + pub(crate) fn from_description(description: ItemDescription, plugin: PluginId) -> Self { + let texture = Texture2D::from_image(&image_from_desc(description.image)); Self { display_name: description.display_name, texture, - sprite: description.sprite, - fx_sprite: description.fx_sprite, + sprite: animated_sprite_from_desc(description.sprite), + fx_sprite: animated_sprite_from_desc(description.fx_sprite), implementing_plugin: plugin } } @@ -47,15 +34,15 @@ impl ItemImplementation { pub struct ItemImplementationRegistry(HashMap); impl ItemImplementationRegistry { - pub fn add(&mut self, description: &ItemDescription, implementing_plugin: PluginId) { + pub(crate) fn add(&mut self, description: ItemDescription, implementing_plugin: PluginId) { self.0.insert(description.item_type, ItemImplementation::from_description(description, implementing_plugin)); } - pub fn get_implementation(&self, item_type: ItemType) -> Option<&ItemImplementation> { + pub(crate) fn get_implementation(&self, item_type: ItemType) -> Option<&ItemImplementation> { self.0.get(&item_type) } - pub fn item_types(&self) -> Vec { + pub(crate) fn item_types(&self) -> Vec { self.0.keys().copied().collect() } } diff --git a/src/nodes/nakama/nakama_realtime_game.rs b/src/nodes/nakama/nakama_realtime_game.rs index 4175289..cb75420 100644 --- a/src/nodes/nakama/nakama_realtime_game.rs +++ b/src/nodes/nakama/nakama_realtime_game.rs @@ -14,9 +14,10 @@ use nakama_rs::api_client::Event; use crate::{ consts, - nodes::{item::ItemType, Nakama, Pickup, Player, RemotePlayer}, + nodes::{Nakama, Pickup, Player, RemotePlayer}, GameType, Resources, }; +use plugin_api::ItemType; struct NetworkCache { sent_position: [u8; 4], diff --git a/src/nodes/pickup.rs b/src/nodes/pickup.rs index 77917de..e64bece 100644 --- a/src/nodes/pickup.rs +++ b/src/nodes/pickup.rs @@ -7,7 +7,8 @@ use macroquad::{ prelude::*, }; -use crate::{Resources, nodes::item::{ItemType, ItemImplementationRegistry}}; +use crate::{Resources, nodes::item::{ItemImplementationRegistry}}; +use plugin_api::ItemType; pub struct Pickup { pub pos: Vec2, diff --git a/src/nodes/player.rs b/src/nodes/player.rs index 7858c16..195342a 100644 --- a/src/nodes/player.rs +++ b/src/nodes/player.rs @@ -15,9 +15,10 @@ use macroquad_platformer::Actor; use crate::{ consts, - nodes::{item::{ItemType, ItemInstanceId, ItemIdSource, ItemImplementationRegistry}, Nakama, NakamaRealtimeGame, Pickup}, + nodes::{item::{ItemInstanceId, ItemIdSource, ItemImplementationRegistry}, Nakama, NakamaRealtimeGame, Pickup}, Resources, }; +use plugin_api::ItemType; #[derive(Default, Debug, Clone)] pub struct Input { @@ -127,7 +128,7 @@ impl Fish { } pub fn pick_weapon(&mut self, item_type: ItemType) { - let item_id_source = storage::get_mut::(); + let mut item_id_source = storage::get_mut::(); let item_registry = storage::get::(); let item_impl = item_registry.get_implementation(item_type).expect("Invalid ItemType"); self.weapon = Some(Weapon { @@ -310,7 +311,7 @@ impl Player { } pub fn weapon(&self) -> Option { - self.fish.weapon.map(|weapon| weapon.item_type) + self.fish.weapon.as_ref().map(|weapon| weapon.item_type) } fn death_coroutine(node: &mut RefMut) -> Coroutine { @@ -458,7 +459,7 @@ impl Player { if self.is_dead() { return; } - if let Some(weapon) = self.fish.weapon { + if let Some(weapon) = &self.fish.weapon { if let Some((remaining, max_uses)) = weapon.uses_remaining() { let full_color = Color::new(0.8, 0.9, 1.0, 1.0); let empty_color = Color::new(0.8, 0.9, 1.0, 0.8); diff --git a/src/nodes/remote_player.rs b/src/nodes/remote_player.rs index 59071cb..9fc8a76 100644 --- a/src/nodes/remote_player.rs +++ b/src/nodes/remote_player.rs @@ -9,11 +9,11 @@ use macroquad::{ use crate::{ nodes::{ - item::ItemType, player::{Fish, Weapon}, }, Resources, }; +use plugin_api::ItemType; pub struct RemotePlayer { pub username: String, @@ -50,7 +50,7 @@ impl RemotePlayer { } pub fn weapon(&self) -> Option { - self.fish.weapon.map(|weapon| weapon.item_type) + self.fish.weapon.as_ref().map(|weapon| weapon.item_type) } pub fn set_pos(&mut self, pos: Vec2) { diff --git a/src/plugin.rs b/src/plugin.rs index 1341daa..ec00bf1 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -5,35 +5,40 @@ use std::{ use macroquad::{ texture::Image, - experimental::animation::AnimatedSprite, + experimental::animation::{AnimatedSprite, Animation}, }; use nanoserde::DeJson; use wasm_plugin_host::WasmPlugin; -use crate::nodes::{ItemType, ItemImplementationRegistry}; +use plugin_api::{ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId, GameApiContext}; +use crate::nodes::{ItemImplementationRegistry}; -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct PluginId(u64); - -#[derive(Clone)] -pub struct PluginDescription { - plugin_id: PluginId, - display_name: String, - items: Vec, +pub fn image_from_desc(desc: ImageDescription) -> Image { + Image { + bytes: desc.bytes, + width: desc.width, + height: desc.height, + } } - -#[derive(Clone, DeJson)] -pub struct ItemDescription { - pub item_type: ItemType, - pub display_name: String, - pub image: Image, - pub sprite: AnimatedSprite, - pub fx_sprite: AnimatedSprite, +pub fn animation_from_desc(desc: AnimationDescription) -> Animation { + Animation { + name: desc.name, + row: desc.row, + frames: desc.frames, + fps: desc.fps, + } } -struct GameApiContext { +pub fn animated_sprite_from_desc(desc: AnimatedSpriteDescription) -> AnimatedSprite { + let animations: Vec = desc.animations.into_iter().map(|a| animation_from_desc(a)).collect(); + AnimatedSprite::new( + desc.tile_width, + desc.tile_height, + &animations, + desc.playing, + ) } pub struct PluginRegistry(HashMap); @@ -47,7 +52,7 @@ impl PluginRegistry { let mut plugin = wasm_plugin_host::WasmPluginBuilder::from_file(entry.path()).expect(&format!("Failed to load plugin {:?}", entry.path())).finish().unwrap(); let description: PluginDescription = plugin.call_function("plugin_description").expect(&format!("Failed to call 'plugin_description' on plugin {:?}", entry.path())); - for item in &description.items { + for item in description.items { item_registry.add(item, description.plugin_id); } From f8acfb69fdad5a3c111e5703b2c9162e9c32a906 Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Thu, 22 Apr 2021 19:15:54 -0700 Subject: [PATCH 04/14] Sprite loading and item pickups as well as state within the plugin --- Cargo.lock | 40 ++++-- Cargo.toml | 2 +- core_plugin/Cargo.lock | 258 ++++++++++++++++++++++++++++++++++--- core_plugin/Cargo.toml | 5 +- core_plugin/README.md | 7 + core_plugin/src/lib.rs | 140 +++++++++++++++++++- plugin_api/Cargo.lock | 60 +++++++-- plugin_api/Cargo.toml | 2 +- plugin_api/src/lib.rs | 69 +++++++--- src/gui.rs | 4 +- src/main.rs | 5 +- src/nodes.rs | 2 +- src/nodes/global_events.rs | 9 +- src/nodes/item.rs | 48 ++++++- src/nodes/pickup.rs | 4 +- src/nodes/player.rs | 74 ++++++++--- src/plugin.rs | 4 + 17 files changed, 643 insertions(+), 90 deletions(-) create mode 100644 core_plugin/README.md diff --git a/Cargo.lock b/Cargo.lock index 9f9fa11..5de67c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,6 +739,12 @@ dependencies = [ "libc", ] +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + [[package]] name = "jni" version = "0.18.0" @@ -1361,7 +1367,7 @@ checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" name = "plugin_api" version = "0.1.0" dependencies = [ - "nanoserde", + "serde", ] [[package]] @@ -1594,9 +1600,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759" dependencies = [ "regex-syntax", ] @@ -1679,6 +1685,12 @@ dependencies = [ "webpki", ] +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + [[package]] name = "same-file" version = "1.0.6" @@ -1799,6 +1811,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha1" version = "0.6.0" @@ -1855,9 +1878,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" +checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883" dependencies = [ "proc-macro2", "quote", @@ -2120,11 +2143,12 @@ checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "wasm_plugin_host" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dbd343a1ad75bc9d11c94253fc56107744004a5f22d3e895f02fa41595032b5" +checksum = "fb4ca6c29b04b3b450b7800b878c4f80b3779a876974eba2d4fe7d4a734ef753" dependencies = [ - "nanoserde", + "serde", + "serde_json", "wasmer", ] diff --git a/Cargo.toml b/Cargo.toml index 7cf68e0..9fe6862 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,6 @@ macroquad-particles = {version = "0.1", features = ["nanoserde"] } bitfield = "0.13" nakama-rs = "0.1" base64 = "0.13" -wasm_plugin_host = { version = "0.1.4", default-features = false, features = ["serialize_nanoserde_json"] } +wasm_plugin_host = { version = "0.1.5", default-features = false, features = ["serialize_json"] } plugin_api = { path = "./plugin_api" } diff --git a/core_plugin/Cargo.lock b/core_plugin/Cargo.lock index 8b2e588..2f41cd9 100644 --- a/core_plugin/Cargo.lock +++ b/core_plugin/Cargo.lock @@ -1,33 +1,189 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bytemuck" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "core_plugin" version = "0.1.0" dependencies = [ + "image", + "once_cell", "plugin_api", "wasm_plugin_guest", + "wee_alloc", +] + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "image" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "num-iter", + "num-rational", + "num-traits", + "png", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "libc" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", ] [[package]] -name = "nanoserde" -version = "0.1.25" +name = "num-rational" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae5894adc81d64b16f75ce8a67c994a6aa4b6e1c99f2920d22e0e575755d7e9" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" dependencies = [ - "nanoserde-derive", + "autocfg", + "num-integer", + "num-traits", ] [[package]] -name = "nanoserde-derive" -version = "0.1.16" +name = "num-traits" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c29c140bb2a43bdc554ca5a34ecfe678bb6e6c6a3dc8d6856852ba2c74f20f" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" [[package]] name = "plugin_api" version = "0.1.0" dependencies = [ - "nanoserde", + "serde", +] + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "miniz_oxide", ] [[package]] @@ -48,11 +204,48 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "syn" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" +checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883" dependencies = [ "proc-macro2", "quote", @@ -67,21 +260,56 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "wasm_plugin_guest" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb984f9537bc14cfce0113e376141bc0e9f707c2182feca30a7bb60eba537e94" +checksum = "fcdd4d39f1d5a1fe6e7f9af1c26cda98ce5caf29c76de38c4348eea7d982ff90" dependencies = [ - "nanoserde", + "serde", + "serde_json", "wasm_plugin_guest_derive", ] [[package]] name = "wasm_plugin_guest_derive" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9731db24d89817f194b45cc3c5e7474a2f98b6842d1a6f7687bf246f565ebc09" +checksum = "798077c1e20331f66c2d64d4ca503cc18c485d776bbd370ad43ab4641a45ffd0" dependencies = [ "proc-macro2", "quote", "syn", ] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/core_plugin/Cargo.toml b/core_plugin/Cargo.toml index fc265b2..a7cf9bf 100644 --- a/core_plugin/Cargo.toml +++ b/core_plugin/Cargo.toml @@ -9,4 +9,7 @@ edition = "2018" [dependencies] plugin_api = { path = "../plugin_api" } -wasm_plugin_guest = { version = "0.1.3", default-features = false, features = ["serialize_nanoserde_json"] } +wasm_plugin_guest = { version = "0.1.4", default-features = false, features = ["serialize_json"] } +image = { version = "0.23", default-features = false, features = ["png"] } +wee_alloc = "0.4.5" +once_cell = "1.7.2" diff --git a/core_plugin/README.md b/core_plugin/README.md new file mode 100644 index 0000000..0c6da65 --- /dev/null +++ b/core_plugin/README.md @@ -0,0 +1,7 @@ +This plugin provides the default weapons used by Fish Game. + +To build: +``` +cargo build --target wasm32-unknown-unknown --release +cp target/wasm32-unknown-unknown/debug/core_plugin.wasm ../plugins/ +``` diff --git a/core_plugin/src/lib.rs b/core_plugin/src/lib.rs index d330667..2f7ba7e 100644 --- a/core_plugin/src/lib.rs +++ b/core_plugin/src/lib.rs @@ -1,10 +1,144 @@ -use plugin_api::{ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId}; +use std::{ + io::Cursor, + sync::Mutex, collections::HashMap, +}; + +use plugin_api::{ItemType, ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId, ItemDescription, Rect, ItemInstanceId}; + +use once_cell::sync::Lazy; + +static ITEMS: Lazy>> = Lazy::new(Mutex::default); + +const GUN: ItemType = ItemType::new(9868317461196439167); +const SWORD: ItemType = ItemType::new(11238048715746880612); + +enum ItemState { + Gun(u32), + Sword, +} #[wasm_plugin_guest::export_function] fn plugin_description() -> PluginDescription { + let sword_image = image::load(Cursor::new(include_bytes!("../../assets/Whale/Sword(65x93).png")), image::ImageFormat::Png).unwrap().to_rgba8(); + let sword_width = sword_image.width() as u16; + let sword_height = sword_image.height() as u16; + let sword_bytes = sword_image.into_vec(); + + let gun_image = image::load(Cursor::new(include_bytes!("../../assets/Whale/Gun(92x32).png")), image::ImageFormat::Png).unwrap().to_rgba8(); + let gun_width = gun_image.width() as u16; + let gun_height = gun_image.height() as u16; + let gun_bytes = gun_image.into_vec(); + PluginDescription { - plugin_id: PluginId(11229058760733382699), + plugin_id: PluginId::new(11229058760733382699), display_name: "basic weapons".to_string(), - items: vec![], + items: vec![ + ItemDescription { + item_type: SWORD, + display_name: "Sword".to_string(), + image: ImageDescription { + bytes: sword_bytes, + width: sword_width as u16, + height: sword_height as u16, + }, + mount_pos_right: [10.0, -35.0], + mount_pos_left: [-50.0, -35.0], + pickup_src: Rect { + x: 200.0, + y: 98.0, + w: 55.0, + h: 83.0, + }, + pickup_dst: [32.0, 32.0], + sprite: AnimatedSpriteDescription { + tile_width: 65, + tile_height: 93, + animations: vec![ + AnimationDescription { + name: "idle".to_string(), + row: 0, + frames: 1, + fps: 1, + }, + ], + playing: true, + }, + fx_sprite: AnimatedSpriteDescription { + tile_width: 76, + tile_height: 66, + animations: vec![ + ], + playing: true, + }, + }, + ItemDescription { + item_type: GUN, + display_name: "Gun".to_string(), + image: ImageDescription { + bytes: gun_bytes, + width: gun_width as u16, + height: gun_height as u16, + }, + mount_pos_right: [0.0, 16.0], + mount_pos_left: [-60.0, 16.0], + pickup_src: Rect { + x: 0.0, + y: 0.0, + w: 64.0, + h: 32.0, + }, + pickup_dst: [32.0, 16.0], + sprite: AnimatedSpriteDescription { + tile_width: 92, + tile_height: 32, + animations: vec![ + AnimationDescription { + name: "idle".to_string(), + row: 0, + frames: 1, + fps: 1, + }, + ], + playing: true, + }, + fx_sprite: AnimatedSpriteDescription { + tile_width: 76, + tile_height: 66, + animations: vec![ + ], + playing: true, + }, + } + ], + } +} + +#[wasm_plugin_guest::export_function] +fn new_instance(item_type: ItemType, item_id: ItemInstanceId) { + let state = match item_type { + GUN => ItemState::Gun(3), + SWORD => ItemState::Sword, + _ => panic!() + }; + + ITEMS.lock().unwrap().insert(item_id, state); +} + +#[wasm_plugin_guest::export_function] +fn destroy_instance(item_id: ItemInstanceId) { + ITEMS.lock().unwrap().remove(&item_id); +} + +#[wasm_plugin_guest::export_function] +fn uses_remaining(item_id: ItemInstanceId) -> Option<(u32, u32)> { + if let Some(ItemState::Gun(ammo)) = ITEMS.lock().unwrap().get(&item_id) { + Some((*ammo, 3)) + } else { + None } } + +#[wasm_plugin_guest::export_function] +fn update_shoot(item_id: ItemInstanceId) -> bool { + true +} diff --git a/plugin_api/Cargo.lock b/plugin_api/Cargo.lock index 635caca..2cc8003 100644 --- a/plugin_api/Cargo.lock +++ b/plugin_api/Cargo.lock @@ -1,23 +1,63 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "nanoserde" -version = "0.1.25" +name = "plugin_api" +version = "0.1.0" +dependencies = [ + "serde", +] + +[[package]] +name = "proc-macro2" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae5894adc81d64b16f75ce8a67c994a6aa4b6e1c99f2920d22e0e575755d7e9" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ - "nanoserde-derive", + "unicode-xid", ] [[package]] -name = "nanoserde-derive" -version = "0.1.16" +name = "quote" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c29c140bb2a43bdc554ca5a34ecfe678bb6e6c6a3dc8d6856852ba2c74f20f" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] [[package]] -name = "plugin_api" -version = "0.1.0" +name = "serde" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883" dependencies = [ - "nanoserde", + "proc-macro2", + "quote", + "unicode-xid", ] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" diff --git a/plugin_api/Cargo.toml b/plugin_api/Cargo.toml index 86d2203..1d05182 100644 --- a/plugin_api/Cargo.toml +++ b/plugin_api/Cargo.toml @@ -5,4 +5,4 @@ authors = ["Alec Deason "] edition = "2018" [dependencies] -nanoserde = "0.1" +serde = { version = "1", features = ["derive"] } diff --git a/plugin_api/src/lib.rs b/plugin_api/src/lib.rs index 5122a01..c1ec7ad 100644 --- a/plugin_api/src/lib.rs +++ b/plugin_api/src/lib.rs @@ -1,47 +1,86 @@ -use nanoserde::{DeJson, SerJson}; +use serde::{Serialize, Deserialize}; + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct ItemType { + id: u64 +} + +impl ItemType { + pub const fn new(id: u64) -> Self { + Self { id } + } +} -#[derive(Copy, Clone, PartialEq, Eq, Hash, DeJson, SerJson)] -pub struct ItemType(u64); impl From for u64 { fn from(item_type: ItemType) -> u64 { - item_type.0 + item_type.id } } + impl From for ItemType { - fn from(item_type: u64) -> ItemType { - ItemType(item_type) + fn from(item_type: u64) -> ItemType{ + ItemType { id: item_type } } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, DeJson, SerJson)] -pub struct PluginId(pub u64); +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct ItemInstanceId { + pub id: u64 +} -#[derive(Clone, DeJson, SerJson)] +impl ItemInstanceId { + pub const fn new(id: u64) -> Self { + Self { id } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct PluginId { + id: u64 +} + +impl PluginId { + pub const fn new(id: u64) -> Self { + Self { id } + } +} + +#[derive(Clone, Serialize, Deserialize)] pub struct PluginDescription { pub plugin_id: PluginId, pub display_name: String, pub items: Vec, } -#[derive(Clone, DeJson, SerJson)] +#[derive(Clone, Serialize, Deserialize)] pub struct ItemDescription { pub item_type: ItemType, pub display_name: String, pub image: ImageDescription, + pub mount_pos_right: [f32; 2], + pub mount_pos_left: [f32; 2], + pub pickup_src: Rect, + pub pickup_dst: [f32; 2], pub sprite: AnimatedSpriteDescription, pub fx_sprite: AnimatedSpriteDescription, } -// TODO: All these *Description structs could be replaced by adding the -// DeJson and SerJson derives inside macroquad, maybe behind a feature gate. -#[derive(Clone, DeJson, SerJson)] +#[derive(Clone, Serialize, Deserialize)] +pub struct Rect { + pub x: f32, + pub y: f32, + pub w: f32, + pub h: f32 +} + +#[derive(Clone, Serialize, Deserialize)] pub struct ImageDescription { pub bytes: Vec, pub width: u16, pub height: u16, } -#[derive(Clone, DeJson, SerJson)] +#[derive(Clone, Serialize, Deserialize)] pub struct AnimatedSpriteDescription { pub tile_width: u32, pub tile_height: u32, @@ -49,7 +88,7 @@ pub struct AnimatedSpriteDescription { pub playing: bool, } -#[derive(Clone, Debug, DeJson, SerJson)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct AnimationDescription { pub name: String, pub row: u32, diff --git a/src/gui.rs b/src/gui.rs index 94b4158..a20eaf7 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -9,14 +9,14 @@ mod lobby; mod main_menu; mod style; mod waitscreen; -//mod credits; +mod credits; pub use authentication::authentication; pub use lobby::matchmaking_lobby; pub use main_menu::main_menu; pub use style::GuiResources; pub use waitscreen::waitscreen; -//pub use credits::credits; +pub use credits::credits; const WINDOW_WIDTH: f32 = 700.0; const WINDOW_HEIGHT: f32 = 300.0; diff --git a/src/main.rs b/src/main.rs index c899671..d82d3b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,7 @@ mod plugin; use gui::Scene; use plugin::PluginRegistry; -use nodes::ItemImplementationRegistry; +use nodes::{ItemIdSource, ItemImplementationRegistry}; pub mod consts { pub const GRAVITY: f32 = 900.0; @@ -274,6 +274,7 @@ async fn network_game(nakama: Handle, game_type: GameType, networ let plugin_registry = PluginRegistry::load("plugins/", &mut item_registry); storage::store(item_registry); storage::store(plugin_registry); + storage::store(ItemIdSource::default()); let level_background = scene::add_node(LevelBackground::new()); @@ -421,7 +422,7 @@ async fn main() { next_scene = gui::waitscreen(nakama, private).await; } Scene::Credits => { - //next_scene = gui::credits().await; + next_scene = gui::credits().await; } } } diff --git a/src/nodes.rs b/src/nodes.rs index 80d83a8..537ae27 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -20,4 +20,4 @@ pub use nakama::{Nakama, NakamaRealtimeGame}; pub use pickup::Pickup; pub use player::Player; pub use remote_player::RemotePlayer; -pub use item::{ItemImplementationRegistry}; +pub use item::{ItemIdSource, ItemImplementationRegistry}; diff --git a/src/nodes/global_events.rs b/src/nodes/global_events.rs index c958e6e..e8ecd86 100644 --- a/src/nodes/global_events.rs +++ b/src/nodes/global_events.rs @@ -46,6 +46,13 @@ impl scene::Node for GlobalEvents { if get_time() - node.last_spawn_time >= Self::SPAWN_INTERVAL as _ && node.spawned_items.len() < 3 { + let item_registry = storage::get::(); + let item_types = item_registry.item_types(); + if item_types.is_empty() { + // Probably no plugins are loaded. Nothing to do. + return; + } + let resources = storage::get::(); let tilewidth = resources.tiled_map.raw_tiled_map.tilewidth as f32; @@ -76,8 +83,6 @@ impl scene::Node for GlobalEvents { node.last_spawn_time = get_time(); - let item_registry = storage::get::(); - let item_types = item_registry.item_types(); let idx = rand::gen_range(0, item_types.len()); let item_type = item_types[idx]; let item_id = node.uid; diff --git a/src/nodes/item.rs b/src/nodes/item.rs index fca990a..c6c982f 100644 --- a/src/nodes/item.rs +++ b/src/nodes/item.rs @@ -2,16 +2,25 @@ use std::collections::HashMap; use macroquad::{ prelude::*, - experimental::animation::AnimatedSprite, + experimental::{ + collections::storage, + animation::AnimatedSprite, + }, }; +use wasm_plugin_host::WasmPlugin; -use plugin_api::{ItemType, PluginId, ItemDescription}; -use crate::plugin::{image_from_desc, animated_sprite_from_desc}; +use plugin_api::{ItemType, PluginId, ItemDescription, ItemInstanceId}; +use crate::plugin::{image_from_desc, animated_sprite_from_desc, PluginRegistry}; pub(crate) struct ItemImplementation { display_name: String, + item_type: ItemType, pub texture: Texture2D, + pub mount_pos_right: Vec2, + pub mount_pos_left: Vec2, + pub pickup_src: Rect, + pub pickup_dst: Vec2, pub sprite: AnimatedSprite, pub fx_sprite: AnimatedSprite, implementing_plugin: PluginId, @@ -20,14 +29,42 @@ pub(crate) struct ItemImplementation { impl ItemImplementation { pub(crate) fn from_description(description: ItemDescription, plugin: PluginId) -> Self { let texture = Texture2D::from_image(&image_from_desc(description.image)); + let pickup_src = description.pickup_src; Self { display_name: description.display_name, + item_type: description.item_type, texture, + mount_pos_right: vec2(description.mount_pos_right[0], description.mount_pos_right[1]), + mount_pos_left: vec2(description.mount_pos_left[0], description.mount_pos_left[1]), + pickup_src: Rect::new(pickup_src.x, pickup_src.y, pickup_src.w, pickup_src.h), + pickup_dst: vec2(description.pickup_dst[0], description.pickup_dst[1]), sprite: animated_sprite_from_desc(description.sprite), fx_sprite: animated_sprite_from_desc(description.fx_sprite), implementing_plugin: plugin } } + + fn with_plugin(&self, f: impl Fn(&mut WasmPlugin) -> R) -> R { + let mut plugin_registry = storage::get_mut::(); + let plugin = plugin_registry.get_plugin(self.implementing_plugin).unwrap(); + f(plugin) + } + + pub(crate) fn construct(&self, item_id: ItemInstanceId) { + self.with_plugin(|p| p.call_function_with_argument("new_instance", &(self.item_type, item_id)).unwrap()) + } + + pub(crate) fn destroy(&self, item_id: ItemInstanceId) { + self.with_plugin(|p| p.call_function_with_argument("destroy_instance", &item_id).unwrap()) + } + + pub(crate) fn uses_remaining(&self, item_id: ItemInstanceId) -> Option<(u32, u32)> { + self.with_plugin(|p| p.call_function_with_argument("uses_remaining", &item_id).unwrap()) + } + + pub(crate) fn update_shoot(&self, item_id: ItemInstanceId) -> bool { + self.with_plugin(|p| p.call_function_with_argument("update_shoot", &item_id).unwrap()) + } } #[derive(Default)] @@ -54,13 +91,10 @@ impl Default for ItemIdSource { } } -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct ItemInstanceId(u64); - impl ItemIdSource { pub fn next_id(&mut self) -> ItemInstanceId { let new_id = self.0; self.0 += 1; - ItemInstanceId(new_id) + ItemInstanceId::new(new_id) } } diff --git a/src/nodes/pickup.rs b/src/nodes/pickup.rs index e64bece..c3029d8 100644 --- a/src/nodes/pickup.rs +++ b/src/nodes/pickup.rs @@ -84,8 +84,8 @@ impl scene::Node for Pickup { node.pos.y + 8., WHITE, DrawTextureParams { - source: Some(Rect::new(0.0, 0.0, 64., 32.)), - dest_size: Some(vec2(32., 16.)), + source: Some(item_impl.pickup_src), + dest_size: Some(item_impl.pickup_dst), ..Default::default() }, ); diff --git a/src/nodes/player.rs b/src/nodes/player.rs index 195342a..cbf87a3 100644 --- a/src/nodes/player.rs +++ b/src/nodes/player.rs @@ -15,10 +15,11 @@ use macroquad_platformer::Actor; use crate::{ consts, - nodes::{item::{ItemInstanceId, ItemIdSource, ItemImplementationRegistry}, Nakama, NakamaRealtimeGame, Pickup}, + nodes::{item::{ItemIdSource, ItemImplementationRegistry}, Nakama, NakamaRealtimeGame, Pickup}, Resources, }; -use plugin_api::ItemType; + +use plugin_api::{ItemType, ItemInstanceId}; #[derive(Default, Debug, Clone)] pub struct Input { @@ -31,14 +32,51 @@ pub struct Input { pub struct Weapon { pub item_type: ItemType, item_id: ItemInstanceId, + texture: Texture2D, + mount_pos_right: Vec2, + mount_pos_left: Vec2, sprite: AnimatedSprite, fx_sprite: AnimatedSprite, fx: bool, } +impl Drop for Weapon { + fn drop(&mut self) { + let item_registry = storage::get::(); + let item_impl = item_registry.get_implementation(self.item_type).expect("Invalid ItemType"); + item_impl.destroy(self.item_id); + } +} + impl Weapon { + fn new(item_type: ItemType) -> Self { + let mut item_id_source = storage::get_mut::(); + let item_registry = storage::get::(); + let item_impl = item_registry.get_implementation(item_type).expect("Invalid ItemType"); + let instance_id = item_id_source.next_id(); + item_impl.construct(instance_id); + Self { + item_type, + item_id: instance_id, + texture: item_impl.texture, + mount_pos_right: item_impl.mount_pos_right, + mount_pos_left: item_impl.mount_pos_left, + sprite: item_impl.sprite.clone(), + fx_sprite: item_impl.fx_sprite.clone(), + fx: false, + } + } + fn uses_remaining(&self) -> Option<(u32, u32)> { - todo!("delegate to plugin") + let item_registry = storage::get::(); + let implementation = item_registry.get_implementation(self.item_type).unwrap(); + implementation.uses_remaining(self.item_id) + } + + fn update_shoot(&self) -> bool { + let item_registry = storage::get::(); + let implementation = item_registry.get_implementation(self.item_type).unwrap(); + implementation.update_shoot(self.item_id) } } @@ -128,16 +166,7 @@ impl Fish { } pub fn pick_weapon(&mut self, item_type: ItemType) { - let mut item_id_source = storage::get_mut::(); - let item_registry = storage::get::(); - let item_impl = item_registry.get_implementation(item_type).expect("Invalid ItemType"); - self.weapon = Some(Weapon { - item_type, - item_id: item_id_source.next_id(), - sprite: item_impl.sprite.clone(), - fx_sprite: item_impl.fx_sprite.clone(), - fx: false, - }); + self.weapon = Some(Weapon::new(item_type)); let resources = storage::get_mut::(); play_sound_once(resources.pickup_sound); } @@ -183,16 +212,14 @@ impl Fish { if self.dead == false { if let Some(weapon) = &mut self.weapon { - let item_registry = storage::get::(); - let item_impl = item_registry.get_implementation(weapon.item_type).expect("Invalid ItemType"); let mount_pos = if self.facing { - vec2(0., 16.) + weapon.mount_pos_right } else { - vec2(-60., 16.) + weapon.mount_pos_left }; weapon.sprite.update(); draw_texture_ex( - item_impl.texture, + weapon.texture, self.pos.x + mount_pos.x, self.pos.y + mount_pos.y, color::WHITE, @@ -207,7 +234,7 @@ impl Fish { if weapon.fx { weapon.fx_sprite.update(); draw_texture_ex( - item_impl.texture, + weapon.texture, self.pos.x + mount_pos.x, self.pos.y + mount_pos.y, color::WHITE, @@ -381,7 +408,14 @@ impl Player { } fn update_shoot(node: &mut RefMut, _dt: f32) { - todo!("actual update"); + if let Some(weapon) = &node.fish.weapon { + let done = weapon.update_shoot(); + if done { + node.state_machine.set_state(Self::ST_NORMAL); + } + } else { + node.state_machine.set_state(Self::ST_NORMAL); + } } fn update_aftermatch(node: &mut RefMut, _dt: f32) { diff --git a/src/plugin.rs b/src/plugin.rs index ec00bf1..1e890e7 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -63,4 +63,8 @@ impl PluginRegistry { PluginRegistry(plugins) } + + pub(crate) fn get_plugin(&mut self, plugin_id: PluginId) -> Option<&mut WasmPlugin> { + self.0.get_mut(&plugin_id).map(|(p, _)| p) + } } From f83ac2922477b3dcbc8b1536916735a305380a10 Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Sun, 25 Apr 2021 11:23:41 -0700 Subject: [PATCH 05/14] Get the game api working via a raw pointer, which I'm not super happy with --- core_plugin/src/lib.rs | 29 ++++++++++++++++- plugin_api/src/lib.rs | 13 ++++++-- src/nodes.rs | 2 +- src/nodes/item.rs | 20 +++++++----- src/nodes/player.rs | 14 ++++---- src/plugin.rs | 74 +++++++++++++++++++++++++++++++++++++----- 6 files changed, 123 insertions(+), 29 deletions(-) diff --git a/core_plugin/src/lib.rs b/core_plugin/src/lib.rs index 2f7ba7e..fac896d 100644 --- a/core_plugin/src/lib.rs +++ b/core_plugin/src/lib.rs @@ -3,15 +3,28 @@ use std::{ sync::Mutex, collections::HashMap, }; -use plugin_api::{ItemType, ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId, ItemDescription, Rect, ItemInstanceId}; +use plugin_api::{ItemType, ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId, ItemDescription, Rect, ItemInstanceId, import_game_api}; use once_cell::sync::Lazy; +// The Mutex here is really not necessary since this is guaranteed to be a single +// threaded environment, I'm just avoiding writing unsafe blocks. A library for +// writing these plugins could probably include a more efficient state store +// that takes advantage of the single threadedness. +// +// An alternative design would be for the new_instance function to allocate state on +// the heap and return a pointer which would then get passed back in when other +// functions are called. I think I like having the non-pointer key and letting the +// plugin interpret that however it sees fit but you could argue for the other version. static ITEMS: Lazy>> = Lazy::new(Mutex::default); const GUN: ItemType = ItemType::new(9868317461196439167); const SWORD: ItemType = ItemType::new(11238048715746880612); +pub const GUN_THROWBACK: f32 = 700.0; + +import_game_api!(); + enum ItemState { Gun(u32), Sword, @@ -140,5 +153,19 @@ fn uses_remaining(item_id: ItemInstanceId) -> Option<(u32, u32)> { #[wasm_plugin_guest::export_function] fn update_shoot(item_id: ItemInstanceId) -> bool { + if let Some(item) = ITEMS.lock().unwrap().get(&item_id) { + match item { + ItemState::Gun(_) => gun_handler(), + ItemState::Sword => true, + } + } else { + true + } +} + +fn gun_handler() -> bool { + spawn_bullet(); + let mut speed = get_speed(); + speed += GUN_THROWBACK * facing_dir(); true } diff --git a/plugin_api/src/lib.rs b/plugin_api/src/lib.rs index c1ec7ad..dc8e6c1 100644 --- a/plugin_api/src/lib.rs +++ b/plugin_api/src/lib.rs @@ -96,6 +96,15 @@ pub struct AnimationDescription { pub fps: u32, } -pub struct GameApiContext { +#[macro_export] +macro_rules! import_game_api { + () => { + wasm_plugin_guest::import_functions! { + fn spawn_bullet(); + fn set_sprite_fx(s: bool); + fn get_speed() -> f32; + fn set_speed(speed: f32); + fn facing_dir() -> f32; + } + }; } - diff --git a/src/nodes.rs b/src/nodes.rs index 537ae27..f90c949 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -18,6 +18,6 @@ pub use global_events::GlobalEvents; pub use level_background::LevelBackground; pub use nakama::{Nakama, NakamaRealtimeGame}; pub use pickup::Pickup; -pub use player::Player; +pub use player::{Player, Fish}; pub use remote_player::RemotePlayer; pub use item::{ItemIdSource, ItemImplementationRegistry}; diff --git a/src/nodes/item.rs b/src/nodes/item.rs index c6c982f..53cef37 100644 --- a/src/nodes/item.rs +++ b/src/nodes/item.rs @@ -7,10 +7,9 @@ use macroquad::{ animation::AnimatedSprite, }, }; -use wasm_plugin_host::WasmPlugin; use plugin_api::{ItemType, PluginId, ItemDescription, ItemInstanceId}; -use crate::plugin::{image_from_desc, animated_sprite_from_desc, PluginRegistry}; +use crate::{nodes::Fish, plugin::{image_from_desc, animated_sprite_from_desc, PluginRegistry, Plugin}}; pub(crate) struct ItemImplementation { @@ -44,26 +43,31 @@ impl ItemImplementation { } } - fn with_plugin(&self, f: impl Fn(&mut WasmPlugin) -> R) -> R { + fn with_plugin(&self, mut f: impl FnMut(&mut Plugin) -> R) -> R { let mut plugin_registry = storage::get_mut::(); let plugin = plugin_registry.get_plugin(self.implementing_plugin).unwrap(); f(plugin) } pub(crate) fn construct(&self, item_id: ItemInstanceId) { - self.with_plugin(|p| p.call_function_with_argument("new_instance", &(self.item_type, item_id)).unwrap()) + self.with_plugin(|p| p.wasm_plugin.call_function_with_argument("new_instance", &(self.item_type, item_id)).unwrap()) } pub(crate) fn destroy(&self, item_id: ItemInstanceId) { - self.with_plugin(|p| p.call_function_with_argument("destroy_instance", &item_id).unwrap()) + self.with_plugin(|p| p.wasm_plugin.call_function_with_argument("destroy_instance", &item_id).unwrap()) } pub(crate) fn uses_remaining(&self, item_id: ItemInstanceId) -> Option<(u32, u32)> { - self.with_plugin(|p| p.call_function_with_argument("uses_remaining", &item_id).unwrap()) + self.with_plugin(|p| p.wasm_plugin.call_function_with_argument("uses_remaining", &item_id).unwrap()) } - pub(crate) fn update_shoot(&self, item_id: ItemInstanceId) -> bool { - self.with_plugin(|p| p.call_function_with_argument("update_shoot", &item_id).unwrap()) + pub(crate) fn update_shoot(&self, item_id: ItemInstanceId, fish: &mut Fish) -> bool { + self.with_plugin(|p| { + p.game_api.lock().unwrap().set_current_fish(fish); + let done = p.wasm_plugin.call_function_with_argument("update_shoot", &item_id).unwrap(); + p.game_api.lock().unwrap().clear_current_fish(); + done + }) } } diff --git a/src/nodes/player.rs b/src/nodes/player.rs index cbf87a3..4e04fd7 100644 --- a/src/nodes/player.rs +++ b/src/nodes/player.rs @@ -73,21 +73,16 @@ impl Weapon { implementation.uses_remaining(self.item_id) } - fn update_shoot(&self) -> bool { - let item_registry = storage::get::(); - let implementation = item_registry.get_implementation(self.item_type).unwrap(); - implementation.update_shoot(self.item_id) - } } pub struct Fish { fish_sprite: AnimatedSprite, pub collider: Actor, - pos: Vec2, + pub pos: Vec2, speed: Vec2, on_ground: bool, dead: bool, - facing: bool, + pub facing: bool, pub weapon: Option, input: Input, } @@ -409,7 +404,10 @@ impl Player { fn update_shoot(node: &mut RefMut, _dt: f32) { if let Some(weapon) = &node.fish.weapon { - let done = weapon.update_shoot(); + let item_registry = storage::get::(); + let implementation = item_registry.get_implementation(weapon.item_type).unwrap(); + let item_id = weapon.item_id; + let done = implementation.update_shoot(item_id, &mut node.fish); if done { node.state_machine.set_state(Self::ST_NORMAL); } diff --git a/src/plugin.rs b/src/plugin.rs index 1e890e7..af584dd 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,18 +1,21 @@ use std::{ + sync::{Arc, Mutex}, path::Path, collections::HashMap, }; use macroquad::{ texture::Image, - experimental::animation::{AnimatedSprite, Animation}, + experimental::{ + scene::{self, Handle, RefMut}, + animation::{AnimatedSprite, Animation}, + }, }; -use nanoserde::DeJson; use wasm_plugin_host::WasmPlugin; -use plugin_api::{ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId, GameApiContext}; -use crate::nodes::{ItemImplementationRegistry}; +use plugin_api::{ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId}; +use crate::nodes::{ItemImplementationRegistry, Player, Fish}; pub fn image_from_desc(desc: ImageDescription) -> Image { @@ -41,7 +44,48 @@ pub fn animated_sprite_from_desc(desc: AnimatedSpriteDescription) -> AnimatedSpr ) } -pub struct PluginRegistry(HashMap); +pub struct PluginRegistry(HashMap); + +pub struct Plugin { + pub wasm_plugin: WasmPlugin, + pub game_api: Arc> +} + +#[derive(Default)] +pub struct GameApi { + current_fish: Option<*mut Fish>, +} +unsafe impl Send for GameApi {} +unsafe impl Sync for GameApi {} + +impl GameApi { + pub fn set_current_fish(&mut self, player: &mut Fish) { + self.current_fish.replace(player as *mut Fish); + } + pub fn clear_current_fish(&mut self) { + self.current_fish.take(); + } + + fn current_fish(&mut self) -> &mut Fish { + unsafe { self.current_fish.unwrap().as_mut() }.unwrap() + } + + fn spawn_bullet(&mut self) { + let fish = self.current_fish(); + let mut bullets = scene::find_node_by_type::().unwrap(); + bullets.spawn_bullet(fish.pos, fish.facing); + } + fn set_sprite_fx(&self, s: bool) { + } + fn get_speed(&self) -> f32 { + 0.0 + } + fn set_speed(&self, speed: f32) { + } + fn facing_dir(&self) -> f32 { + 0.0 + } +} impl PluginRegistry { pub fn load(path: impl AsRef, item_registry: &mut ItemImplementationRegistry) -> Self { @@ -49,14 +93,26 @@ impl PluginRegistry { for entry in path.as_ref().read_dir().expect("Unable to read plugins directory") { if let Ok(entry) = entry { if entry.path().to_str().unwrap().contains(".wasm") { - let mut plugin = wasm_plugin_host::WasmPluginBuilder::from_file(entry.path()).expect(&format!("Failed to load plugin {:?}", entry.path())).finish().unwrap(); + let game_api = Arc::new(Mutex::new(GameApi::default())); + let mut builder = wasm_plugin_host::WasmPluginBuilder::from_file(entry.path()).expect(&format!("Failed to load plugin {:?}", entry.path())); + builder = builder.import_function_with_context("spawn_bullet", game_api.clone(), |ctx: &Arc>| { ctx.lock().unwrap().spawn_bullet(); }); + builder = builder.import_function_with_context("set_sprite_fx", game_api.clone(), |ctx: &Arc>, s: bool| { ctx.lock().unwrap().set_sprite_fx(s); }); + builder = builder.import_function_with_context("get_speed", game_api.clone(), |ctx: &Arc>| { ctx.lock().unwrap().get_speed() }); + builder = builder.import_function_with_context("set_speed", game_api.clone(), |ctx: &Arc>, s: f32| { ctx.lock().unwrap().set_speed(s); }); + builder = builder.import_function_with_context("facing_dir", game_api.clone(), |ctx: &Arc>| { ctx.lock().unwrap().facing_dir() }); + let mut plugin = builder + .finish() + .unwrap(); let description: PluginDescription = plugin.call_function("plugin_description").expect(&format!("Failed to call 'plugin_description' on plugin {:?}", entry.path())); for item in description.items { item_registry.add(item, description.plugin_id); } - plugins.insert(description.plugin_id, (plugin, GameApiContext {})); + plugins.insert(description.plugin_id, Plugin { + wasm_plugin: plugin, + game_api + }); } } } @@ -64,7 +120,7 @@ impl PluginRegistry { PluginRegistry(plugins) } - pub(crate) fn get_plugin(&mut self, plugin_id: PluginId) -> Option<&mut WasmPlugin> { - self.0.get_mut(&plugin_id).map(|(p, _)| p) + pub(crate) fn get_plugin(&mut self, plugin_id: PluginId) -> Option<&mut Plugin> { + self.0.get_mut(&plugin_id) } } From be09182d0b9f31118fa39885795f41dae75a007d Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Sun, 25 Apr 2021 13:22:29 -0700 Subject: [PATCH 06/14] Safer version of the current_fish code --- src/nodes/item.rs | 3 +-- src/plugin.rs | 50 +++++++++++++++++++++++++++++------------------ 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/nodes/item.rs b/src/nodes/item.rs index 53cef37..0b00477 100644 --- a/src/nodes/item.rs +++ b/src/nodes/item.rs @@ -63,9 +63,8 @@ impl ItemImplementation { pub(crate) fn update_shoot(&self, item_id: ItemInstanceId, fish: &mut Fish) -> bool { self.with_plugin(|p| { - p.game_api.lock().unwrap().set_current_fish(fish); + let _guard = p.game_api.set_current_fish(fish); let done = p.wasm_plugin.call_function_with_argument("update_shoot", &item_id).unwrap(); - p.game_api.lock().unwrap().clear_current_fish(); done }) } diff --git a/src/plugin.rs b/src/plugin.rs index af584dd..5ac70e6 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -48,32 +48,44 @@ pub struct PluginRegistry(HashMap); pub struct Plugin { pub wasm_plugin: WasmPlugin, - pub game_api: Arc> + pub game_api: GameApi, } -#[derive(Default)] +#[derive(Default, Clone)] pub struct GameApi { - current_fish: Option<*mut Fish>, + current_fish: Arc>>, } unsafe impl Send for GameApi {} unsafe impl Sync for GameApi {} +pub struct CurrentFishGuard(GameApi); +impl Drop for CurrentFishGuard { + fn drop(&mut self) { + self.0.clear_current_fish(); + } +} + impl GameApi { - pub fn set_current_fish(&mut self, player: &mut Fish) { - self.current_fish.replace(player as *mut Fish); + pub fn set_current_fish(&self, player: &mut Fish) -> CurrentFishGuard { + self.current_fish.lock().unwrap().replace(player as *mut Fish); + CurrentFishGuard(self.clone()) } - pub fn clear_current_fish(&mut self) { - self.current_fish.take(); + pub fn clear_current_fish(&self) { + self.current_fish.lock().unwrap().take(); } - fn current_fish(&mut self) -> &mut Fish { - unsafe { self.current_fish.unwrap().as_mut() }.unwrap() + fn with_current_fish(&self, f: impl Fn(&mut Fish)) { + let fish_guard = self.current_fish.lock().unwrap(); + let fish_ptr: *mut Fish = fish_guard.unwrap(); + let fish_ref = unsafe { fish_ptr.as_mut().unwrap() }; + f(fish_ref); } - fn spawn_bullet(&mut self) { - let fish = self.current_fish(); - let mut bullets = scene::find_node_by_type::().unwrap(); - bullets.spawn_bullet(fish.pos, fish.facing); + fn spawn_bullet(&self) { + self.with_current_fish(|fish| { + let mut bullets = scene::find_node_by_type::().unwrap(); + bullets.spawn_bullet(fish.pos, fish.facing); + }); } fn set_sprite_fx(&self, s: bool) { } @@ -93,13 +105,13 @@ impl PluginRegistry { for entry in path.as_ref().read_dir().expect("Unable to read plugins directory") { if let Ok(entry) = entry { if entry.path().to_str().unwrap().contains(".wasm") { - let game_api = Arc::new(Mutex::new(GameApi::default())); + let game_api = GameApi::default(); let mut builder = wasm_plugin_host::WasmPluginBuilder::from_file(entry.path()).expect(&format!("Failed to load plugin {:?}", entry.path())); - builder = builder.import_function_with_context("spawn_bullet", game_api.clone(), |ctx: &Arc>| { ctx.lock().unwrap().spawn_bullet(); }); - builder = builder.import_function_with_context("set_sprite_fx", game_api.clone(), |ctx: &Arc>, s: bool| { ctx.lock().unwrap().set_sprite_fx(s); }); - builder = builder.import_function_with_context("get_speed", game_api.clone(), |ctx: &Arc>| { ctx.lock().unwrap().get_speed() }); - builder = builder.import_function_with_context("set_speed", game_api.clone(), |ctx: &Arc>, s: f32| { ctx.lock().unwrap().set_speed(s); }); - builder = builder.import_function_with_context("facing_dir", game_api.clone(), |ctx: &Arc>| { ctx.lock().unwrap().facing_dir() }); + builder = builder.import_function_with_context("spawn_bullet", game_api.clone(), |ctx: &GameApi| { ctx.spawn_bullet(); }); + builder = builder.import_function_with_context("set_sprite_fx", game_api.clone(), |ctx: &GameApi, s: bool| { ctx.set_sprite_fx(s); }); + builder = builder.import_function_with_context("get_speed", game_api.clone(), |ctx: &GameApi| { ctx.get_speed() }); + builder = builder.import_function_with_context("set_speed", game_api.clone(), |ctx: &GameApi, s: f32| { ctx.set_speed(s); }); + builder = builder.import_function_with_context("facing_dir", game_api.clone(), |ctx: &GameApi| { ctx.facing_dir() }); let mut plugin = builder .finish() .unwrap(); From 5be5295990b4015dae9e43e31a39cad6a96dab70 Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Tue, 27 Apr 2021 08:50:27 -0700 Subject: [PATCH 07/14] Weapon coroutines mostly working --- core_plugin/Cargo.lock | 119 ++++++++++++++++++++++++++ core_plugin/Cargo.toml | 3 + core_plugin/src/lib.rs | 189 +++++++++++++++++++++++++++++++++-------- plugin_api/src/lib.rs | 11 ++- src/nodes/item.rs | 6 +- src/nodes/player.rs | 39 +++++---- src/plugin.rs | 71 ++++++++++++++-- 7 files changed, 374 insertions(+), 64 deletions(-) diff --git a/core_plugin/Cargo.lock b/core_plugin/Cargo.lock index 2f41cd9..bfab374 100644 --- a/core_plugin/Cargo.lock +++ b/core_plugin/Cargo.lock @@ -6,6 +6,26 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "slab", +] + +[[package]] +name = "async-task" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" + [[package]] name = "autocfg" version = "1.0.1" @@ -30,6 +50,12 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cache-padded" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" + [[package]] name = "cfg-if" version = "0.1.10" @@ -48,11 +74,23 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "concurrent-queue" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" +dependencies = [ + "cache-padded", +] + [[package]] name = "core_plugin" version = "0.1.0" dependencies = [ + "async-executor", + "futures-lite", "image", + "lazy_static", "once_cell", "plugin_api", "wasm_plugin_guest", @@ -78,6 +116,42 @@ dependencies = [ "byteorder", ] +[[package]] +name = "fastrand" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77b705829d1e87f762c2df6da140b26af5839e1033aa84aa5f56bb688e4e1bdb" +dependencies = [ + "instant", +] + +[[package]] +name = "futures-core" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" + +[[package]] +name = "futures-io" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04" + +[[package]] +name = "futures-lite" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4481d0cd0de1d204a4fa55e7d45f07b1d958abcb06714b3446438e2eff695fb" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "image" version = "0.23.14" @@ -93,18 +167,39 @@ dependencies = [ "png", ] +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "itoa" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + [[package]] name = "memory_units" version = "0.4.0" @@ -167,6 +262,18 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + +[[package]] +name = "pin-project-lite" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" + [[package]] name = "plugin_api" version = "0.1.0" @@ -241,6 +348,12 @@ dependencies = [ "serde", ] +[[package]] +name = "slab" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" + [[package]] name = "syn" version = "1.0.70" @@ -258,6 +371,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "wasm_plugin_guest" version = "0.1.4" diff --git a/core_plugin/Cargo.toml b/core_plugin/Cargo.toml index a7cf9bf..3ac0e7f 100644 --- a/core_plugin/Cargo.toml +++ b/core_plugin/Cargo.toml @@ -13,3 +13,6 @@ wasm_plugin_guest = { version = "0.1.4", default-features = false, features = [" image = { version = "0.23", default-features = false, features = ["png"] } wee_alloc = "0.4.5" once_cell = "1.7.2" +futures-lite = "1.11.3" +async-executor = "1.4.1" +lazy_static = "1.4.0" diff --git a/core_plugin/src/lib.rs b/core_plugin/src/lib.rs index fac896d..28e73e9 100644 --- a/core_plugin/src/lib.rs +++ b/core_plugin/src/lib.rs @@ -1,11 +1,14 @@ use std::{ + future::Future, + sync::Arc, + cell::{RefCell, Cell}, io::Cursor, - sync::Mutex, collections::HashMap, + collections::HashMap, }; +use async_executor::{LocalExecutor, Task}; use plugin_api::{ItemType, ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId, ItemDescription, Rect, ItemInstanceId, import_game_api}; -use once_cell::sync::Lazy; // The Mutex here is really not necessary since this is guaranteed to be a single // threaded environment, I'm just avoiding writing unsafe blocks. A library for @@ -16,7 +19,9 @@ use once_cell::sync::Lazy; // the heap and return a pointer which would then get passed back in when other // functions are called. I think I like having the non-pointer key and letting the // plugin interpret that however it sees fit but you could argue for the other version. -static ITEMS: Lazy>> = Lazy::new(Mutex::default); +thread_local! { + pub static ITEMS:RefCell> = Default::default(); +} const GUN: ItemType = ItemType::new(9868317461196439167); const SWORD: ItemType = ItemType::new(11238048715746880612); @@ -25,9 +30,9 @@ pub const GUN_THROWBACK: f32 = 700.0; import_game_api!(); -enum ItemState { - Gun(u32), - Sword, +pub enum ItemState { + Gun(ItemCoroutine, GunState), + Sword(ItemCoroutine), } #[wasm_plugin_guest::export_function] @@ -73,16 +78,16 @@ fn plugin_description() -> PluginDescription { frames: 1, fps: 1, }, + AnimationDescription { + name: "shoot".to_string(), + row: 1, + frames: 4, + fps: 15, + }, ], playing: true, }, - fx_sprite: AnimatedSpriteDescription { - tile_width: 76, - tile_height: 66, - animations: vec![ - ], - playing: true, - }, + fx_sprite: None, }, ItemDescription { item_type: GUN, @@ -111,16 +116,28 @@ fn plugin_description() -> PluginDescription { frames: 1, fps: 1, }, + AnimationDescription { + name: "shoot".to_string(), + row: 1, + frames: 3, + fps: 15, + }, ], playing: true, }, - fx_sprite: AnimatedSpriteDescription { + fx_sprite: Some(AnimatedSpriteDescription { tile_width: 76, tile_height: 66, animations: vec![ + AnimationDescription { + name: "shoot".to_string(), + row: 2, + frames: 3, + fps: 15, + }, ], playing: true, - }, + }), } ], } @@ -129,43 +146,147 @@ fn plugin_description() -> PluginDescription { #[wasm_plugin_guest::export_function] fn new_instance(item_type: ItemType, item_id: ItemInstanceId) { let state = match item_type { - GUN => ItemState::Gun(3), - SWORD => ItemState::Sword, + GUN => ItemState::Gun(ItemCoroutine::default(), GunState::default()), + SWORD => ItemState::Sword(ItemCoroutine::default()), _ => panic!() }; - ITEMS.lock().unwrap().insert(item_id, state); + ITEMS.with(|items| items.borrow_mut().insert(item_id, state)); } #[wasm_plugin_guest::export_function] fn destroy_instance(item_id: ItemInstanceId) { - ITEMS.lock().unwrap().remove(&item_id); + ITEMS.with(|items| items.borrow_mut().remove(&item_id)); } #[wasm_plugin_guest::export_function] fn uses_remaining(item_id: ItemInstanceId) -> Option<(u32, u32)> { - if let Some(ItemState::Gun(ammo)) = ITEMS.lock().unwrap().get(&item_id) { - Some((*ammo, 3)) - } else { - None - } + ITEMS.with(|items| { + if let Some(ItemState::Gun(_, state)) = items.borrow_mut().get(&item_id) { + Some((state.ammo, 3)) + } else { + None + } + }) } #[wasm_plugin_guest::export_function] -fn update_shoot(item_id: ItemInstanceId) -> bool { - if let Some(item) = ITEMS.lock().unwrap().get(&item_id) { - match item { - ItemState::Gun(_) => gun_handler(), - ItemState::Sword => true, +fn update_shoot(item_id: ItemInstanceId, delta_time: f32) -> bool { + ITEMS.with(|items| { + if let Some(item) = items.borrow_mut().get_mut(&item_id) { + match item { + ItemState::Gun(coroutine, state) => { + if !coroutine.started() { + state.ammo -= 1; + coroutine.start(gun_coroutine); + } + coroutine.step(delta_time) + }, + ItemState::Sword(coroutine) => { + if !coroutine.started() { + coroutine.start(sword_coroutine); + } + coroutine.step(delta_time) + }, + } + } else { + true + } + }) +} + +pub struct ItemCoroutine { + executor: LocalExecutor<'static>, + timer: Timer, + coroutine: Option>, +} + +impl Default for ItemCoroutine { + fn default() -> Self { + Self { + executor: LocalExecutor::new(), + timer: Timer::default(), + coroutine: None, } - } else { - true } } -fn gun_handler() -> bool { +impl ItemCoroutine { + fn step(&mut self, delta_time: f32) -> bool { + self.timer.incr_time(delta_time); + if self.coroutine.is_none() { + return false; + } + self.executor.try_tick(); + if self.executor.is_empty() { + self.coroutine.take(); + true + } else { + false + } + } + + fn started(&self) -> bool { + self.coroutine.is_some() + } + + fn start(&mut self, f: F) + where + Fu: Future, + F: Fn(Timer) -> Fu + 'static + { + let timer = self.timer.clone(); + self.coroutine = Some(self.executor.spawn(async move { + f(timer).await + })); + } +} + +pub struct GunState { + ammo: u32, +} + +impl Default for GunState { + fn default() -> Self { + Self { + ammo: 3, + } + } +} + +#[derive(Clone, Default)] +struct Timer { + time: Arc>, +} + +impl Timer { + async fn wait_seconds(&self, seconds: f32) { + let end = self.time.get() + seconds; + while self.time.get() < end { + futures_lite::future::yield_now().await; + } + } + + fn incr_time(&self, delta: f32) { + let current_time = self.time.get(); + self.time.set(current_time + delta); + } +} + +async fn gun_coroutine(timer: Timer) { spawn_bullet(); + set_sprite_fx(true); let mut speed = get_speed(); - speed += GUN_THROWBACK * facing_dir(); - true + speed[0] -= GUN_THROWBACK * facing_dir(); + set_speed(speed); + set_sprite_animation(1); + timer.wait_seconds(0.08*3.0).await; + set_sprite_animation(0); + set_sprite_fx(false); +} + +async fn sword_coroutine(timer: Timer) { + set_sprite_animation(1); + timer.wait_seconds(0.08*3.0).await; + set_sprite_animation(0); } diff --git a/plugin_api/src/lib.rs b/plugin_api/src/lib.rs index dc8e6c1..012060c 100644 --- a/plugin_api/src/lib.rs +++ b/plugin_api/src/lib.rs @@ -62,7 +62,7 @@ pub struct ItemDescription { pub pickup_src: Rect, pub pickup_dst: [f32; 2], pub sprite: AnimatedSpriteDescription, - pub fx_sprite: AnimatedSpriteDescription, + pub fx_sprite: Option, } #[derive(Clone, Serialize, Deserialize)] @@ -102,9 +102,14 @@ macro_rules! import_game_api { wasm_plugin_guest::import_functions! { fn spawn_bullet(); fn set_sprite_fx(s: bool); - fn get_speed() -> f32; - fn set_speed(speed: f32); + fn get_speed() -> [f32; 2]; + fn set_speed(speed: [f32; 2]); fn facing_dir() -> f32; + fn set_sprite_animation(animation: u32); + fn set_fx_sprite_animation(animation: u32); + fn set_sprite_frame(frame: u32); + fn set_fx_sprite_frame(frame: u32); + fn clear_weapon(); } }; } diff --git a/src/nodes/item.rs b/src/nodes/item.rs index 0b00477..ce26f7c 100644 --- a/src/nodes/item.rs +++ b/src/nodes/item.rs @@ -21,7 +21,7 @@ pub(crate) struct ItemImplementation { pub pickup_src: Rect, pub pickup_dst: Vec2, pub sprite: AnimatedSprite, - pub fx_sprite: AnimatedSprite, + pub fx_sprite: Option, implementing_plugin: PluginId, } @@ -38,7 +38,7 @@ impl ItemImplementation { pickup_src: Rect::new(pickup_src.x, pickup_src.y, pickup_src.w, pickup_src.h), pickup_dst: vec2(description.pickup_dst[0], description.pickup_dst[1]), sprite: animated_sprite_from_desc(description.sprite), - fx_sprite: animated_sprite_from_desc(description.fx_sprite), + fx_sprite: description.fx_sprite.map(|d| animated_sprite_from_desc(d)), implementing_plugin: plugin } } @@ -64,7 +64,7 @@ impl ItemImplementation { pub(crate) fn update_shoot(&self, item_id: ItemInstanceId, fish: &mut Fish) -> bool { self.with_plugin(|p| { let _guard = p.game_api.set_current_fish(fish); - let done = p.wasm_plugin.call_function_with_argument("update_shoot", &item_id).unwrap(); + let done = p.wasm_plugin.call_function_with_argument("update_shoot", &(item_id, get_frame_time())).unwrap(); done }) } diff --git a/src/nodes/player.rs b/src/nodes/player.rs index 4e04fd7..cba3c29 100644 --- a/src/nodes/player.rs +++ b/src/nodes/player.rs @@ -35,9 +35,9 @@ pub struct Weapon { texture: Texture2D, mount_pos_right: Vec2, mount_pos_left: Vec2, - sprite: AnimatedSprite, - fx_sprite: AnimatedSprite, - fx: bool, + pub sprite: AnimatedSprite, + pub fx_sprite: Option, + pub fx: bool, } impl Drop for Weapon { @@ -79,7 +79,7 @@ pub struct Fish { fish_sprite: AnimatedSprite, pub collider: Actor, pub pos: Vec2, - speed: Vec2, + pub speed: Vec2, on_ground: bool, dead: bool, pub facing: bool, @@ -227,19 +227,21 @@ impl Fish { ); if weapon.fx { - weapon.fx_sprite.update(); - draw_texture_ex( - weapon.texture, - self.pos.x + mount_pos.x, - self.pos.y + mount_pos.y, - color::WHITE, - DrawTextureParams { - source: Some(weapon.fx_sprite.frame().source_rect), - dest_size: Some(weapon.fx_sprite.frame().dest_size), - flip_x: !self.facing, - ..Default::default() - }, - ); + if let Some(sprite) = &mut weapon.fx_sprite { + sprite.update(); + draw_texture_ex( + weapon.texture, + self.pos.x + mount_pos.x, + self.pos.y + mount_pos.y, + color::WHITE, + DrawTextureParams { + source: Some(sprite.frame().source_rect), + dest_size: Some(sprite.frame().dest_size), + flip_x: !self.facing, + ..Default::default() + }, + ); + } } } } @@ -409,6 +411,9 @@ impl Player { let item_id = weapon.item_id; let done = implementation.update_shoot(item_id, &mut node.fish); if done { + if let Some((0, _)) = implementation.uses_remaining(item_id) { + node.fish.weapon.take(); + } node.state_machine.set_state(Self::ST_NORMAL); } } else { diff --git a/src/plugin.rs b/src/plugin.rs index 5ac70e6..d3b4237 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -74,11 +74,11 @@ impl GameApi { self.current_fish.lock().unwrap().take(); } - fn with_current_fish(&self, f: impl Fn(&mut Fish)) { + fn with_current_fish(&self, f: impl Fn(&mut Fish) -> R) -> R { let fish_guard = self.current_fish.lock().unwrap(); let fish_ptr: *mut Fish = fish_guard.unwrap(); let fish_ref = unsafe { fish_ptr.as_mut().unwrap() }; - f(fish_ref); + f(fish_ref) } fn spawn_bullet(&self) { @@ -87,15 +87,68 @@ impl GameApi { bullets.spawn_bullet(fish.pos, fish.facing); }); } + fn set_sprite_fx(&self, s: bool) { + self.with_current_fish(|fish| { + if let Some(weapon) = &mut fish.weapon { + weapon.fx = s; + } + }); + } + + fn get_speed(&self) -> [f32; 2] { + self.with_current_fish(|fish| { + [fish.speed.x, fish.speed.y] + }) + } + + fn set_speed(&self, speed: [f32; 2]) { + self.with_current_fish(|fish| { + fish.speed.x = speed[0]; + fish.speed.y = speed[1]; + }) + } + + fn set_sprite_animation(&self, animation: usize) { + self.with_current_fish(|fish| { + if let Some(weapon) = &mut fish.weapon { + weapon.sprite.set_animation(animation); + } + }) } - fn get_speed(&self) -> f32 { - 0.0 + + fn set_fx_sprite_animation(&self, animation: usize) { + self.with_current_fish(|fish| { + if let Some(weapon) = &mut fish.weapon { + if let Some(sprite) = &mut weapon.fx_sprite { + sprite.set_animation(animation); + } + } + }) + } + + fn set_sprite_frame(&self, frame: u32) { + self.with_current_fish(|fish| { + if let Some(weapon) = &mut fish.weapon { + weapon.sprite.set_frame(frame); + } + }) } - fn set_speed(&self, speed: f32) { + + fn set_fx_sprite_frame(&self, frame: u32) { + self.with_current_fish(|fish| { + if let Some(weapon) = &mut fish.weapon { + if let Some(sprite) = &mut weapon.fx_sprite { + sprite.set_frame(frame); + } + } + }) } + fn facing_dir(&self) -> f32 { - 0.0 + self.with_current_fish(|fish| { + fish.facing_dir() + }) } } @@ -110,7 +163,11 @@ impl PluginRegistry { builder = builder.import_function_with_context("spawn_bullet", game_api.clone(), |ctx: &GameApi| { ctx.spawn_bullet(); }); builder = builder.import_function_with_context("set_sprite_fx", game_api.clone(), |ctx: &GameApi, s: bool| { ctx.set_sprite_fx(s); }); builder = builder.import_function_with_context("get_speed", game_api.clone(), |ctx: &GameApi| { ctx.get_speed() }); - builder = builder.import_function_with_context("set_speed", game_api.clone(), |ctx: &GameApi, s: f32| { ctx.set_speed(s); }); + builder = builder.import_function_with_context("set_speed", game_api.clone(), |ctx: &GameApi, s: [f32; 2]| { ctx.set_speed(s); }); + builder = builder.import_function_with_context("set_sprite_animation", game_api.clone(), |ctx: &GameApi, animation: usize| { ctx.set_sprite_animation(animation); }); + builder = builder.import_function_with_context("set_fx_sprite_animation", game_api.clone(), |ctx: &GameApi, animation: usize| { ctx.set_fx_sprite_animation(animation); }); + builder = builder.import_function_with_context("set_sprite_frame", game_api.clone(), |ctx: &GameApi, frame: u32| { ctx.set_sprite_frame(frame); }); + builder = builder.import_function_with_context("set_fx_sprite_frame", game_api.clone(), |ctx: &GameApi, frame: u32| { ctx.set_fx_sprite_frame(frame); }); builder = builder.import_function_with_context("facing_dir", game_api.clone(), |ctx: &GameApi| { ctx.facing_dir() }); let mut plugin = builder .finish() From 5baa85adbd2b4f3f43bcdafcbf6bc6d63b18533f Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Tue, 27 Apr 2021 11:55:31 -0700 Subject: [PATCH 08/14] Explosions and bullets again --- core_plugin/src/lib.rs | 59 ++++++++++++++++++++--- plugin_api/src/lib.rs | 1 + src/nodes/item.rs | 15 ++++-- src/nodes/player.rs | 42 +++++++++++----- src/plugin.rs | 107 ++++++++++++++++++----------------------- 5 files changed, 138 insertions(+), 86 deletions(-) diff --git a/core_plugin/src/lib.rs b/core_plugin/src/lib.rs index 28e73e9..3ded5a4 100644 --- a/core_plugin/src/lib.rs +++ b/core_plugin/src/lib.rs @@ -4,8 +4,11 @@ use std::{ cell::{RefCell, Cell}, io::Cursor, collections::HashMap, + pin::Pin, + task::{Context, Poll}, }; use async_executor::{LocalExecutor, Task}; +use futures_lite::future; use plugin_api::{ItemType, ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId, ItemDescription, Rect, ItemInstanceId, import_game_api}; @@ -214,10 +217,12 @@ impl Default for ItemCoroutine { impl ItemCoroutine { fn step(&mut self, delta_time: f32) -> bool { self.timer.incr_time(delta_time); + debug_print(format!("poop {}", self.timer.time.get())); if self.coroutine.is_none() { - return false; + return true; } - self.executor.try_tick(); + let r = self.executor.try_tick(); + debug_print(format!("poop {} {}", self.executor.is_empty(), r)); if self.executor.is_empty() { self.coroutine.take(); true @@ -261,10 +266,15 @@ struct Timer { impl Timer { async fn wait_seconds(&self, seconds: f32) { - let end = self.time.get() + seconds; - while self.time.get() < end { - futures_lite::future::yield_now().await; - } + let timer = self.clone(); + let start_time = timer.time.get(); + future::poll_fn(move |_cx| { + if timer.time.get() - start_time >= seconds { + Poll::Ready(()) + } else { + Poll::Pending + } + }).await; } fn incr_time(&self, delta: f32) { @@ -273,6 +283,27 @@ impl Timer { } } +pub struct TimerDelayFuture { + start_time: f32, + time: f32, + timer: Timer, +} +impl Unpin for TimerDelayFuture {} + +impl Future for TimerDelayFuture { + type Output = Option<()>; + + fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { + debug_print(format!("{} {} {}", self.timer.time.get(), self.start_time, self.time)); + if self.timer.time.get() - self.start_time >= self.time { + Poll::Ready(Some(())) + } else { + //Poll::Pending + Poll::Ready(Some(())) + } + } +} + async fn gun_coroutine(timer: Timer) { spawn_bullet(); set_sprite_fx(true); @@ -280,13 +311,25 @@ async fn gun_coroutine(timer: Timer) { speed[0] -= GUN_THROWBACK * facing_dir(); set_speed(speed); set_sprite_animation(1); - timer.wait_seconds(0.08*3.0).await; + + for i in 0u32..3 { + set_sprite_frame(i); + set_fx_sprite_frame(i); + debug_print("gun pre wait".to_string()); + timer.wait_seconds(0.08).await; + debug_print("gun post wait".to_string()); + } set_sprite_animation(0); set_sprite_fx(false); + debug_print("gun done".to_string()); } async fn sword_coroutine(timer: Timer) { set_sprite_animation(1); - timer.wait_seconds(0.08*3.0).await; + for i in 0u32..3 { + set_sprite_frame(i); + timer.wait_seconds(0.08).await; + } set_sprite_animation(0); + debug_print("sword done".to_string()); } diff --git a/plugin_api/src/lib.rs b/plugin_api/src/lib.rs index 012060c..aa5876e 100644 --- a/plugin_api/src/lib.rs +++ b/plugin_api/src/lib.rs @@ -110,6 +110,7 @@ macro_rules! import_game_api { fn set_sprite_frame(frame: u32); fn set_fx_sprite_frame(frame: u32); fn clear_weapon(); + fn debug_print(message: String); } }; } diff --git a/src/nodes/item.rs b/src/nodes/item.rs index ce26f7c..ba1cde7 100644 --- a/src/nodes/item.rs +++ b/src/nodes/item.rs @@ -3,13 +3,14 @@ use std::collections::HashMap; use macroquad::{ prelude::*, experimental::{ + scene::Handle, collections::storage, animation::AnimatedSprite, }, }; use plugin_api::{ItemType, PluginId, ItemDescription, ItemInstanceId}; -use crate::{nodes::Fish, plugin::{image_from_desc, animated_sprite_from_desc, PluginRegistry, Plugin}}; +use crate::{nodes::Player, plugin::{image_from_desc, animated_sprite_from_desc, PluginRegistry, Plugin}}; pub(crate) struct ItemImplementation { @@ -61,11 +62,15 @@ impl ItemImplementation { self.with_plugin(|p| p.wasm_plugin.call_function_with_argument("uses_remaining", &item_id).unwrap()) } - pub(crate) fn update_shoot(&self, item_id: ItemInstanceId, fish: &mut Fish) -> bool { + pub(crate) fn update_shoot(&self, item_id: ItemInstanceId, player: Handle) -> bool { self.with_plugin(|p| { - let _guard = p.game_api.set_current_fish(fish); - let done = p.wasm_plugin.call_function_with_argument("update_shoot", &(item_id, get_frame_time())).unwrap(); - done + let Plugin { + game_api, + wasm_plugin + } = p; + game_api.with_current_player(player, || { + wasm_plugin.call_function_with_argument("update_shoot", &(item_id, get_frame_time())).unwrap() + }) }) } } diff --git a/src/nodes/player.rs b/src/nodes/player.rs index cba3c29..ae1a6b0 100644 --- a/src/nodes/player.rs +++ b/src/nodes/player.rs @@ -291,6 +291,7 @@ impl Player { Self::ST_SHOOT, State::new() .update(Self::update_shoot) + .coroutine(Self::shoot_coroutine), ); state_machine.add_state( Self::ST_AFTERMATCH, @@ -405,20 +406,37 @@ impl Player { } fn update_shoot(node: &mut RefMut, _dt: f32) { - if let Some(weapon) = &node.fish.weapon { - let item_registry = storage::get::(); - let implementation = item_registry.get_implementation(weapon.item_type).unwrap(); - let item_id = weapon.item_id; - let done = implementation.update_shoot(item_id, &mut node.fish); - if done { - if let Some((0, _)) = implementation.uses_remaining(item_id) { - node.fish.weapon.take(); + } + + fn shoot_coroutine(node: &mut RefMut) -> Coroutine { + let handle = node.handle(); + let coroutine = async move { + loop { + let mut item = None; + if let Some(weapon) = &scene::get_node(handle).fish.weapon { + item = Some((weapon.item_type, weapon.item_id)); } - node.state_machine.set_state(Self::ST_NORMAL); + if let Some((item_type, item_id)) = item { + let item_registry = storage::get::(); + let implementation = item_registry.get_implementation(item_type).unwrap(); + let done = implementation.update_shoot(item_id, handle); + if done { + let node = &mut *scene::get_node(handle); + if let Some((0, _)) = implementation.uses_remaining(item_id) { + node.fish.weapon.take(); + } + node.state_machine.set_state(Self::ST_NORMAL); + break + } + } else { + let node = &mut *scene::get_node(handle); + node.state_machine.set_state(Self::ST_NORMAL); + break + } + wait_seconds(0.005).await; } - } else { - node.state_machine.set_state(Self::ST_NORMAL); - } + }; + start_coroutine(coroutine) } fn update_aftermatch(node: &mut RefMut, _dt: f32) { diff --git a/src/plugin.rs b/src/plugin.rs index d3b4237..b41de4e 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -15,7 +15,7 @@ use macroquad::{ use wasm_plugin_host::WasmPlugin; use plugin_api::{ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId}; -use crate::nodes::{ItemImplementationRegistry, Player, Fish}; +use crate::nodes::{ItemImplementationRegistry, Player}; pub fn image_from_desc(desc: ImageDescription) -> Image { @@ -53,102 +53,86 @@ pub struct Plugin { #[derive(Default, Clone)] pub struct GameApi { - current_fish: Arc>>, + current_player: Arc>>>, } unsafe impl Send for GameApi {} unsafe impl Sync for GameApi {} -pub struct CurrentFishGuard(GameApi); -impl Drop for CurrentFishGuard { - fn drop(&mut self) { - self.0.clear_current_fish(); - } -} - impl GameApi { - pub fn set_current_fish(&self, player: &mut Fish) -> CurrentFishGuard { - self.current_fish.lock().unwrap().replace(player as *mut Fish); - CurrentFishGuard(self.clone()) - } - pub fn clear_current_fish(&self) { - self.current_fish.lock().unwrap().take(); + pub fn with_current_player(&self, player: Handle, mut f: impl FnMut() -> R) -> R { + self.current_player.lock().unwrap().replace(player); + let result = f(); + self.current_player.lock().unwrap().take(); + result } - fn with_current_fish(&self, f: impl Fn(&mut Fish) -> R) -> R { - let fish_guard = self.current_fish.lock().unwrap(); - let fish_ptr: *mut Fish = fish_guard.unwrap(); - let fish_ref = unsafe { fish_ptr.as_mut().unwrap() }; - f(fish_ref) + fn current_player(&self) -> Handle { + self.current_player.lock().unwrap().unwrap().clone() } fn spawn_bullet(&self) { - self.with_current_fish(|fish| { - let mut bullets = scene::find_node_by_type::().unwrap(); - bullets.spawn_bullet(fish.pos, fish.facing); - }); + let node = &mut *scene::get_node(self.current_player()); + let mut bullets = scene::find_node_by_type::().unwrap(); + bullets.spawn_bullet(node.fish.pos, node.fish.facing); } fn set_sprite_fx(&self, s: bool) { - self.with_current_fish(|fish| { - if let Some(weapon) = &mut fish.weapon { - weapon.fx = s; - } - }); + let node = &mut *scene::get_node(self.current_player()); + if let Some(weapon) = &mut node.fish.weapon { + weapon.fx = s; + } } fn get_speed(&self) -> [f32; 2] { - self.with_current_fish(|fish| { - [fish.speed.x, fish.speed.y] - }) + let node = &mut *scene::get_node(self.current_player()); + [node.fish.speed.x, node.fish.speed.y] } fn set_speed(&self, speed: [f32; 2]) { - self.with_current_fish(|fish| { - fish.speed.x = speed[0]; - fish.speed.y = speed[1]; - }) + let node = &mut *scene::get_node(self.current_player()); + node.fish.speed.x = speed[0]; + node.fish.speed.y = speed[1]; } fn set_sprite_animation(&self, animation: usize) { - self.with_current_fish(|fish| { - if let Some(weapon) = &mut fish.weapon { - weapon.sprite.set_animation(animation); - } - }) + let node = &mut *scene::get_node(self.current_player()); + if let Some(weapon) = &mut node.fish.weapon { + weapon.sprite.set_animation(animation); + } } fn set_fx_sprite_animation(&self, animation: usize) { - self.with_current_fish(|fish| { - if let Some(weapon) = &mut fish.weapon { - if let Some(sprite) = &mut weapon.fx_sprite { - sprite.set_animation(animation); - } + let node = &mut *scene::get_node(self.current_player()); + if let Some(weapon) = &mut node.fish.weapon { + if let Some(sprite) = &mut weapon.fx_sprite { + sprite.set_animation(animation); } - }) + } } fn set_sprite_frame(&self, frame: u32) { - self.with_current_fish(|fish| { - if let Some(weapon) = &mut fish.weapon { - weapon.sprite.set_frame(frame); - } - }) + let node = &mut *scene::get_node(self.current_player()); + if let Some(weapon) = &mut node.fish.weapon { + weapon.sprite.set_frame(frame); + } } fn set_fx_sprite_frame(&self, frame: u32) { - self.with_current_fish(|fish| { - if let Some(weapon) = &mut fish.weapon { - if let Some(sprite) = &mut weapon.fx_sprite { - sprite.set_frame(frame); - } + let node = &mut *scene::get_node(self.current_player()); + if let Some(weapon) = &mut node.fish.weapon { + if let Some(sprite) = &mut weapon.fx_sprite { + sprite.set_frame(frame); } - }) + } } fn facing_dir(&self) -> f32 { - self.with_current_fish(|fish| { - fish.facing_dir() - }) + let node = &mut *scene::get_node(self.current_player()); + node.fish.facing_dir() + } + + fn debug_print(&self, message: String) { + println!("{}", message); } } @@ -169,6 +153,7 @@ impl PluginRegistry { builder = builder.import_function_with_context("set_sprite_frame", game_api.clone(), |ctx: &GameApi, frame: u32| { ctx.set_sprite_frame(frame); }); builder = builder.import_function_with_context("set_fx_sprite_frame", game_api.clone(), |ctx: &GameApi, frame: u32| { ctx.set_fx_sprite_frame(frame); }); builder = builder.import_function_with_context("facing_dir", game_api.clone(), |ctx: &GameApi| { ctx.facing_dir() }); + builder = builder.import_function_with_context("debug_print", game_api.clone(), |ctx: &GameApi, message: String| { ctx.debug_print(message) }); let mut plugin = builder .finish() .unwrap(); From 5446fef115b504ccc047370c664bbbce397c0586 Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Tue, 27 Apr 2021 16:03:40 -0700 Subject: [PATCH 09/14] Rip out the in-plugin async stuff because it was taking too long to get right. We can come back to that. --- core_plugin/Cargo.lock | 181 +-------------------------------------- core_plugin/Cargo.toml | 5 -- core_plugin/src/lib.rs | 187 ++++++++++------------------------------- src/nodes/item.rs | 2 +- 4 files changed, 46 insertions(+), 329 deletions(-) diff --git a/core_plugin/Cargo.lock b/core_plugin/Cargo.lock index bfab374..c658346 100644 --- a/core_plugin/Cargo.lock +++ b/core_plugin/Cargo.lock @@ -6,26 +6,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" -[[package]] -name = "async-executor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "once_cell", - "slab", -] - -[[package]] -name = "async-task" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" - [[package]] name = "autocfg" version = "1.0.1" @@ -50,18 +30,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "cache-padded" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -74,27 +42,13 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" -[[package]] -name = "concurrent-queue" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" -dependencies = [ - "cache-padded", -] - [[package]] name = "core_plugin" version = "0.1.0" dependencies = [ - "async-executor", - "futures-lite", "image", - "lazy_static", - "once_cell", "plugin_api", "wasm_plugin_guest", - "wee_alloc", ] [[package]] @@ -103,7 +57,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] @@ -116,42 +70,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "fastrand" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77b705829d1e87f762c2df6da140b26af5839e1033aa84aa5f56bb688e4e1bdb" -dependencies = [ - "instant", -] - -[[package]] -name = "futures-core" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "098cd1c6dda6ca01650f1a37a794245eb73181d0d4d4e955e2f3c37db7af1815" - -[[package]] -name = "futures-io" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "365a1a1fb30ea1c03a830fdb2158f5236833ac81fa0ad12fe35b29cddc35cb04" - -[[package]] -name = "futures-lite" -version = "1.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4481d0cd0de1d204a4fa55e7d45f07b1d958abcb06714b3446438e2eff695fb" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "image" version = "0.23.14" @@ -167,45 +85,12 @@ dependencies = [ "png", ] -[[package]] -name = "instant" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" -dependencies = [ - "cfg-if 1.0.0", -] - [[package]] name = "itoa" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" - -[[package]] -name = "memchr" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" - -[[package]] -name = "memory_units" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" - [[package]] name = "miniz_oxide" version = "0.3.7" @@ -256,24 +141,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "once_cell" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" - -[[package]] -name = "parking" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" - -[[package]] -name = "pin-project-lite" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" - [[package]] name = "plugin_api" version = "0.1.0" @@ -348,12 +215,6 @@ dependencies = [ "serde", ] -[[package]] -name = "slab" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" - [[package]] name = "syn" version = "1.0.70" @@ -371,12 +232,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" -[[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - [[package]] name = "wasm_plugin_guest" version = "0.1.4" @@ -398,37 +253,3 @@ dependencies = [ "quote", "syn", ] - -[[package]] -name = "wee_alloc" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" -dependencies = [ - "cfg-if 0.1.10", - "libc", - "memory_units", - "winapi", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/core_plugin/Cargo.toml b/core_plugin/Cargo.toml index 3ac0e7f..a23ac8e 100644 --- a/core_plugin/Cargo.toml +++ b/core_plugin/Cargo.toml @@ -11,8 +11,3 @@ edition = "2018" plugin_api = { path = "../plugin_api" } wasm_plugin_guest = { version = "0.1.4", default-features = false, features = ["serialize_json"] } image = { version = "0.23", default-features = false, features = ["png"] } -wee_alloc = "0.4.5" -once_cell = "1.7.2" -futures-lite = "1.11.3" -async-executor = "1.4.1" -lazy_static = "1.4.0" diff --git a/core_plugin/src/lib.rs b/core_plugin/src/lib.rs index 3ded5a4..ef5afc6 100644 --- a/core_plugin/src/lib.rs +++ b/core_plugin/src/lib.rs @@ -1,14 +1,8 @@ use std::{ - future::Future, - sync::Arc, - cell::{RefCell, Cell}, + cell::RefCell, io::Cursor, collections::HashMap, - pin::Pin, - task::{Context, Poll}, }; -use async_executor::{LocalExecutor, Task}; -use futures_lite::future; use plugin_api::{ItemType, ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId, ItemDescription, Rect, ItemInstanceId, import_game_api}; @@ -34,8 +28,8 @@ pub const GUN_THROWBACK: f32 = 700.0; import_game_api!(); pub enum ItemState { - Gun(ItemCoroutine, GunState), - Sword(ItemCoroutine), + Gun(GunState), + Sword(SwordState), } #[wasm_plugin_guest::export_function] @@ -149,8 +143,8 @@ fn plugin_description() -> PluginDescription { #[wasm_plugin_guest::export_function] fn new_instance(item_type: ItemType, item_id: ItemInstanceId) { let state = match item_type { - GUN => ItemState::Gun(ItemCoroutine::default(), GunState::default()), - SWORD => ItemState::Sword(ItemCoroutine::default()), + GUN => ItemState::Gun(GunState::default()), + SWORD => ItemState::Sword(SwordState::default()), _ => panic!() }; @@ -165,7 +159,7 @@ fn destroy_instance(item_id: ItemInstanceId) { #[wasm_plugin_guest::export_function] fn uses_remaining(item_id: ItemInstanceId) -> Option<(u32, u32)> { ITEMS.with(|items| { - if let Some(ItemState::Gun(_, state)) = items.borrow_mut().get(&item_id) { + if let Some(ItemState::Gun(state)) = items.borrow_mut().get(&item_id) { Some((state.ammo, 3)) } else { None @@ -174,22 +168,46 @@ fn uses_remaining(item_id: ItemInstanceId) -> Option<(u32, u32)> { } #[wasm_plugin_guest::export_function] -fn update_shoot(item_id: ItemInstanceId, delta_time: f32) -> bool { +fn update_shoot(item_id: ItemInstanceId, current_time: f64) -> bool { ITEMS.with(|items| { if let Some(item) = items.borrow_mut().get_mut(&item_id) { match item { - ItemState::Gun(coroutine, state) => { - if !coroutine.started() { + ItemState::Gun(state) => { + if let Some(time) = state.recovery_time { + if time <= current_time { + set_sprite_animation(0); + set_sprite_fx(false); + state.recovery_time.take(); + true + } else { + false + } + } else { state.ammo -= 1; - coroutine.start(gun_coroutine); + spawn_bullet(); + set_sprite_fx(true); + let mut speed = get_speed(); + speed[0] -= GUN_THROWBACK * facing_dir(); + set_speed(speed); + set_sprite_animation(1); + state.recovery_time = Some(current_time + 0.08 * 3.0); + false } - coroutine.step(delta_time) }, - ItemState::Sword(coroutine) => { - if !coroutine.started() { - coroutine.start(sword_coroutine); + ItemState::Sword(state) => { + if let Some(time) = state.recovery_time { + if time <= current_time { + set_sprite_animation(0); + state.recovery_time.take(); + true + } else { + false + } + } else { + set_sprite_animation(1); + state.recovery_time = Some(current_time + 0.08 * 3.0); + false } - coroutine.step(delta_time) }, } } else { @@ -198,138 +216,21 @@ fn update_shoot(item_id: ItemInstanceId, delta_time: f32) -> bool { }) } -pub struct ItemCoroutine { - executor: LocalExecutor<'static>, - timer: Timer, - coroutine: Option>, -} - -impl Default for ItemCoroutine { - fn default() -> Self { - Self { - executor: LocalExecutor::new(), - timer: Timer::default(), - coroutine: None, - } - } -} - -impl ItemCoroutine { - fn step(&mut self, delta_time: f32) -> bool { - self.timer.incr_time(delta_time); - debug_print(format!("poop {}", self.timer.time.get())); - if self.coroutine.is_none() { - return true; - } - let r = self.executor.try_tick(); - debug_print(format!("poop {} {}", self.executor.is_empty(), r)); - if self.executor.is_empty() { - self.coroutine.take(); - true - } else { - false - } - } - - fn started(&self) -> bool { - self.coroutine.is_some() - } - - fn start(&mut self, f: F) - where - Fu: Future, - F: Fn(Timer) -> Fu + 'static - { - let timer = self.timer.clone(); - self.coroutine = Some(self.executor.spawn(async move { - f(timer).await - })); - } -} - pub struct GunState { + recovery_time: Option, ammo: u32, } impl Default for GunState { fn default() -> Self { Self { + recovery_time: None, ammo: 3, } } } -#[derive(Clone, Default)] -struct Timer { - time: Arc>, -} - -impl Timer { - async fn wait_seconds(&self, seconds: f32) { - let timer = self.clone(); - let start_time = timer.time.get(); - future::poll_fn(move |_cx| { - if timer.time.get() - start_time >= seconds { - Poll::Ready(()) - } else { - Poll::Pending - } - }).await; - } - - fn incr_time(&self, delta: f32) { - let current_time = self.time.get(); - self.time.set(current_time + delta); - } -} - -pub struct TimerDelayFuture { - start_time: f32, - time: f32, - timer: Timer, -} -impl Unpin for TimerDelayFuture {} - -impl Future for TimerDelayFuture { - type Output = Option<()>; - - fn poll(self: Pin<&mut Self>, _: &mut Context) -> Poll { - debug_print(format!("{} {} {}", self.timer.time.get(), self.start_time, self.time)); - if self.timer.time.get() - self.start_time >= self.time { - Poll::Ready(Some(())) - } else { - //Poll::Pending - Poll::Ready(Some(())) - } - } -} - -async fn gun_coroutine(timer: Timer) { - spawn_bullet(); - set_sprite_fx(true); - let mut speed = get_speed(); - speed[0] -= GUN_THROWBACK * facing_dir(); - set_speed(speed); - set_sprite_animation(1); - - for i in 0u32..3 { - set_sprite_frame(i); - set_fx_sprite_frame(i); - debug_print("gun pre wait".to_string()); - timer.wait_seconds(0.08).await; - debug_print("gun post wait".to_string()); - } - set_sprite_animation(0); - set_sprite_fx(false); - debug_print("gun done".to_string()); -} - -async fn sword_coroutine(timer: Timer) { - set_sprite_animation(1); - for i in 0u32..3 { - set_sprite_frame(i); - timer.wait_seconds(0.08).await; - } - set_sprite_animation(0); - debug_print("sword done".to_string()); +#[derive(Default)] +pub struct SwordState { + recovery_time: Option, } diff --git a/src/nodes/item.rs b/src/nodes/item.rs index ba1cde7..525cf44 100644 --- a/src/nodes/item.rs +++ b/src/nodes/item.rs @@ -69,7 +69,7 @@ impl ItemImplementation { wasm_plugin } = p; game_api.with_current_player(player, || { - wasm_plugin.call_function_with_argument("update_shoot", &(item_id, get_frame_time())).unwrap() + wasm_plugin.call_function_with_argument("update_shoot", &(item_id, get_time())).unwrap() }) }) } From 4122da8f6ca07225d29ab446483cfa2b3f60de92 Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Thu, 29 Apr 2021 12:37:54 -0700 Subject: [PATCH 10/14] remote player handling --- core_plugin/Cargo.lock | 7 ++ core_plugin/Cargo.toml | 1 + core_plugin/src/lib.rs | 140 ++++++++++++++--------- plugin_api/src/lib.rs | 5 +- src/nodes/item.rs | 14 ++- src/nodes/nakama/nakama_realtime_game.rs | 22 ++-- src/nodes/pickup.rs | 2 +- src/nodes/player.rs | 23 ++-- src/nodes/remote_player.rs | 26 ++++- src/plugin.rs | 140 +++++++++++++++++++---- 10 files changed, 277 insertions(+), 103 deletions(-) diff --git a/core_plugin/Cargo.lock b/core_plugin/Cargo.lock index c658346..ffe98c1 100644 --- a/core_plugin/Cargo.lock +++ b/core_plugin/Cargo.lock @@ -47,6 +47,7 @@ name = "core_plugin" version = "0.1.0" dependencies = [ "image", + "lazy_static", "plugin_api", "wasm_plugin_guest", ] @@ -91,6 +92,12 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "miniz_oxide" version = "0.3.7" diff --git a/core_plugin/Cargo.toml b/core_plugin/Cargo.toml index a23ac8e..09cf787 100644 --- a/core_plugin/Cargo.toml +++ b/core_plugin/Cargo.toml @@ -11,3 +11,4 @@ edition = "2018" plugin_api = { path = "../plugin_api" } wasm_plugin_guest = { version = "0.1.4", default-features = false, features = ["serialize_json"] } image = { version = "0.23", default-features = false, features = ["png"] } +lazy_static = "1.4.0" diff --git a/core_plugin/src/lib.rs b/core_plugin/src/lib.rs index ef5afc6..69ad316 100644 --- a/core_plugin/src/lib.rs +++ b/core_plugin/src/lib.rs @@ -1,5 +1,5 @@ use std::{ - cell::RefCell, + sync::Mutex, io::Cursor, collections::HashMap, }; @@ -7,17 +7,8 @@ use std::{ use plugin_api::{ItemType, ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId, ItemDescription, Rect, ItemInstanceId, import_game_api}; -// The Mutex here is really not necessary since this is guaranteed to be a single -// threaded environment, I'm just avoiding writing unsafe blocks. A library for -// writing these plugins could probably include a more efficient state store -// that takes advantage of the single threadedness. -// -// An alternative design would be for the new_instance function to allocate state on -// the heap and return a pointer which would then get passed back in when other -// functions are called. I think I like having the non-pointer key and letting the -// plugin interpret that however it sees fit but you could argue for the other version. -thread_local! { - pub static ITEMS:RefCell> = Default::default(); +lazy_static::lazy_static! { + static ref ITEMS:Mutex> = Default::default(); } const GUN: ItemType = ItemType::new(9868317461196439167); @@ -148,72 +139,109 @@ fn new_instance(item_type: ItemType, item_id: ItemInstanceId) { _ => panic!() }; - ITEMS.with(|items| items.borrow_mut().insert(item_id, state)); + ITEMS.lock().unwrap().insert(item_id, state); } #[wasm_plugin_guest::export_function] fn destroy_instance(item_id: ItemInstanceId) { - ITEMS.with(|items| items.borrow_mut().remove(&item_id)); + ITEMS.lock().unwrap().remove(&item_id); } #[wasm_plugin_guest::export_function] fn uses_remaining(item_id: ItemInstanceId) -> Option<(u32, u32)> { - ITEMS.with(|items| { - if let Some(ItemState::Gun(state)) = items.borrow_mut().get(&item_id) { - Some((state.ammo, 3)) - } else { - None - } - }) + if let Some(ItemState::Gun(state)) = ITEMS.lock().unwrap().get(&item_id) { + Some((state.ammo, 3)) + } else { + None + } } #[wasm_plugin_guest::export_function] fn update_shoot(item_id: ItemInstanceId, current_time: f64) -> bool { - ITEMS.with(|items| { - if let Some(item) = items.borrow_mut().get_mut(&item_id) { - match item { - ItemState::Gun(state) => { - if let Some(time) = state.recovery_time { - if time <= current_time { - set_sprite_animation(0); - set_sprite_fx(false); - state.recovery_time.take(); - true - } else { - false + if let Some(item) = ITEMS.lock().unwrap().get_mut(&item_id) { + match item { + ItemState::Gun(state) => { + if let Some(time) = state.recovery_time { + if time <= current_time { + set_sprite_animation(0); + set_sprite_fx(false); + state.recovery_time.take(); + if state.ammo == 0 { + disarm(); } + true } else { - state.ammo -= 1; - spawn_bullet(); - set_sprite_fx(true); - let mut speed = get_speed(); - speed[0] -= GUN_THROWBACK * facing_dir(); - set_speed(speed); - set_sprite_animation(1); - state.recovery_time = Some(current_time + 0.08 * 3.0); false } - }, - ItemState::Sword(state) => { - if let Some(time) = state.recovery_time { - if time <= current_time { - set_sprite_animation(0); - state.recovery_time.take(); - true + } else { + state.ammo -= 1; + spawn_bullet(); + nakama_shoot(); + set_sprite_fx(true); + let mut speed = get_speed(); + speed[0] -= GUN_THROWBACK * facing_dir(); + set_speed(speed); + set_sprite_animation(1); + state.recovery_time = Some(current_time + 0.08 * 3.0); + false + } + }, + ItemState::Sword(state) => { + if let Some(time) = state.recovery_time { + if time <= current_time { + set_sprite_animation(0); + state.recovery_time.take(); + true + } else { + nakama_shoot(); + let pos = position(); + let sword_hit_box = if facing_dir() > 0.0 { + [pos[0] + 35., pos[1] - 5., 40., 60.] } else { - false - } + [pos[0] - 50., pos[1] - 5., 40., 60.] + }; + hit_rect(sword_hit_box); + false + } + } else { + set_sprite_animation(1); + state.recovery_time = Some(current_time + 0.08 * 3.0); + false + } + }, + } + } else { + true + } +} + +#[wasm_plugin_guest::export_function] +fn update_remote_shoot(item_id: ItemInstanceId, current_time: f64) -> bool { + if let Some(item) = ITEMS.lock().unwrap().get_mut(&item_id) { + match item { + ItemState::Gun(_) => { + spawn_bullet(); + true + }, + ItemState::Sword(state) => { + if let Some(time) = state.recovery_time { + if time <= current_time { + set_sprite_animation(0); + state.recovery_time.take(); + true } else { - set_sprite_animation(1); - state.recovery_time = Some(current_time + 0.08 * 3.0); false } - }, + } else { + set_sprite_animation(1); + state.recovery_time = Some(current_time + 0.08 * 3.0); + false + } } - } else { - true } - }) + } else { + true + } } pub struct GunState { diff --git a/plugin_api/src/lib.rs b/plugin_api/src/lib.rs index aa5876e..bc3895e 100644 --- a/plugin_api/src/lib.rs +++ b/plugin_api/src/lib.rs @@ -101,15 +101,18 @@ macro_rules! import_game_api { () => { wasm_plugin_guest::import_functions! { fn spawn_bullet(); + fn hit_rect(rect: [f32; 4]) -> u32; fn set_sprite_fx(s: bool); fn get_speed() -> [f32; 2]; fn set_speed(speed: [f32; 2]); fn facing_dir() -> f32; + fn position() -> [f32; 2]; fn set_sprite_animation(animation: u32); fn set_fx_sprite_animation(animation: u32); fn set_sprite_frame(frame: u32); fn set_fx_sprite_frame(frame: u32); - fn clear_weapon(); + fn disarm(); + fn nakama_shoot(); fn debug_print(message: String); } }; diff --git a/src/nodes/item.rs b/src/nodes/item.rs index 525cf44..f4d0551 100644 --- a/src/nodes/item.rs +++ b/src/nodes/item.rs @@ -10,7 +10,7 @@ use macroquad::{ }; use plugin_api::{ItemType, PluginId, ItemDescription, ItemInstanceId}; -use crate::{nodes::Player, plugin::{image_from_desc, animated_sprite_from_desc, PluginRegistry, Plugin}}; +use crate::{nodes::{Player, RemotePlayer}, plugin::{image_from_desc, animated_sprite_from_desc, PluginRegistry, Plugin}}; pub(crate) struct ItemImplementation { @@ -73,6 +73,18 @@ impl ItemImplementation { }) }) } + + pub(crate) fn update_remote_shoot(&self, item_id: ItemInstanceId, player: Handle) -> bool { + self.with_plugin(|p| { + let Plugin { + game_api, + wasm_plugin + } = p; + game_api.with_remote_player(player, || { + wasm_plugin.call_function_with_argument("update_remote_shoot", &(item_id, get_time())).unwrap() + }) + }) + } } #[derive(Default)] diff --git a/src/nodes/nakama/nakama_realtime_game.rs b/src/nodes/nakama/nakama_realtime_game.rs index cb75420..7fbdf85 100644 --- a/src/nodes/nakama/nakama_realtime_game.rs +++ b/src/nodes/nakama/nakama_realtime_game.rs @@ -20,13 +20,13 @@ use crate::{ use plugin_api::ItemType; struct NetworkCache { - sent_position: [u8; 4], + sent_position: [u8; 11], last_send_time: f64, } impl NetworkCache { fn flush(&mut self) { - self.sent_position = [0; 4]; + self.sent_position = [0; 11]; self.last_send_time = 0.0; } } @@ -39,34 +39,34 @@ bitfield::bitfield! { y, set_y: 19, 10; facing, set_facing: 20; shooting, set_shooting: 21; - u64, weapon, set_weapon: 30, 22; - dead, set_dead: 31; + u64, weapon, set_weapon: 85, 22; + dead, set_dead: 86; } #[test] fn test_bitfield() { - let mut bits = PlayerStateBits([0; 4]); + let mut bits = PlayerStateBits([0; 11]); bits.set_x(345); bits.set_y(567); bits.set_facing(true); bits.set_shooting(false); - bits.set_weapon(66); + bits.set_weapon(11527428624421318257); assert_eq!(bits.x(), 345); assert_eq!(bits.y(), 567); assert_eq!(bits.facing(), true); assert_eq!(bits.shooting(), false); - assert_eq!(bits.weapon(), 66); + assert_eq!(bits.weapon(), 11527428624421318257); assert_eq!(bits.dead(), false); - assert_eq!(std::mem::size_of_val(&bits), 4); + assert_eq!(std::mem::size_of_val(&bits), 11); } mod message { use nanoserde::{DeBin, SerBin}; #[derive(Debug, Clone, SerBin, DeBin, PartialEq)] - pub struct State(pub [u8; 4]); + pub struct State(pub [u8; 11]); impl State { pub const OPCODE: i32 = 1; } @@ -143,7 +143,7 @@ impl NakamaRealtimeGame { NakamaRealtimeGame { game_type, network_cache: NetworkCache { - sent_position: [0; 4], + sent_position: [0; 11], last_send_time: 0.0, }, remote_players: BTreeMap::new(), @@ -316,7 +316,7 @@ impl Node for NakamaRealtimeGame { node.network_cache.last_send_time = get_time(); - let mut state = PlayerStateBits([0; 4]); + let mut state = PlayerStateBits([0; 11]); state.set_x(player.pos().x as u32); state.set_y(player.pos().y as u32); diff --git a/src/nodes/pickup.rs b/src/nodes/pickup.rs index c3029d8..19a47b9 100644 --- a/src/nodes/pickup.rs +++ b/src/nodes/pickup.rs @@ -75,7 +75,7 @@ impl scene::Node for Pickup { ); let item_registry = storage::get::(); - let item_impl = item_registry.get_implementation(node.item_type).expect("Invalid ItemType"); + let item_impl = item_registry.get_implementation(node.item_type).expect(&format!("Invalid ItemType: {:?}", node.item_type)); //TODO: position sprite draw_texture_ex( diff --git a/src/nodes/player.rs b/src/nodes/player.rs index ae1a6b0..29f4e0f 100644 --- a/src/nodes/player.rs +++ b/src/nodes/player.rs @@ -31,7 +31,7 @@ pub struct Input { pub struct Weapon { pub item_type: ItemType, - item_id: ItemInstanceId, + pub item_id: ItemInstanceId, texture: Texture2D, mount_pos_right: Vec2, mount_pos_left: Vec2, @@ -43,7 +43,7 @@ pub struct Weapon { impl Drop for Weapon { fn drop(&mut self) { let item_registry = storage::get::(); - let item_impl = item_registry.get_implementation(self.item_type).expect("Invalid ItemType"); + let item_impl = item_registry.get_implementation(self.item_type).expect(&format!("Invalid ItemType: {:?}", self.item_type)); item_impl.destroy(self.item_id); } } @@ -52,7 +52,7 @@ impl Weapon { fn new(item_type: ItemType) -> Self { let mut item_id_source = storage::get_mut::(); let item_registry = storage::get::(); - let item_impl = item_registry.get_implementation(item_type).expect("Invalid ItemType"); + let item_impl = item_registry.get_implementation(item_type).expect(&format!("Invalid ItemType: {:?}", item_type)); let instance_id = item_id_source.next_id(); item_impl.construct(instance_id); Self { @@ -84,6 +84,7 @@ pub struct Fish { dead: bool, pub facing: bool, pub weapon: Option, + discarded_weapon: Option, input: Input, } @@ -132,6 +133,7 @@ impl Fish { speed: vec2(0., 0.), facing: true, weapon: None, + discarded_weapon: None, input: Default::default(), } } @@ -157,7 +159,11 @@ impl Fish { } pub fn disarm(&mut self) { - self.weapon = None; + // This juggle is necessary because the plugins need to clean up + // their internal state when a weapon is discarded but they aren't + // currently callable recursively so the actual weapon cleanup must + // be defered until there's no call to the plugin happening. + self.discarded_weapon = self.weapon.take(); } pub fn pick_weapon(&mut self, item_type: ItemType) { @@ -258,7 +264,7 @@ pub struct Player { state_machine: StateMachine>, leaderboard_written: bool, nakama: Handle, - nakama_realtime: Handle, + pub nakama_realtime: Handle, } impl Player { @@ -422,9 +428,6 @@ impl Player { let done = implementation.update_shoot(item_id, handle); if done { let node = &mut *scene::get_node(handle); - if let Some((0, _)) = implementation.uses_remaining(item_id) { - node.fish.weapon.take(); - } node.state_machine.set_state(Self::ST_NORMAL); break } @@ -552,6 +555,10 @@ impl scene::Node for Player { } fn update(mut node: RefMut) { + // finalize any disarmament that happened in the previous frame + // here where it's safe to call the plugin's item destructor. + node.fish.discarded_weapon.take(); + let game_started = scene::find_node_by_type::() .unwrap() .game_started(); diff --git a/src/nodes/remote_player.rs b/src/nodes/remote_player.rs index 9fc8a76..38105d1 100644 --- a/src/nodes/remote_player.rs +++ b/src/nodes/remote_player.rs @@ -9,7 +9,8 @@ use macroquad::{ use crate::{ nodes::{ - player::{Fish, Weapon}, + player::Fish, + item::ItemImplementationRegistry, }, Resources, }; @@ -18,7 +19,7 @@ use plugin_api::ItemType; pub struct RemotePlayer { pub username: String, pub id: String, - fish: Fish, + pub fish: Fish, pub dead: bool, pub ready: bool, @@ -72,7 +73,26 @@ impl RemotePlayer { } pub fn shoot(&mut self, handle: Handle) { - todo!("remote weapon handling"); + let coroutine = async move { + loop { + let mut item = None; + if let Some(weapon) = &scene::get_node(handle).fish.weapon { + item = Some((weapon.item_type, weapon.item_id)); + } + if let Some((item_type, item_id)) = item { + let item_registry = storage::get::(); + let implementation = item_registry.get_implementation(item_type).unwrap(); + let done = implementation.update_remote_shoot(item_id, handle); + if done { + break + } + } else { + break + } + wait_seconds(0.005).await; + } + }; + start_coroutine(coroutine); } } impl scene::Node for RemotePlayer { diff --git a/src/plugin.rs b/src/plugin.rs index b41de4e..2bf80dc 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -5,6 +5,7 @@ use std::{ }; use macroquad::{ + prelude::*, texture::Image, experimental::{ scene::{self, Handle, RefMut}, @@ -15,7 +16,7 @@ use macroquad::{ use wasm_plugin_host::WasmPlugin; use plugin_api::{ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId}; -use crate::nodes::{ItemImplementationRegistry, Player}; +use crate::nodes::{ItemImplementationRegistry, Player, RemotePlayer, Fish}; pub fn image_from_desc(desc: ImageDescription) -> Image { @@ -54,10 +55,37 @@ pub struct Plugin { #[derive(Default, Clone)] pub struct GameApi { current_player: Arc>>>, + current_remote_player: Arc>>>, } unsafe impl Send for GameApi {} unsafe impl Sync for GameApi {} +enum LocalOrRemotePlayer { + Local(RefMut), + Remote(RefMut) +} +struct FishMut { + node: LocalOrRemotePlayer +} +impl std::ops::Deref for FishMut { + type Target = Fish; + + fn deref(&self) -> &Self::Target { + match &self.node { + LocalOrRemotePlayer::Local(player) => &player.fish, + LocalOrRemotePlayer::Remote(player) => &player.fish, + } + } +} +impl std::ops::DerefMut for FishMut { + fn deref_mut(&mut self) -> &mut Self::Target { + match &mut self.node { + LocalOrRemotePlayer::Local(player) => &mut player.fish, + LocalOrRemotePlayer::Remote(player) => &mut player.fish, + } + } +} + impl GameApi { pub fn with_current_player(&self, player: Handle, mut f: impl FnMut() -> R) -> R { self.current_player.lock().unwrap().replace(player); @@ -66,44 +94,88 @@ impl GameApi { result } - fn current_player(&self) -> Handle { - self.current_player.lock().unwrap().unwrap().clone() + pub fn with_remote_player(&self, player: Handle, mut f: impl FnMut() -> R) -> R { + self.current_remote_player.lock().unwrap().replace(player); + let result = f(); + self.current_remote_player.lock().unwrap().take(); + result + } + + fn current_player(&self) -> Option> { + self.current_player.lock().unwrap().map(|p| p.clone()) + } + + fn current_remote_player(&self) -> Option> { + self.current_remote_player.lock().unwrap().map(|p| p.clone()) + } + + fn current_fish(&self) -> FishMut { + if let Some(handle) = self.current_player() { + let node = scene::get_node(handle); + FishMut { + node: LocalOrRemotePlayer::Local(node) + } + } else { + let node = scene::get_node(self.current_remote_player().unwrap()); + FishMut { + node: LocalOrRemotePlayer::Remote(node) + } + } } fn spawn_bullet(&self) { - let node = &mut *scene::get_node(self.current_player()); let mut bullets = scene::find_node_by_type::().unwrap(); - bullets.spawn_bullet(node.fish.pos, node.fish.facing); + let fish = self.current_fish(); + bullets.spawn_bullet(fish.pos, fish.facing); + } + + fn hit_rect(&self, rect: [f32; 4]) -> u32 { + if let Some(handle) = self.current_player() { + let hit_box = Rect::new(rect[0], rect[1], rect[2], rect[3]); + let node = &mut *scene::get_node(handle); + let others = scene::find_nodes_by_type::(); + let mut hit_count = 0; + for player in others { + if Rect::new(player.pos().x, player.pos().y, 20., 64.).overlaps(&hit_box) { + hit_count += 1; + let mut net = scene::get_node(node.nakama_realtime); + net.kill(&player.id, !node.fish.facing); + } + } + hit_count + } else { + 0 + } } fn set_sprite_fx(&self, s: bool) { - let node = &mut *scene::get_node(self.current_player()); - if let Some(weapon) = &mut node.fish.weapon { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { weapon.fx = s; } } fn get_speed(&self) -> [f32; 2] { - let node = &mut *scene::get_node(self.current_player()); - [node.fish.speed.x, node.fish.speed.y] + let fish = self.current_fish(); + [fish.speed.x, fish.speed.y] } fn set_speed(&self, speed: [f32; 2]) { - let node = &mut *scene::get_node(self.current_player()); - node.fish.speed.x = speed[0]; - node.fish.speed.y = speed[1]; + let mut fish = self.current_fish(); + fish.speed.x = speed[0]; + fish.speed.y = speed[1]; } fn set_sprite_animation(&self, animation: usize) { - let node = &mut *scene::get_node(self.current_player()); - if let Some(weapon) = &mut node.fish.weapon { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { weapon.sprite.set_animation(animation); } } fn set_fx_sprite_animation(&self, animation: usize) { - let node = &mut *scene::get_node(self.current_player()); - if let Some(weapon) = &mut node.fish.weapon { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { if let Some(sprite) = &mut weapon.fx_sprite { sprite.set_animation(animation); } @@ -111,15 +183,15 @@ impl GameApi { } fn set_sprite_frame(&self, frame: u32) { - let node = &mut *scene::get_node(self.current_player()); - if let Some(weapon) = &mut node.fish.weapon { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { weapon.sprite.set_frame(frame); } } fn set_fx_sprite_frame(&self, frame: u32) { - let node = &mut *scene::get_node(self.current_player()); - if let Some(weapon) = &mut node.fish.weapon { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { if let Some(sprite) = &mut weapon.fx_sprite { sprite.set_frame(frame); } @@ -127,8 +199,27 @@ impl GameApi { } fn facing_dir(&self) -> f32 { - let node = &mut *scene::get_node(self.current_player()); - node.fish.facing_dir() + let fish = self.current_fish(); + fish.facing_dir() + } + + fn position(&self) -> [f32; 2] { + let fish = self.current_fish(); + let pos = fish.pos(); + [pos.x, pos.y] + } + + fn nakama_shoot(&self) { + if let Some(handle) = self.current_player() { + let node = &mut *scene::get_node(handle); + let mut nakama = scene::get_node(node.nakama_realtime); + nakama.shoot(); + } + } + + fn disarm(&self) { + let mut fish = self.current_fish(); + fish.disarm(); } fn debug_print(&self, message: String) { @@ -144,7 +235,9 @@ impl PluginRegistry { if entry.path().to_str().unwrap().contains(".wasm") { let game_api = GameApi::default(); let mut builder = wasm_plugin_host::WasmPluginBuilder::from_file(entry.path()).expect(&format!("Failed to load plugin {:?}", entry.path())); + // TODO: This should probably be a macro or something to reduce boilerplate builder = builder.import_function_with_context("spawn_bullet", game_api.clone(), |ctx: &GameApi| { ctx.spawn_bullet(); }); + builder = builder.import_function_with_context("hit_rect", game_api.clone(), |ctx: &GameApi, rect: [f32; 4]| { ctx.hit_rect(rect) }); builder = builder.import_function_with_context("set_sprite_fx", game_api.clone(), |ctx: &GameApi, s: bool| { ctx.set_sprite_fx(s); }); builder = builder.import_function_with_context("get_speed", game_api.clone(), |ctx: &GameApi| { ctx.get_speed() }); builder = builder.import_function_with_context("set_speed", game_api.clone(), |ctx: &GameApi, s: [f32; 2]| { ctx.set_speed(s); }); @@ -153,6 +246,9 @@ impl PluginRegistry { builder = builder.import_function_with_context("set_sprite_frame", game_api.clone(), |ctx: &GameApi, frame: u32| { ctx.set_sprite_frame(frame); }); builder = builder.import_function_with_context("set_fx_sprite_frame", game_api.clone(), |ctx: &GameApi, frame: u32| { ctx.set_fx_sprite_frame(frame); }); builder = builder.import_function_with_context("facing_dir", game_api.clone(), |ctx: &GameApi| { ctx.facing_dir() }); + builder = builder.import_function_with_context("position", game_api.clone(), |ctx: &GameApi| { ctx.position() }); + builder = builder.import_function_with_context("disarm", game_api.clone(), |ctx: &GameApi| { ctx.disarm() }); + builder = builder.import_function_with_context("nakama_shoot", game_api.clone(), |ctx: &GameApi| { ctx.nakama_shoot() }); builder = builder.import_function_with_context("debug_print", game_api.clone(), |ctx: &GameApi, message: String| { ctx.debug_print(message) }); let mut plugin = builder .finish() From c7beb6c2d4860a05c820d59d810ab0ce3ef59615 Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Sat, 1 May 2021 13:20:29 -0700 Subject: [PATCH 11/14] Update wasm_plugin --- Cargo.lock | 74 ++++---- Cargo.toml | 2 +- core_plugin/Cargo.lock | 23 ++- core_plugin/Cargo.toml | 2 +- core_plugin/src/lib.rs | 16 +- plugin_api/src/lib.rs | 8 + src/gui.rs | 4 +- src/main.rs | 8 +- src/nodes.rs | 6 +- src/nodes/global_events.rs | 2 +- src/nodes/item.rs | 66 +++++--- src/nodes/nakama/nakama_realtime_game.rs | 7 +- src/nodes/pickup.rs | 6 +- src/nodes/player.rs | 26 +-- src/nodes/remote_player.rs | 43 +++-- src/plugin.rs | 204 ++++++++++++++++++----- 16 files changed, 333 insertions(+), 164 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5de67c6..129cafb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,11 +57,12 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "backtrace" -version = "0.3.57" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ed203b9ba68b242c62b3fb7480f589dd49829be1edb3fe8fc8b4ffda2dcb8d" +checksum = "88fb5a785d6b44fd9d6700935608639af1b8356de1e55d5f7c2740f4faa15d82" dependencies = [ "addr2line", + "cc", "cfg-if 1.0.0", "libc", "miniz_oxide 0.4.4", @@ -365,9 +366,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12" +checksum = "52fb27eab85b17fbb9f6fd667089e07d6a2eb8743d02639ee7f6a7a7729c9c94" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -378,9 +379,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" +checksum = "4feb231f0d4d6af81aed15928e58ecf5816aa62a2393e2c82f46973e92a9a278" dependencies = [ "autocfg", "cfg-if 1.0.0", @@ -824,9 +825,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.93" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" +checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" [[package]] name = "libloading" @@ -850,9 +851,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3c91c24eae6777794bb1997ad98bbb87daf92890acab859f7eaa4320333176" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" dependencies = [ "scopeguard", ] @@ -877,9 +878,9 @@ dependencies = [ [[package]] name = "macroquad" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e9a227ffaf48bcb3022d0e1941fd0f4da4b49f0d05fd53ea252b2f024dca82" +checksum = "2d4b6dab140824bd6958aae0020011b30de49d82615ec60d2f3622d4dc1777ae" dependencies = [ "bumpalo", "fontdue", @@ -934,9 +935,9 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "memchr" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] name = "memmap2" @@ -1283,9 +1284,9 @@ checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" [[package]] name = "openssl" -version = "0.10.33" +version = "0.10.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577" +checksum = "6d7830286ad6a3973c0f1d9b73738f69c76b739301d0229c4b96501695cbe4c8" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -1297,9 +1298,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.61" +version = "0.9.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f" +checksum = "fa52160d45fa2e7608d504b7c3a3355afed615e6d8b627a74458634ba21b69bd" dependencies = [ "autocfg", "cc", @@ -1580,9 +1581,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041" +checksum = "85dd92e586f7355c633911e11f77f3d12f04b1b1bd76a198bd34ae3af8341ef2" dependencies = [ "bitflags", ] @@ -1600,18 +1601,18 @@ dependencies = [ [[package]] name = "regex" -version = "1.4.6" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759" +checksum = "1efb2352a0f4d4b128f734b5c44c79ff80117351138733f12f982fe3e2b13343" dependencies = [ "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.23" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" +checksum = "00efb87459ba4f6fb2169d20f68565555688e1250ee6825cdf6254f8b48fafb2" [[package]] name = "region" @@ -1878,9 +1879,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883" +checksum = "ad184cc9470f9117b2ac6817bfe297307418819ba40552f9b3846f05c33d5373" dependencies = [ "proc-macro2", "quote", @@ -1953,9 +1954,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" +checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", @@ -1976,9 +1977,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" dependencies = [ "lazy_static", ] @@ -2009,9 +2010,9 @@ dependencies = [ [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "untrusted" @@ -2021,9 +2022,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "ureq" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fbeb1aabb07378cf0e084971a74f24241273304653184f54cdce113c0d7df1b" +checksum = "2475a6781e9bc546e7b64f4013d2f4032c8c6a40fcffd7c6f4ee734a890972ab" dependencies = [ "base64", "chunked_transfer", @@ -2143,10 +2144,11 @@ checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" [[package]] name = "wasm_plugin_host" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4ca6c29b04b3b450b7800b878c4f80b3779a876974eba2d4fe7d4a734ef753" +checksum = "1e0e8c64adb96d595ada056e5229e653594dbaab81ce3f9b932cf0caade26bef" dependencies = [ + "bitfield", "serde", "serde_json", "wasmer", diff --git a/Cargo.toml b/Cargo.toml index 9fe6862..e184426 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,6 @@ macroquad-particles = {version = "0.1", features = ["nanoserde"] } bitfield = "0.13" nakama-rs = "0.1" base64 = "0.13" -wasm_plugin_host = { version = "0.1.5", default-features = false, features = ["serialize_json"] } +wasm_plugin_host = { version = "0.1.6", default-features = false, features = ["serialize_json"] } plugin_api = { path = "./plugin_api" } diff --git a/core_plugin/Cargo.lock b/core_plugin/Cargo.lock index ffe98c1..cfc5198 100644 --- a/core_plugin/Cargo.lock +++ b/core_plugin/Cargo.lock @@ -12,6 +12,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + [[package]] name = "bitflags" version = "1.2.1" @@ -224,9 +230,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883" +checksum = "ad184cc9470f9117b2ac6817bfe297307418819ba40552f9b3846f05c33d5373" dependencies = [ "proc-macro2", "quote", @@ -235,16 +241,17 @@ dependencies = [ [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "wasm_plugin_guest" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdd4d39f1d5a1fe6e7f9af1c26cda98ce5caf29c76de38c4348eea7d982ff90" +checksum = "85398177caedea857f8a881cafa0256101b261a65d03872e577fdb7bff16f89c" dependencies = [ + "bitfield", "serde", "serde_json", "wasm_plugin_guest_derive", @@ -252,9 +259,9 @@ dependencies = [ [[package]] name = "wasm_plugin_guest_derive" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "798077c1e20331f66c2d64d4ca503cc18c485d776bbd370ad43ab4641a45ffd0" +checksum = "8029abbe46726b92fcfb290819097e8ae205030d8b09cdada5be037476ad879a" dependencies = [ "proc-macro2", "quote", diff --git a/core_plugin/Cargo.toml b/core_plugin/Cargo.toml index 09cf787..e0996d8 100644 --- a/core_plugin/Cargo.toml +++ b/core_plugin/Cargo.toml @@ -9,6 +9,6 @@ edition = "2018" [dependencies] plugin_api = { path = "../plugin_api" } -wasm_plugin_guest = { version = "0.1.4", default-features = false, features = ["serialize_json"] } +wasm_plugin_guest = { version = "0.1.5", default-features = false, features = ["serialize_json"] } image = { version = "0.23", default-features = false, features = ["png"] } lazy_static = "1.4.0" diff --git a/core_plugin/src/lib.rs b/core_plugin/src/lib.rs index 69ad316..a029c5d 100644 --- a/core_plugin/src/lib.rs +++ b/core_plugin/src/lib.rs @@ -4,7 +4,7 @@ use std::{ collections::HashMap, }; -use plugin_api::{ItemType, ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId, ItemDescription, Rect, ItemInstanceId, import_game_api}; +use plugin_api::{ItemType, ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId, ItemDescription, Rect, ItemInstanceId, import_game_api, SoundDescription}; lazy_static::lazy_static! { @@ -38,6 +38,16 @@ fn plugin_description() -> PluginDescription { PluginDescription { plugin_id: PluginId::new(11229058760733382699), display_name: "basic weapons".to_string(), + sounds: vec![ + SoundDescription { + name: "sword".to_string(), + bytes: include_bytes!("../../assets/sounds/sword.wav").to_vec(), + }, + SoundDescription { + name: "shoot".to_string(), + bytes: include_bytes!("../../assets/sounds/shoot.ogg").to_vec(), + }, + ], items: vec![ ItemDescription { item_type: SWORD, @@ -175,6 +185,7 @@ fn update_shoot(item_id: ItemInstanceId, current_time: f64) -> bool { } } else { state.ammo -= 1; + play_sound_once("shoot".to_string()); spawn_bullet(); nakama_shoot(); set_sprite_fx(true); @@ -194,6 +205,7 @@ fn update_shoot(item_id: ItemInstanceId, current_time: f64) -> bool { true } else { nakama_shoot(); + play_sound_once("sword".to_string()); let pos = position(); let sword_hit_box = if facing_dir() > 0.0 { [pos[0] + 35., pos[1] - 5., 40., 60.] @@ -221,6 +233,7 @@ fn update_remote_shoot(item_id: ItemInstanceId, current_time: f64) -> bool { match item { ItemState::Gun(_) => { spawn_bullet(); + play_sound_once("shoot".to_string()); true }, ItemState::Sword(state) => { @@ -233,6 +246,7 @@ fn update_remote_shoot(item_id: ItemInstanceId, current_time: f64) -> bool { false } } else { + play_sound_once("sword".to_string()); set_sprite_animation(1); state.recovery_time = Some(current_time + 0.08 * 3.0); false diff --git a/plugin_api/src/lib.rs b/plugin_api/src/lib.rs index bc3895e..c14a35a 100644 --- a/plugin_api/src/lib.rs +++ b/plugin_api/src/lib.rs @@ -50,6 +50,7 @@ pub struct PluginDescription { pub plugin_id: PluginId, pub display_name: String, pub items: Vec, + pub sounds: Vec, } #[derive(Clone, Serialize, Deserialize)] @@ -73,6 +74,12 @@ pub struct Rect { pub h: f32 } +#[derive(Clone, Serialize, Deserialize)] +pub struct SoundDescription { + pub name: String, + pub bytes: Vec, +} + #[derive(Clone, Serialize, Deserialize)] pub struct ImageDescription { pub bytes: Vec, @@ -112,6 +119,7 @@ macro_rules! import_game_api { fn set_sprite_frame(frame: u32); fn set_fx_sprite_frame(frame: u32); fn disarm(); + fn play_sound_once(sound: String); fn nakama_shoot(); fn debug_print(message: String); } diff --git a/src/gui.rs b/src/gui.rs index a20eaf7..1a021a6 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -5,18 +5,18 @@ use macroquad::{ }; mod authentication; +mod credits; mod lobby; mod main_menu; mod style; mod waitscreen; -mod credits; pub use authentication::authentication; +pub use credits::credits; pub use lobby::matchmaking_lobby; pub use main_menu::main_menu; pub use style::GuiResources; pub use waitscreen::waitscreen; -pub use credits::credits; const WINDOW_WIDTH: f32 = 700.0; const WINDOW_HEIGHT: f32 = 300.0; diff --git a/src/main.rs b/src/main.rs index d82d3b6..65920be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,20 +14,20 @@ use macroquad::{ }; use nanoserde::DeJson; -use particles::EmittersCache; use macroquad_platformer::World as CollisionWorld; +use particles::EmittersCache; mod credentials { include!(concat!(env!("OUT_DIR"), "/nakama_credentials.rs")); } -mod nodes; mod gui; +mod nodes; mod plugin; use gui::Scene; -use plugin::PluginRegistry; use nodes::{ItemIdSource, ItemImplementationRegistry}; +use plugin::PluginRegistry; pub mod consts { pub const GRAVITY: f32 = 900.0; @@ -271,7 +271,7 @@ async fn network_game(nakama: Handle, game_type: GameType, networ let h = resources.tiled_map.raw_tiled_map.tileheight * resources.tiled_map.raw_tiled_map.height; let mut item_registry = ItemImplementationRegistry::default(); - let plugin_registry = PluginRegistry::load("plugins/", &mut item_registry); + let plugin_registry = PluginRegistry::load("plugins/", &mut item_registry).await; storage::store(item_registry); storage::store(plugin_registry); storage::store(ItemIdSource::default()); diff --git a/src/nodes.rs b/src/nodes.rs index f90c949..a694937 100644 --- a/src/nodes.rs +++ b/src/nodes.rs @@ -3,21 +3,21 @@ mod camera; mod decoration; mod fxses; mod global_events; +mod item; mod level_background; mod nakama; mod pickup; mod player; mod remote_player; -mod item; pub use bullets::Bullets; pub use camera::Camera; pub use decoration::Decoration; pub use fxses::Fxses; pub use global_events::GlobalEvents; +pub use item::{ItemIdSource, ItemImplementationRegistry}; pub use level_background::LevelBackground; pub use nakama::{Nakama, NakamaRealtimeGame}; pub use pickup::Pickup; -pub use player::{Player, Fish}; +pub use player::{Fish, Player}; pub use remote_player::RemotePlayer; -pub use item::{ItemIdSource, ItemImplementationRegistry}; diff --git a/src/nodes/global_events.rs b/src/nodes/global_events.rs index e8ecd86..67d0bfa 100644 --- a/src/nodes/global_events.rs +++ b/src/nodes/global_events.rs @@ -7,7 +7,7 @@ use macroquad::{ }; use crate::{ - nodes::{item::{ItemImplementationRegistry}, NakamaRealtimeGame, Pickup, Player, RemotePlayer}, + nodes::{item::ItemImplementationRegistry, NakamaRealtimeGame, Pickup, Player, RemotePlayer}, Resources, }; use plugin_api::ItemType; diff --git a/src/nodes/item.rs b/src/nodes/item.rs index f4d0551..ab73326 100644 --- a/src/nodes/item.rs +++ b/src/nodes/item.rs @@ -1,17 +1,15 @@ use std::collections::HashMap; use macroquad::{ + experimental::{animation::AnimatedSprite, collections::storage, scene::Handle}, prelude::*, - experimental::{ - scene::Handle, - collections::storage, - animation::AnimatedSprite, - }, }; -use plugin_api::{ItemType, PluginId, ItemDescription, ItemInstanceId}; -use crate::{nodes::{Player, RemotePlayer}, plugin::{image_from_desc, animated_sprite_from_desc, PluginRegistry, Plugin}}; - +use crate::{ + nodes::{Player, RemotePlayer}, + plugin::{animated_sprite_from_desc, image_from_desc, Plugin, PluginRegistry}, +}; +use plugin_api::{ItemDescription, ItemInstanceId, ItemType, PluginId}; pub(crate) struct ItemImplementation { display_name: String, @@ -34,54 +32,79 @@ impl ItemImplementation { display_name: description.display_name, item_type: description.item_type, texture, - mount_pos_right: vec2(description.mount_pos_right[0], description.mount_pos_right[1]), + mount_pos_right: vec2( + description.mount_pos_right[0], + description.mount_pos_right[1], + ), mount_pos_left: vec2(description.mount_pos_left[0], description.mount_pos_left[1]), pickup_src: Rect::new(pickup_src.x, pickup_src.y, pickup_src.w, pickup_src.h), pickup_dst: vec2(description.pickup_dst[0], description.pickup_dst[1]), sprite: animated_sprite_from_desc(description.sprite), fx_sprite: description.fx_sprite.map(|d| animated_sprite_from_desc(d)), - implementing_plugin: plugin + implementing_plugin: plugin, } } fn with_plugin(&self, mut f: impl FnMut(&mut Plugin) -> R) -> R { let mut plugin_registry = storage::get_mut::(); - let plugin = plugin_registry.get_plugin(self.implementing_plugin).unwrap(); + let plugin = plugin_registry + .get_plugin(self.implementing_plugin) + .unwrap(); f(plugin) } pub(crate) fn construct(&self, item_id: ItemInstanceId) { - self.with_plugin(|p| p.wasm_plugin.call_function_with_argument("new_instance", &(self.item_type, item_id)).unwrap()) + self.with_plugin(|p| { + p.wasm_plugin + .call_function_with_argument("new_instance", &(self.item_type, item_id)) + .unwrap() + }) } pub(crate) fn destroy(&self, item_id: ItemInstanceId) { - self.with_plugin(|p| p.wasm_plugin.call_function_with_argument("destroy_instance", &item_id).unwrap()) + self.with_plugin(|p| { + p.wasm_plugin + .call_function_with_argument("destroy_instance", &item_id) + .unwrap() + }) } pub(crate) fn uses_remaining(&self, item_id: ItemInstanceId) -> Option<(u32, u32)> { - self.with_plugin(|p| p.wasm_plugin.call_function_with_argument("uses_remaining", &item_id).unwrap()) + self.with_plugin(|p| { + p.wasm_plugin + .call_function_with_argument("uses_remaining", &item_id) + .unwrap() + }) } pub(crate) fn update_shoot(&self, item_id: ItemInstanceId, player: Handle) -> bool { self.with_plugin(|p| { let Plugin { game_api, - wasm_plugin + wasm_plugin, } = p; game_api.with_current_player(player, || { - wasm_plugin.call_function_with_argument("update_shoot", &(item_id, get_time())).unwrap() + wasm_plugin + .call_function_with_argument("update_shoot", &(item_id, get_time())) + .unwrap() }) }) } - pub(crate) fn update_remote_shoot(&self, item_id: ItemInstanceId, player: Handle) -> bool { + pub(crate) fn update_remote_shoot( + &self, + item_id: ItemInstanceId, + player: Handle, + ) -> bool { self.with_plugin(|p| { let Plugin { game_api, - wasm_plugin + wasm_plugin, } = p; game_api.with_remote_player(player, || { - wasm_plugin.call_function_with_argument("update_remote_shoot", &(item_id, get_time())).unwrap() + wasm_plugin + .call_function_with_argument("update_remote_shoot", &(item_id, get_time())) + .unwrap() }) }) } @@ -92,7 +115,10 @@ pub struct ItemImplementationRegistry(HashMap); impl ItemImplementationRegistry { pub(crate) fn add(&mut self, description: ItemDescription, implementing_plugin: PluginId) { - self.0.insert(description.item_type, ItemImplementation::from_description(description, implementing_plugin)); + self.0.insert( + description.item_type, + ItemImplementation::from_description(description, implementing_plugin), + ); } pub(crate) fn get_implementation(&self, item_type: ItemType) -> Option<&ItemImplementation> { diff --git a/src/nodes/nakama/nakama_realtime_game.rs b/src/nodes/nakama/nakama_realtime_game.rs index 7fbdf85..9142606 100644 --- a/src/nodes/nakama/nakama_realtime_game.rs +++ b/src/nodes/nakama/nakama_realtime_game.rs @@ -399,7 +399,7 @@ impl Node for NakamaRealtimeGame { resources.disarm_fxses.spawn(pos + vec2(16., 33.)); other.disarm(); } - if other.weapon().map_or(0, |weapon| weapon.into() ) + if other.weapon().map_or(0, |weapon| weapon.into()) != state.weapon() { other.pick_weapon(state.weapon().into()); @@ -432,10 +432,7 @@ impl Node for NakamaRealtimeGame { } = DeBin::deserialize_bin(&data).unwrap(); let pos = vec2(x as f32, y as f32); - let new_node = scene::add_node(Pickup::new( - pos, - item_type.into() - )); + let new_node = scene::add_node(Pickup::new(pos, item_type.into())); if let Some(pickup) = node.pickups.insert(id as _, new_node) { if let Some(node) = scene::try_get_node(pickup) { node.delete(); diff --git a/src/nodes/pickup.rs b/src/nodes/pickup.rs index 19a47b9..322dda9 100644 --- a/src/nodes/pickup.rs +++ b/src/nodes/pickup.rs @@ -7,7 +7,7 @@ use macroquad::{ prelude::*, }; -use crate::{Resources, nodes::item::{ItemImplementationRegistry}}; +use crate::{nodes::item::ItemImplementationRegistry, Resources}; use plugin_api::ItemType; pub struct Pickup { @@ -75,7 +75,9 @@ impl scene::Node for Pickup { ); let item_registry = storage::get::(); - let item_impl = item_registry.get_implementation(node.item_type).expect(&format!("Invalid ItemType: {:?}", node.item_type)); + let item_impl = item_registry + .get_implementation(node.item_type) + .expect(&format!("Invalid ItemType: {:?}", node.item_type)); //TODO: position sprite draw_texture_ex( diff --git a/src/nodes/player.rs b/src/nodes/player.rs index 29f4e0f..8a5d16d 100644 --- a/src/nodes/player.rs +++ b/src/nodes/player.rs @@ -15,11 +15,14 @@ use macroquad_platformer::Actor; use crate::{ consts, - nodes::{item::{ItemIdSource, ItemImplementationRegistry}, Nakama, NakamaRealtimeGame, Pickup}, + nodes::{ + item::{ItemIdSource, ItemImplementationRegistry}, + Nakama, NakamaRealtimeGame, Pickup, + }, Resources, }; -use plugin_api::{ItemType, ItemInstanceId}; +use plugin_api::{ItemInstanceId, ItemType}; #[derive(Default, Debug, Clone)] pub struct Input { @@ -43,7 +46,9 @@ pub struct Weapon { impl Drop for Weapon { fn drop(&mut self) { let item_registry = storage::get::(); - let item_impl = item_registry.get_implementation(self.item_type).expect(&format!("Invalid ItemType: {:?}", self.item_type)); + let item_impl = item_registry + .get_implementation(self.item_type) + .expect(&format!("Invalid ItemType: {:?}", self.item_type)); item_impl.destroy(self.item_id); } } @@ -52,7 +57,9 @@ impl Weapon { fn new(item_type: ItemType) -> Self { let mut item_id_source = storage::get_mut::(); let item_registry = storage::get::(); - let item_impl = item_registry.get_implementation(item_type).expect(&format!("Invalid ItemType: {:?}", item_type)); + let item_impl = item_registry + .get_implementation(item_type) + .expect(&format!("Invalid ItemType: {:?}", item_type)); let instance_id = item_id_source.next_id(); item_impl.construct(instance_id); Self { @@ -72,7 +79,6 @@ impl Weapon { let implementation = item_registry.get_implementation(self.item_type).unwrap(); implementation.uses_remaining(self.item_id) } - } pub struct Fish { @@ -326,8 +332,7 @@ impl Player { } pub fn pick_weapon(&mut self, item_type: ItemType) { - if self.state_machine.state() != Self::ST_SHOOT - { + if self.state_machine.state() != Self::ST_SHOOT { self.fish.pick_weapon(item_type); } } @@ -411,8 +416,7 @@ impl Player { start_coroutine(coroutine) } - fn update_shoot(node: &mut RefMut, _dt: f32) { - } + fn update_shoot(node: &mut RefMut, _dt: f32) {} fn shoot_coroutine(node: &mut RefMut) -> Coroutine { let handle = node.handle(); @@ -429,12 +433,12 @@ impl Player { if done { let node = &mut *scene::get_node(handle); node.state_machine.set_state(Self::ST_NORMAL); - break + break; } } else { let node = &mut *scene::get_node(handle); node.state_machine.set_state(Self::ST_NORMAL); - break + break; } wait_seconds(0.005).await; } diff --git a/src/nodes/remote_player.rs b/src/nodes/remote_player.rs index 38105d1..2c14de1 100644 --- a/src/nodes/remote_player.rs +++ b/src/nodes/remote_player.rs @@ -8,10 +8,7 @@ use macroquad::{ }; use crate::{ - nodes::{ - player::Fish, - item::ItemImplementationRegistry, - }, + nodes::{item::ItemImplementationRegistry, player::Fish}, Resources, }; use plugin_api::ItemType; @@ -74,25 +71,25 @@ impl RemotePlayer { pub fn shoot(&mut self, handle: Handle) { let coroutine = async move { - loop { - let mut item = None; - if let Some(weapon) = &scene::get_node(handle).fish.weapon { - item = Some((weapon.item_type, weapon.item_id)); - } - if let Some((item_type, item_id)) = item { - let item_registry = storage::get::(); - let implementation = item_registry.get_implementation(item_type).unwrap(); - let done = implementation.update_remote_shoot(item_id, handle); - if done { - break - } - } else { - break - } - wait_seconds(0.005).await; - } - }; - start_coroutine(coroutine); + loop { + let mut item = None; + if let Some(weapon) = &scene::get_node(handle).fish.weapon { + item = Some((weapon.item_type, weapon.item_id)); + } + if let Some((item_type, item_id)) = item { + let item_registry = storage::get::(); + let implementation = item_registry.get_implementation(item_type).unwrap(); + let done = implementation.update_remote_shoot(item_id, handle); + if done { + break; + } + } else { + break; + } + wait_seconds(0.005).await; + } + }; + start_coroutine(coroutine); } } impl scene::Node for RemotePlayer { diff --git a/src/plugin.rs b/src/plugin.rs index 2bf80dc..6eec644 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,23 +1,25 @@ use std::{ - sync::{Arc, Mutex}, - path::Path, collections::HashMap, + path::Path, + sync::{Arc, Mutex}, }; use macroquad::{ - prelude::*, - texture::Image, + audio::{load_sound_data, play_sound_once, Sound}, experimental::{ - scene::{self, Handle, RefMut}, animation::{AnimatedSprite, Animation}, + scene::{self, Handle, RefMut}, }, + prelude::*, + texture::Image, }; use wasm_plugin_host::WasmPlugin; -use plugin_api::{ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId}; -use crate::nodes::{ItemImplementationRegistry, Player, RemotePlayer, Fish}; - +use crate::nodes::{Fish, ItemImplementationRegistry, Player, RemotePlayer}; +use plugin_api::{ + AnimatedSpriteDescription, AnimationDescription, ImageDescription, PluginDescription, PluginId, +}; pub fn image_from_desc(desc: ImageDescription) -> Image { Image { @@ -36,13 +38,12 @@ pub fn animation_from_desc(desc: AnimationDescription) -> Animation { } pub fn animated_sprite_from_desc(desc: AnimatedSpriteDescription) -> AnimatedSprite { - let animations: Vec = desc.animations.into_iter().map(|a| animation_from_desc(a)).collect(); - AnimatedSprite::new( - desc.tile_width, - desc.tile_height, - &animations, - desc.playing, - ) + let animations: Vec = desc + .animations + .into_iter() + .map(|a| animation_from_desc(a)) + .collect(); + AnimatedSprite::new(desc.tile_width, desc.tile_height, &animations, desc.playing) } pub struct PluginRegistry(HashMap); @@ -56,16 +57,17 @@ pub struct Plugin { pub struct GameApi { current_player: Arc>>>, current_remote_player: Arc>>>, + sounds: Arc>>, } unsafe impl Send for GameApi {} unsafe impl Sync for GameApi {} enum LocalOrRemotePlayer { Local(RefMut), - Remote(RefMut) + Remote(RefMut), } struct FishMut { - node: LocalOrRemotePlayer + node: LocalOrRemotePlayer, } impl std::ops::Deref for FishMut { type Target = Fish; @@ -94,7 +96,11 @@ impl GameApi { result } - pub fn with_remote_player(&self, player: Handle, mut f: impl FnMut() -> R) -> R { + pub fn with_remote_player( + &self, + player: Handle, + mut f: impl FnMut() -> R, + ) -> R { self.current_remote_player.lock().unwrap().replace(player); let result = f(); self.current_remote_player.lock().unwrap().take(); @@ -106,19 +112,22 @@ impl GameApi { } fn current_remote_player(&self) -> Option> { - self.current_remote_player.lock().unwrap().map(|p| p.clone()) + self.current_remote_player + .lock() + .unwrap() + .map(|p| p.clone()) } fn current_fish(&self) -> FishMut { if let Some(handle) = self.current_player() { let node = scene::get_node(handle); FishMut { - node: LocalOrRemotePlayer::Local(node) + node: LocalOrRemotePlayer::Local(node), } } else { let node = scene::get_node(self.current_remote_player().unwrap()); FishMut { - node: LocalOrRemotePlayer::Remote(node) + node: LocalOrRemotePlayer::Remote(node), } } } @@ -222,47 +231,150 @@ impl GameApi { fish.disarm(); } + fn play_sound_once(&self, name: String) { + if let Some(sound) = self.sounds.lock().unwrap().get(&name) { + play_sound_once(*sound); + } + } + fn debug_print(&self, message: String) { println!("{}", message); } } impl PluginRegistry { - pub fn load(path: impl AsRef, item_registry: &mut ItemImplementationRegistry) -> Self { + pub async fn load( + path: impl AsRef, + item_registry: &mut ItemImplementationRegistry, + ) -> Self { let mut plugins = HashMap::new(); - for entry in path.as_ref().read_dir().expect("Unable to read plugins directory") { + for entry in path + .as_ref() + .read_dir() + .expect("Unable to read plugins directory") + { if let Ok(entry) = entry { if entry.path().to_str().unwrap().contains(".wasm") { let game_api = GameApi::default(); - let mut builder = wasm_plugin_host::WasmPluginBuilder::from_file(entry.path()).expect(&format!("Failed to load plugin {:?}", entry.path())); + let mut builder = wasm_plugin_host::WasmPluginBuilder::from_file(entry.path()) + .expect(&format!("Failed to load plugin {:?}", entry.path())); // TODO: This should probably be a macro or something to reduce boilerplate - builder = builder.import_function_with_context("spawn_bullet", game_api.clone(), |ctx: &GameApi| { ctx.spawn_bullet(); }); - builder = builder.import_function_with_context("hit_rect", game_api.clone(), |ctx: &GameApi, rect: [f32; 4]| { ctx.hit_rect(rect) }); - builder = builder.import_function_with_context("set_sprite_fx", game_api.clone(), |ctx: &GameApi, s: bool| { ctx.set_sprite_fx(s); }); - builder = builder.import_function_with_context("get_speed", game_api.clone(), |ctx: &GameApi| { ctx.get_speed() }); - builder = builder.import_function_with_context("set_speed", game_api.clone(), |ctx: &GameApi, s: [f32; 2]| { ctx.set_speed(s); }); - builder = builder.import_function_with_context("set_sprite_animation", game_api.clone(), |ctx: &GameApi, animation: usize| { ctx.set_sprite_animation(animation); }); - builder = builder.import_function_with_context("set_fx_sprite_animation", game_api.clone(), |ctx: &GameApi, animation: usize| { ctx.set_fx_sprite_animation(animation); }); - builder = builder.import_function_with_context("set_sprite_frame", game_api.clone(), |ctx: &GameApi, frame: u32| { ctx.set_sprite_frame(frame); }); - builder = builder.import_function_with_context("set_fx_sprite_frame", game_api.clone(), |ctx: &GameApi, frame: u32| { ctx.set_fx_sprite_frame(frame); }); - builder = builder.import_function_with_context("facing_dir", game_api.clone(), |ctx: &GameApi| { ctx.facing_dir() }); - builder = builder.import_function_with_context("position", game_api.clone(), |ctx: &GameApi| { ctx.position() }); - builder = builder.import_function_with_context("disarm", game_api.clone(), |ctx: &GameApi| { ctx.disarm() }); - builder = builder.import_function_with_context("nakama_shoot", game_api.clone(), |ctx: &GameApi| { ctx.nakama_shoot() }); - builder = builder.import_function_with_context("debug_print", game_api.clone(), |ctx: &GameApi, message: String| { ctx.debug_print(message) }); - let mut plugin = builder - .finish() - .unwrap(); - let description: PluginDescription = plugin.call_function("plugin_description").expect(&format!("Failed to call 'plugin_description' on plugin {:?}", entry.path())); + builder = builder.import_function_with_context( + "spawn_bullet", + game_api.clone(), + |ctx: &GameApi| { + ctx.spawn_bullet(); + }, + ); + builder = builder.import_function_with_context( + "hit_rect", + game_api.clone(), + |ctx: &GameApi, rect: [f32; 4]| ctx.hit_rect(rect), + ); + builder = builder.import_function_with_context( + "set_sprite_fx", + game_api.clone(), + |ctx: &GameApi, s: bool| { + ctx.set_sprite_fx(s); + }, + ); + builder = builder.import_function_with_context( + "get_speed", + game_api.clone(), + |ctx: &GameApi| ctx.get_speed(), + ); + builder = builder.import_function_with_context( + "set_speed", + game_api.clone(), + |ctx: &GameApi, s: [f32; 2]| { + ctx.set_speed(s); + }, + ); + builder = builder.import_function_with_context( + "set_sprite_animation", + game_api.clone(), + |ctx: &GameApi, animation: usize| { + ctx.set_sprite_animation(animation); + }, + ); + builder = builder.import_function_with_context( + "set_fx_sprite_animation", + game_api.clone(), + |ctx: &GameApi, animation: usize| { + ctx.set_fx_sprite_animation(animation); + }, + ); + builder = builder.import_function_with_context( + "set_sprite_frame", + game_api.clone(), + |ctx: &GameApi, frame: u32| { + ctx.set_sprite_frame(frame); + }, + ); + builder = builder.import_function_with_context( + "set_fx_sprite_frame", + game_api.clone(), + |ctx: &GameApi, frame: u32| { + ctx.set_fx_sprite_frame(frame); + }, + ); + builder = builder.import_function_with_context( + "facing_dir", + game_api.clone(), + |ctx: &GameApi| ctx.facing_dir(), + ); + builder = builder.import_function_with_context( + "position", + game_api.clone(), + |ctx: &GameApi| ctx.position(), + ); + builder = builder.import_function_with_context( + "disarm", + game_api.clone(), + |ctx: &GameApi| ctx.disarm(), + ); + builder = builder.import_function_with_context( + "play_sound_once", + game_api.clone(), + |ctx: &GameApi, sound: String| { + ctx.play_sound_once(sound); + }, + ); + builder = builder.import_function_with_context( + "nakama_shoot", + game_api.clone(), + |ctx: &GameApi| ctx.nakama_shoot(), + ); + builder = builder.import_function_with_context( + "debug_print", + game_api.clone(), + |ctx: &GameApi, message: String| ctx.debug_print(message), + ); + let mut plugin = builder.finish().unwrap(); + let description: PluginDescription = + plugin.call_function("plugin_description").expect(&format!( + "Failed to call 'plugin_description' on plugin {:?}", + entry.path() + )); for item in description.items { item_registry.add(item, description.plugin_id); } - plugins.insert(description.plugin_id, Plugin { - wasm_plugin: plugin, - game_api - }); + { + let mut sounds = game_api.sounds.lock().unwrap(); + for sound in description.sounds { + sounds.insert(sound.name, load_sound_data(&sound.bytes).await.unwrap()); + } + } + + plugins.insert( + description.plugin_id, + Plugin { + wasm_plugin: plugin, + game_api, + }, + ); } } } From b0097e5c92c8d71f76a09f2da5ca6d5c7895823a Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Tue, 4 May 2021 12:42:09 -0700 Subject: [PATCH 12/14] Replace PluginRegistry with a dummy so that the game compiles in WASM --- Cargo.toml | 3 +- src/nodes/item.rs | 28 +++ src/plugin.rs | 603 ++++++++++++++++++++++++---------------------- 3 files changed, 346 insertions(+), 288 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e184426..57d9fcc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ macroquad-particles = {version = "0.1", features = ["nanoserde"] } bitfield = "0.13" nakama-rs = "0.1" base64 = "0.13" -wasm_plugin_host = { version = "0.1.6", default-features = false, features = ["serialize_json"] } plugin_api = { path = "./plugin_api" } +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +wasm_plugin_host = { version = "0.1.6", default-features = false, features = ["serialize_json"] } diff --git a/src/nodes/item.rs b/src/nodes/item.rs index ab73326..aeaaa37 100644 --- a/src/nodes/item.rs +++ b/src/nodes/item.rs @@ -44,7 +44,10 @@ impl ItemImplementation { implementing_plugin: plugin, } } +} +#[cfg(not(target_arch = "wasm32"))] +impl ItemImplementation { fn with_plugin(&self, mut f: impl FnMut(&mut Plugin) -> R) -> R { let mut plugin_registry = storage::get_mut::(); let plugin = plugin_registry @@ -110,6 +113,31 @@ impl ItemImplementation { } } +#[cfg(target_arch = "wasm32")] +impl ItemImplementation { + pub(crate) fn construct(&self, item_id: ItemInstanceId) { + } + + pub(crate) fn destroy(&self, item_id: ItemInstanceId) { + } + + pub(crate) fn uses_remaining(&self, item_id: ItemInstanceId) -> Option<(u32, u32)> { + None + } + + pub(crate) fn update_shoot(&self, item_id: ItemInstanceId, player: Handle) -> bool { + true + } + + pub(crate) fn update_remote_shoot( + &self, + item_id: ItemInstanceId, + player: Handle, + ) -> bool { + true + } +} + #[derive(Default)] pub struct ItemImplementationRegistry(HashMap); diff --git a/src/plugin.rs b/src/plugin.rs index 6eec644..5c3ff34 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -3,7 +3,6 @@ use std::{ path::Path, sync::{Arc, Mutex}, }; - use macroquad::{ audio::{load_sound_data, play_sound_once, Sound}, experimental::{ @@ -14,8 +13,6 @@ use macroquad::{ texture::Image, }; -use wasm_plugin_host::WasmPlugin; - use crate::nodes::{Fish, ItemImplementationRegistry, Player, RemotePlayer}; use plugin_api::{ AnimatedSpriteDescription, AnimationDescription, ImageDescription, PluginDescription, PluginId, @@ -46,343 +43,375 @@ pub fn animated_sprite_from_desc(desc: AnimatedSpriteDescription) -> AnimatedSpr AnimatedSprite::new(desc.tile_width, desc.tile_height, &animations, desc.playing) } -pub struct PluginRegistry(HashMap); +#[cfg(not(target_arch = "wasm32"))] +pub use native_host::*; +#[cfg(target_arch = "wasm32")] +pub use wasm_host::*; -pub struct Plugin { - pub wasm_plugin: WasmPlugin, - pub game_api: GameApi, -} -#[derive(Default, Clone)] -pub struct GameApi { - current_player: Arc>>>, - current_remote_player: Arc>>>, - sounds: Arc>>, -} -unsafe impl Send for GameApi {} -unsafe impl Sync for GameApi {} +#[cfg(not(target_arch = "wasm32"))] +mod native_host { + use super::*; -enum LocalOrRemotePlayer { - Local(RefMut), - Remote(RefMut), -} -struct FishMut { - node: LocalOrRemotePlayer, -} -impl std::ops::Deref for FishMut { - type Target = Fish; + use wasm_plugin_host::WasmPlugin; - fn deref(&self) -> &Self::Target { - match &self.node { - LocalOrRemotePlayer::Local(player) => &player.fish, - LocalOrRemotePlayer::Remote(player) => &player.fish, - } - } -} -impl std::ops::DerefMut for FishMut { - fn deref_mut(&mut self) -> &mut Self::Target { - match &mut self.node { - LocalOrRemotePlayer::Local(player) => &mut player.fish, - LocalOrRemotePlayer::Remote(player) => &mut player.fish, - } - } -} + pub struct PluginRegistry(HashMap); -impl GameApi { - pub fn with_current_player(&self, player: Handle, mut f: impl FnMut() -> R) -> R { - self.current_player.lock().unwrap().replace(player); - let result = f(); - self.current_player.lock().unwrap().take(); - result + pub struct Plugin { + pub wasm_plugin: WasmPlugin, + pub game_api: GameApi, } - pub fn with_remote_player( - &self, - player: Handle, - mut f: impl FnMut() -> R, - ) -> R { - self.current_remote_player.lock().unwrap().replace(player); - let result = f(); - self.current_remote_player.lock().unwrap().take(); - result + #[derive(Default, Clone)] + pub struct GameApi { + current_player: Arc>>>, + current_remote_player: Arc>>>, + sounds: Arc>>, } + unsafe impl Send for GameApi {} + unsafe impl Sync for GameApi {} - fn current_player(&self) -> Option> { - self.current_player.lock().unwrap().map(|p| p.clone()) + enum LocalOrRemotePlayer { + Local(RefMut), + Remote(RefMut), } - - fn current_remote_player(&self) -> Option> { - self.current_remote_player - .lock() - .unwrap() - .map(|p| p.clone()) + struct FishMut { + node: LocalOrRemotePlayer, } + impl std::ops::Deref for FishMut { + type Target = Fish; - fn current_fish(&self) -> FishMut { - if let Some(handle) = self.current_player() { - let node = scene::get_node(handle); - FishMut { - node: LocalOrRemotePlayer::Local(node), + fn deref(&self) -> &Self::Target { + match &self.node { + LocalOrRemotePlayer::Local(player) => &player.fish, + LocalOrRemotePlayer::Remote(player) => &player.fish, } - } else { - let node = scene::get_node(self.current_remote_player().unwrap()); - FishMut { - node: LocalOrRemotePlayer::Remote(node), + } + } + impl std::ops::DerefMut for FishMut { + fn deref_mut(&mut self) -> &mut Self::Target { + match &mut self.node { + LocalOrRemotePlayer::Local(player) => &mut player.fish, + LocalOrRemotePlayer::Remote(player) => &mut player.fish, } } } - fn spawn_bullet(&self) { - let mut bullets = scene::find_node_by_type::().unwrap(); - let fish = self.current_fish(); - bullets.spawn_bullet(fish.pos, fish.facing); - } + impl GameApi { + pub fn with_current_player(&self, player: Handle, mut f: impl FnMut() -> R) -> R { + self.current_player.lock().unwrap().replace(player); + let result = f(); + self.current_player.lock().unwrap().take(); + result + } - fn hit_rect(&self, rect: [f32; 4]) -> u32 { - if let Some(handle) = self.current_player() { - let hit_box = Rect::new(rect[0], rect[1], rect[2], rect[3]); - let node = &mut *scene::get_node(handle); - let others = scene::find_nodes_by_type::(); - let mut hit_count = 0; - for player in others { - if Rect::new(player.pos().x, player.pos().y, 20., 64.).overlaps(&hit_box) { - hit_count += 1; - let mut net = scene::get_node(node.nakama_realtime); - net.kill(&player.id, !node.fish.facing); + pub fn with_remote_player( + &self, + player: Handle, + mut f: impl FnMut() -> R, + ) -> R { + self.current_remote_player.lock().unwrap().replace(player); + let result = f(); + self.current_remote_player.lock().unwrap().take(); + result + } + + fn current_player(&self) -> Option> { + self.current_player.lock().unwrap().map(|p| p.clone()) + } + + fn current_remote_player(&self) -> Option> { + self.current_remote_player + .lock() + .unwrap() + .map(|p| p.clone()) + } + + fn current_fish(&self) -> FishMut { + if let Some(handle) = self.current_player() { + let node = scene::get_node(handle); + FishMut { + node: LocalOrRemotePlayer::Local(node), + } + } else { + let node = scene::get_node(self.current_remote_player().unwrap()); + FishMut { + node: LocalOrRemotePlayer::Remote(node), } } - hit_count - } else { - 0 } - } - fn set_sprite_fx(&self, s: bool) { - let mut fish = self.current_fish(); - if let Some(weapon) = &mut fish.weapon { - weapon.fx = s; + fn spawn_bullet(&self) { + let mut bullets = scene::find_node_by_type::().unwrap(); + let fish = self.current_fish(); + bullets.spawn_bullet(fish.pos, fish.facing); } - } - fn get_speed(&self) -> [f32; 2] { - let fish = self.current_fish(); - [fish.speed.x, fish.speed.y] - } + fn hit_rect(&self, rect: [f32; 4]) -> u32 { + if let Some(handle) = self.current_player() { + let hit_box = Rect::new(rect[0], rect[1], rect[2], rect[3]); + let node = &mut *scene::get_node(handle); + let others = scene::find_nodes_by_type::(); + let mut hit_count = 0; + for player in others { + if Rect::new(player.pos().x, player.pos().y, 20., 64.).overlaps(&hit_box) { + hit_count += 1; + let mut net = scene::get_node(node.nakama_realtime); + net.kill(&player.id, !node.fish.facing); + } + } + hit_count + } else { + 0 + } + } - fn set_speed(&self, speed: [f32; 2]) { - let mut fish = self.current_fish(); - fish.speed.x = speed[0]; - fish.speed.y = speed[1]; - } + fn set_sprite_fx(&self, s: bool) { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { + weapon.fx = s; + } + } - fn set_sprite_animation(&self, animation: usize) { - let mut fish = self.current_fish(); - if let Some(weapon) = &mut fish.weapon { - weapon.sprite.set_animation(animation); + fn get_speed(&self) -> [f32; 2] { + let fish = self.current_fish(); + [fish.speed.x, fish.speed.y] + } + + fn set_speed(&self, speed: [f32; 2]) { + let mut fish = self.current_fish(); + fish.speed.x = speed[0]; + fish.speed.y = speed[1]; } - } - fn set_fx_sprite_animation(&self, animation: usize) { - let mut fish = self.current_fish(); - if let Some(weapon) = &mut fish.weapon { - if let Some(sprite) = &mut weapon.fx_sprite { - sprite.set_animation(animation); + fn set_sprite_animation(&self, animation: usize) { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { + weapon.sprite.set_animation(animation); } } - } - fn set_sprite_frame(&self, frame: u32) { - let mut fish = self.current_fish(); - if let Some(weapon) = &mut fish.weapon { - weapon.sprite.set_frame(frame); + fn set_fx_sprite_animation(&self, animation: usize) { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { + if let Some(sprite) = &mut weapon.fx_sprite { + sprite.set_animation(animation); + } + } } - } - fn set_fx_sprite_frame(&self, frame: u32) { - let mut fish = self.current_fish(); - if let Some(weapon) = &mut fish.weapon { - if let Some(sprite) = &mut weapon.fx_sprite { - sprite.set_frame(frame); + fn set_sprite_frame(&self, frame: u32) { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { + weapon.sprite.set_frame(frame); } } - } - fn facing_dir(&self) -> f32 { - let fish = self.current_fish(); - fish.facing_dir() - } + fn set_fx_sprite_frame(&self, frame: u32) { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { + if let Some(sprite) = &mut weapon.fx_sprite { + sprite.set_frame(frame); + } + } + } - fn position(&self) -> [f32; 2] { - let fish = self.current_fish(); - let pos = fish.pos(); - [pos.x, pos.y] - } + fn facing_dir(&self) -> f32 { + let fish = self.current_fish(); + fish.facing_dir() + } - fn nakama_shoot(&self) { - if let Some(handle) = self.current_player() { - let node = &mut *scene::get_node(handle); - let mut nakama = scene::get_node(node.nakama_realtime); - nakama.shoot(); + fn position(&self) -> [f32; 2] { + let fish = self.current_fish(); + let pos = fish.pos(); + [pos.x, pos.y] } - } - fn disarm(&self) { - let mut fish = self.current_fish(); - fish.disarm(); - } + fn nakama_shoot(&self) { + if let Some(handle) = self.current_player() { + let node = &mut *scene::get_node(handle); + let mut nakama = scene::get_node(node.nakama_realtime); + nakama.shoot(); + } + } - fn play_sound_once(&self, name: String) { - if let Some(sound) = self.sounds.lock().unwrap().get(&name) { - play_sound_once(*sound); + fn disarm(&self) { + let mut fish = self.current_fish(); + fish.disarm(); + } + + fn play_sound_once(&self, name: String) { + if let Some(sound) = self.sounds.lock().unwrap().get(&name) { + play_sound_once(*sound); + } } - } - fn debug_print(&self, message: String) { - println!("{}", message); + fn debug_print(&self, message: String) { + println!("{}", message); + } } -} -impl PluginRegistry { - pub async fn load( - path: impl AsRef, - item_registry: &mut ItemImplementationRegistry, - ) -> Self { - let mut plugins = HashMap::new(); - for entry in path - .as_ref() - .read_dir() - .expect("Unable to read plugins directory") - { - if let Ok(entry) = entry { - if entry.path().to_str().unwrap().contains(".wasm") { - let game_api = GameApi::default(); - let mut builder = wasm_plugin_host::WasmPluginBuilder::from_file(entry.path()) - .expect(&format!("Failed to load plugin {:?}", entry.path())); - // TODO: This should probably be a macro or something to reduce boilerplate - builder = builder.import_function_with_context( - "spawn_bullet", - game_api.clone(), - |ctx: &GameApi| { - ctx.spawn_bullet(); - }, - ); - builder = builder.import_function_with_context( - "hit_rect", - game_api.clone(), - |ctx: &GameApi, rect: [f32; 4]| ctx.hit_rect(rect), - ); - builder = builder.import_function_with_context( - "set_sprite_fx", - game_api.clone(), - |ctx: &GameApi, s: bool| { - ctx.set_sprite_fx(s); - }, - ); - builder = builder.import_function_with_context( - "get_speed", - game_api.clone(), - |ctx: &GameApi| ctx.get_speed(), - ); - builder = builder.import_function_with_context( - "set_speed", - game_api.clone(), - |ctx: &GameApi, s: [f32; 2]| { - ctx.set_speed(s); - }, - ); - builder = builder.import_function_with_context( - "set_sprite_animation", - game_api.clone(), - |ctx: &GameApi, animation: usize| { - ctx.set_sprite_animation(animation); - }, - ); - builder = builder.import_function_with_context( - "set_fx_sprite_animation", - game_api.clone(), - |ctx: &GameApi, animation: usize| { - ctx.set_fx_sprite_animation(animation); - }, - ); - builder = builder.import_function_with_context( - "set_sprite_frame", - game_api.clone(), - |ctx: &GameApi, frame: u32| { - ctx.set_sprite_frame(frame); - }, - ); - builder = builder.import_function_with_context( - "set_fx_sprite_frame", - game_api.clone(), - |ctx: &GameApi, frame: u32| { - ctx.set_fx_sprite_frame(frame); - }, - ); - builder = builder.import_function_with_context( - "facing_dir", - game_api.clone(), - |ctx: &GameApi| ctx.facing_dir(), - ); - builder = builder.import_function_with_context( - "position", - game_api.clone(), - |ctx: &GameApi| ctx.position(), - ); - builder = builder.import_function_with_context( - "disarm", - game_api.clone(), - |ctx: &GameApi| ctx.disarm(), - ); - builder = builder.import_function_with_context( - "play_sound_once", - game_api.clone(), - |ctx: &GameApi, sound: String| { - ctx.play_sound_once(sound); - }, - ); - builder = builder.import_function_with_context( - "nakama_shoot", - game_api.clone(), - |ctx: &GameApi| ctx.nakama_shoot(), - ); - builder = builder.import_function_with_context( - "debug_print", - game_api.clone(), - |ctx: &GameApi, message: String| ctx.debug_print(message), - ); - let mut plugin = builder.finish().unwrap(); - let description: PluginDescription = - plugin.call_function("plugin_description").expect(&format!( - "Failed to call 'plugin_description' on plugin {:?}", - entry.path() - )); - - for item in description.items { - item_registry.add(item, description.plugin_id); - } + impl PluginRegistry { + pub async fn load( + path: impl AsRef, + item_registry: &mut ItemImplementationRegistry, + ) -> Self { + let mut plugins = HashMap::new(); + for entry in path + .as_ref() + .read_dir() + .expect("Unable to read plugins directory") + { + if let Ok(entry) = entry { + if entry.path().to_str().unwrap().contains(".wasm") { + let game_api = GameApi::default(); + let mut builder = wasm_plugin_host::WasmPluginBuilder::from_file(entry.path()) + .expect(&format!("Failed to load plugin {:?}", entry.path())); + // TODO: This should probably be a macro or something to reduce boilerplate + builder = builder.import_function_with_context( + "spawn_bullet", + game_api.clone(), + |ctx: &GameApi| { + ctx.spawn_bullet(); + }, + ); + builder = builder.import_function_with_context( + "hit_rect", + game_api.clone(), + |ctx: &GameApi, rect: [f32; 4]| ctx.hit_rect(rect), + ); + builder = builder.import_function_with_context( + "set_sprite_fx", + game_api.clone(), + |ctx: &GameApi, s: bool| { + ctx.set_sprite_fx(s); + }, + ); + builder = builder.import_function_with_context( + "get_speed", + game_api.clone(), + |ctx: &GameApi| ctx.get_speed(), + ); + builder = builder.import_function_with_context( + "set_speed", + game_api.clone(), + |ctx: &GameApi, s: [f32; 2]| { + ctx.set_speed(s); + }, + ); + builder = builder.import_function_with_context( + "set_sprite_animation", + game_api.clone(), + |ctx: &GameApi, animation: usize| { + ctx.set_sprite_animation(animation); + }, + ); + builder = builder.import_function_with_context( + "set_fx_sprite_animation", + game_api.clone(), + |ctx: &GameApi, animation: usize| { + ctx.set_fx_sprite_animation(animation); + }, + ); + builder = builder.import_function_with_context( + "set_sprite_frame", + game_api.clone(), + |ctx: &GameApi, frame: u32| { + ctx.set_sprite_frame(frame); + }, + ); + builder = builder.import_function_with_context( + "set_fx_sprite_frame", + game_api.clone(), + |ctx: &GameApi, frame: u32| { + ctx.set_fx_sprite_frame(frame); + }, + ); + builder = builder.import_function_with_context( + "facing_dir", + game_api.clone(), + |ctx: &GameApi| ctx.facing_dir(), + ); + builder = builder.import_function_with_context( + "position", + game_api.clone(), + |ctx: &GameApi| ctx.position(), + ); + builder = builder.import_function_with_context( + "disarm", + game_api.clone(), + |ctx: &GameApi| ctx.disarm(), + ); + builder = builder.import_function_with_context( + "play_sound_once", + game_api.clone(), + |ctx: &GameApi, sound: String| { + ctx.play_sound_once(sound); + }, + ); + builder = builder.import_function_with_context( + "nakama_shoot", + game_api.clone(), + |ctx: &GameApi| ctx.nakama_shoot(), + ); + builder = builder.import_function_with_context( + "debug_print", + game_api.clone(), + |ctx: &GameApi, message: String| ctx.debug_print(message), + ); + let mut plugin = builder.finish().unwrap(); + let description: PluginDescription = + plugin.call_function("plugin_description").expect(&format!( + "Failed to call 'plugin_description' on plugin {:?}", + entry.path() + )); + + for item in description.items { + item_registry.add(item, description.plugin_id); + } - { - let mut sounds = game_api.sounds.lock().unwrap(); - for sound in description.sounds { - sounds.insert(sound.name, load_sound_data(&sound.bytes).await.unwrap()); + { + let mut sounds = game_api.sounds.lock().unwrap(); + for sound in description.sounds { + sounds.insert(sound.name, load_sound_data(&sound.bytes).await.unwrap()); + } } - } - plugins.insert( - description.plugin_id, - Plugin { - wasm_plugin: plugin, - game_api, - }, - ); + plugins.insert( + description.plugin_id, + Plugin { + wasm_plugin: plugin, + game_api, + }, + ); + } } } + + PluginRegistry(plugins) } - PluginRegistry(plugins) + pub(crate) fn get_plugin(&mut self, plugin_id: PluginId) -> Option<&mut Plugin> { + self.0.get_mut(&plugin_id) + } } +} - pub(crate) fn get_plugin(&mut self, plugin_id: PluginId) -> Option<&mut Plugin> { - self.0.get_mut(&plugin_id) +#[cfg(target_arch = "wasm32")] +mod wasm_host { + use super::*; + pub struct Plugin; + + pub struct PluginRegistry; + impl PluginRegistry { + pub async fn load( + _path: impl AsRef, + _item_registry: &mut ItemImplementationRegistry, + ) -> Self { + PluginRegistry + } + pub(crate) fn get_plugin(&mut self, plugin_id: PluginId) -> Option<&mut Plugin> { + None + } } } From 300421de418342869ea0801ced787a1449a7d71a Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Wed, 5 May 2021 14:13:20 -0700 Subject: [PATCH 13/14] Inline core plugin when compiling wasm version of the game --- Cargo.lock | 34 +++ Cargo.toml | 3 + core_plugin/Cargo.toml | 7 +- core_plugin/src/lib.rs | 217 ++++++++++++----- plugin_api/src/lib.rs | 26 ++ src/nodes/global_events.rs | 1 - src/nodes/item.rs | 64 +---- src/plugin.rs | 487 ++++++++++++++++++++++--------------- 8 files changed, 530 insertions(+), 309 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 129cafb..44daea7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -225,6 +225,16 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +[[package]] +name = "core_plugin" +version = "0.1.0" +dependencies = [ + "image", + "lazy_static", + "plugin_api", + "wasm_plugin_guest", +] + [[package]] name = "coreaudio-rs" version = "0.10.0" @@ -518,6 +528,7 @@ version = "0.1.0" dependencies = [ "base64", "bitfield", + "core_plugin", "macroquad", "macroquad-particles", "macroquad-platformer", @@ -2142,6 +2153,29 @@ version = "0.2.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" +[[package]] +name = "wasm_plugin_guest" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85398177caedea857f8a881cafa0256101b261a65d03872e577fdb7bff16f89c" +dependencies = [ + "bitfield", + "serde", + "serde_json", + "wasm_plugin_guest_derive", +] + +[[package]] +name = "wasm_plugin_guest_derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8029abbe46726b92fcfb290819097e8ae205030d8b09cdada5be037476ad879a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "wasm_plugin_host" version = "0.1.6" diff --git a/Cargo.toml b/Cargo.toml index 57d9fcc..dd6384d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,6 @@ plugin_api = { path = "./plugin_api" } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] wasm_plugin_host = { version = "0.1.6", default-features = false, features = ["serialize_json"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +core_plugin = { path = "./core_plugin", features = ["inline"] } diff --git a/core_plugin/Cargo.toml b/core_plugin/Cargo.toml index e0996d8..489d1ee 100644 --- a/core_plugin/Cargo.toml +++ b/core_plugin/Cargo.toml @@ -5,10 +5,13 @@ authors = ["Alec Deason "] edition = "2018" [lib] - crate-type = ["cdylib"] + crate-type = ["rlib", "cdylib"] + +[features] +inline = [] [dependencies] plugin_api = { path = "../plugin_api" } -wasm_plugin_guest = { version = "0.1.5", default-features = false, features = ["serialize_json"] } image = { version = "0.23", default-features = false, features = ["png"] } lazy_static = "1.4.0" +wasm_plugin_guest = { version = "0.1.5", default-features = false, features = ["serialize_json"] } diff --git a/core_plugin/src/lib.rs b/core_plugin/src/lib.rs index a029c5d..0e1c974 100644 --- a/core_plugin/src/lib.rs +++ b/core_plugin/src/lib.rs @@ -1,11 +1,9 @@ -use std::{ - sync::Mutex, - io::Cursor, - collections::HashMap, -}; - -use plugin_api::{ItemType, ImageDescription, AnimationDescription, AnimatedSpriteDescription, PluginDescription, PluginId, ItemDescription, Rect, ItemInstanceId, import_game_api, SoundDescription}; +use std::{collections::HashMap, io::Cursor, sync::Mutex}; +use plugin_api::{ + import_game_api, AnimatedSpriteDescription, AnimationDescription, GameApi, ImageDescription, + ItemDescription, ItemInstanceId, ItemType, PluginDescription, PluginId, Rect, SoundDescription, +}; lazy_static::lazy_static! { static ref ITEMS:Mutex> = Default::default(); @@ -16,21 +14,100 @@ const SWORD: ItemType = ItemType::new(11238048715746880612); pub const GUN_THROWBACK: f32 = 700.0; -import_game_api!(); - pub enum ItemState { Gun(GunState), Sword(SwordState), } -#[wasm_plugin_guest::export_function] -fn plugin_description() -> PluginDescription { - let sword_image = image::load(Cursor::new(include_bytes!("../../assets/Whale/Sword(65x93).png")), image::ImageFormat::Png).unwrap().to_rgba8(); +#[cfg(not(feature = "inline"))] +use wasm_game_api::*; + +#[cfg(not(feature = "inline"))] +mod wasm_game_api { + use super::*; + import_game_api!(); + + pub struct GuestGameApi; + impl GameApi for GuestGameApi { + fn spawn_bullet(&self) { + spawn_bullet(); + } + + fn hit_rect(&self, rect: [f32; 4]) -> u32 { + hit_rect(rect) + } + + fn set_sprite_fx(&self, s: bool) { + set_sprite_fx(s); + } + + fn get_speed(&self) -> [f32; 2] { + get_speed() + } + + fn set_speed(&self, speed: [f32; 2]) { + set_speed(speed); + } + + fn facing_dir(&self) -> f32 { + facing_dir() + } + + fn position(&self) -> [f32; 2] { + position() + } + + fn set_sprite_animation(&self, animation: u32) { + set_sprite_animation(animation); + } + + fn set_fx_sprite_animation(&self, animation: u32) { + set_fx_sprite_animation(animation); + } + + fn set_sprite_frame(&self, frame: u32) { + set_sprite_frame(frame); + } + + fn set_fx_sprite_frame(&self, frame: u32) { + set_fx_sprite_frame(frame); + } + + fn disarm(&self) { + disarm(); + } + + fn play_sound_once(&self, sound: String) { + play_sound_once(sound); + } + + fn nakama_shoot(&self) { + nakama_shoot(); + } + + fn debug_print(&self, message: String) { + debug_print(message); + } + } +} +#[cfg_attr(not(feature = "inline"), wasm_plugin_guest::export_function)] +pub fn plugin_description() -> PluginDescription { + let sword_image = image::load( + Cursor::new(include_bytes!("../../assets/Whale/Sword(65x93).png")), + image::ImageFormat::Png, + ) + .unwrap() + .to_rgba8(); let sword_width = sword_image.width() as u16; let sword_height = sword_image.height() as u16; let sword_bytes = sword_image.into_vec(); - let gun_image = image::load(Cursor::new(include_bytes!("../../assets/Whale/Gun(92x32).png")), image::ImageFormat::Png).unwrap().to_rgba8(); + let gun_image = image::load( + Cursor::new(include_bytes!("../../assets/Whale/Gun(92x32).png")), + image::ImageFormat::Png, + ) + .unwrap() + .to_rgba8(); let gun_width = gun_image.width() as u16; let gun_height = gun_image.height() as u16; let gun_bytes = gun_image.into_vec(); @@ -126,39 +203,37 @@ fn plugin_description() -> PluginDescription { fx_sprite: Some(AnimatedSpriteDescription { tile_width: 76, tile_height: 66, - animations: vec![ - AnimationDescription { - name: "shoot".to_string(), - row: 2, - frames: 3, - fps: 15, - }, - ], + animations: vec![AnimationDescription { + name: "shoot".to_string(), + row: 2, + frames: 3, + fps: 15, + }], playing: true, }), - } + }, ], } } -#[wasm_plugin_guest::export_function] -fn new_instance(item_type: ItemType, item_id: ItemInstanceId) { +#[cfg_attr(not(feature = "inline"), wasm_plugin_guest::export_function)] +pub fn new_instance(item_type: ItemType, item_id: ItemInstanceId) { let state = match item_type { GUN => ItemState::Gun(GunState::default()), SWORD => ItemState::Sword(SwordState::default()), - _ => panic!() + _ => panic!(), }; ITEMS.lock().unwrap().insert(item_id, state); } -#[wasm_plugin_guest::export_function] -fn destroy_instance(item_id: ItemInstanceId) { +#[cfg_attr(not(feature = "inline"), wasm_plugin_guest::export_function)] +pub fn destroy_instance(item_id: ItemInstanceId) { ITEMS.lock().unwrap().remove(&item_id); } -#[wasm_plugin_guest::export_function] -fn uses_remaining(item_id: ItemInstanceId) -> Option<(u32, u32)> { +#[cfg_attr(not(feature = "inline"), wasm_plugin_guest::export_function)] +pub fn uses_remaining(item_id: ItemInstanceId) -> Option<(u32, u32)> { if let Some(ItemState::Gun(state)) = ITEMS.lock().unwrap().get(&item_id) { Some((state.ammo, 3)) } else { @@ -166,18 +241,28 @@ fn uses_remaining(item_id: ItemInstanceId) -> Option<(u32, u32)> { } } +#[cfg(not(feature = "inline"))] #[wasm_plugin_guest::export_function] -fn update_shoot(item_id: ItemInstanceId, current_time: f64) -> bool { +pub fn update_shoot(item_id: ItemInstanceId, current_time: f64) -> bool { + inner_update_shoot(item_id, current_time, &GuestGameApi) +} + +#[cfg(feature = "inline")] +pub fn update_shoot(item_id: ItemInstanceId, current_time: f64, game_api: &dyn GameApi) -> bool { + inner_update_shoot(item_id, current_time, game_api) +} + +fn inner_update_shoot(item_id: ItemInstanceId, current_time: f64, game_api: &dyn GameApi) -> bool { if let Some(item) = ITEMS.lock().unwrap().get_mut(&item_id) { match item { ItemState::Gun(state) => { if let Some(time) = state.recovery_time { if time <= current_time { - set_sprite_animation(0); - set_sprite_fx(false); + game_api.set_sprite_animation(0); + game_api.set_sprite_fx(false); state.recovery_time.take(); if state.ammo == 0 { - disarm(); + game_api.disarm(); } true } else { @@ -185,69 +270,87 @@ fn update_shoot(item_id: ItemInstanceId, current_time: f64) -> bool { } } else { state.ammo -= 1; - play_sound_once("shoot".to_string()); - spawn_bullet(); - nakama_shoot(); - set_sprite_fx(true); - let mut speed = get_speed(); - speed[0] -= GUN_THROWBACK * facing_dir(); - set_speed(speed); - set_sprite_animation(1); + game_api.play_sound_once("shoot".to_string()); + game_api.spawn_bullet(); + game_api.nakama_shoot(); + game_api.set_sprite_fx(true); + let mut speed = game_api.get_speed(); + speed[0] -= GUN_THROWBACK * game_api.facing_dir(); + game_api.set_speed(speed); + game_api.set_sprite_animation(1); state.recovery_time = Some(current_time + 0.08 * 3.0); false } - }, + } ItemState::Sword(state) => { if let Some(time) = state.recovery_time { if time <= current_time { - set_sprite_animation(0); + game_api.set_sprite_animation(0); state.recovery_time.take(); true } else { - nakama_shoot(); - play_sound_once("sword".to_string()); - let pos = position(); - let sword_hit_box = if facing_dir() > 0.0 { + game_api.nakama_shoot(); + game_api.play_sound_once("sword".to_string()); + let pos = game_api.position(); + let sword_hit_box = if game_api.facing_dir() > 0.0 { [pos[0] + 35., pos[1] - 5., 40., 60.] } else { [pos[0] - 50., pos[1] - 5., 40., 60.] }; - hit_rect(sword_hit_box); + game_api.hit_rect(sword_hit_box); false } } else { - set_sprite_animation(1); + game_api.set_sprite_animation(1); state.recovery_time = Some(current_time + 0.08 * 3.0); false } - }, + } } } else { true } } +#[cfg(not(feature = "inline"))] #[wasm_plugin_guest::export_function] -fn update_remote_shoot(item_id: ItemInstanceId, current_time: f64) -> bool { +pub fn update_remote_shoot(item_id: ItemInstanceId, current_time: f64) -> bool { + inner_update_remote_shoot(item_id, current_time, &GuestGameApi) +} + +#[cfg(feature = "inline")] +pub fn update_remote_shoot( + item_id: ItemInstanceId, + current_time: f64, + game_api: &dyn GameApi, +) -> bool { + inner_update_remote_shoot(item_id, current_time, game_api) +} + +fn inner_update_remote_shoot( + item_id: ItemInstanceId, + current_time: f64, + game_api: &dyn GameApi, +) -> bool { if let Some(item) = ITEMS.lock().unwrap().get_mut(&item_id) { match item { ItemState::Gun(_) => { - spawn_bullet(); - play_sound_once("shoot".to_string()); + game_api.spawn_bullet(); + game_api.play_sound_once("shoot".to_string()); true - }, + } ItemState::Sword(state) => { if let Some(time) = state.recovery_time { if time <= current_time { - set_sprite_animation(0); + game_api.set_sprite_animation(0); state.recovery_time.take(); true } else { false } } else { - play_sound_once("sword".to_string()); - set_sprite_animation(1); + game_api.play_sound_once("sword".to_string()); + game_api.set_sprite_animation(1); state.recovery_time = Some(current_time + 0.08 * 3.0); false } diff --git a/plugin_api/src/lib.rs b/plugin_api/src/lib.rs index c14a35a..fad05e4 100644 --- a/plugin_api/src/lib.rs +++ b/plugin_api/src/lib.rs @@ -103,6 +103,32 @@ pub struct AnimationDescription { pub fps: u32, } +pub trait PluginApi { + fn new_instance(&mut self, item_type: ItemType, item_id: ItemInstanceId); + fn destroy_instance(&mut self, item_id: ItemInstanceId); + fn uses_remaining(&mut self, item_id: ItemInstanceId) -> Option<(u32, u32)>; + fn update_shoot(&mut self, item_id: ItemInstanceId, time: f64) -> bool; + fn update_remote_shoot(&mut self, item_id: ItemInstanceId, time: f64) -> bool; +} + +pub trait GameApi { + fn spawn_bullet(&self); + fn hit_rect(&self, rect: [f32; 4]) -> u32; + fn set_sprite_fx(&self, s: bool); + fn get_speed(&self) -> [f32; 2]; + fn set_speed(&self, speed: [f32; 2]); + fn facing_dir(&self) -> f32; + fn position(&self) -> [f32; 2]; + fn set_sprite_animation(&self, animation: u32); + fn set_fx_sprite_animation(&self, animation: u32); + fn set_sprite_frame(&self, frame: u32); + fn set_fx_sprite_frame(&self, frame: u32); + fn disarm(&self); + fn play_sound_once(&self, sound: String); + fn nakama_shoot(&self); + fn debug_print(&self, message: String); +} + #[macro_export] macro_rules! import_game_api { () => { diff --git a/src/nodes/global_events.rs b/src/nodes/global_events.rs index 67d0bfa..485aecc 100644 --- a/src/nodes/global_events.rs +++ b/src/nodes/global_events.rs @@ -10,7 +10,6 @@ use crate::{ nodes::{item::ItemImplementationRegistry, NakamaRealtimeGame, Pickup, Player, RemotePlayer}, Resources, }; -use plugin_api::ItemType; pub struct GlobalEvents { last_spawn_time: f64, diff --git a/src/nodes/item.rs b/src/nodes/item.rs index aeaaa37..02e3b2d 100644 --- a/src/nodes/item.rs +++ b/src/nodes/item.rs @@ -9,7 +9,7 @@ use crate::{ nodes::{Player, RemotePlayer}, plugin::{animated_sprite_from_desc, image_from_desc, Plugin, PluginRegistry}, }; -use plugin_api::{ItemDescription, ItemInstanceId, ItemType, PluginId}; +use plugin_api::{ItemDescription, ItemInstanceId, ItemType, PluginApi, PluginId}; pub(crate) struct ItemImplementation { display_name: String, @@ -46,7 +46,6 @@ impl ItemImplementation { } } -#[cfg(not(target_arch = "wasm32"))] impl ItemImplementation { fn with_plugin(&self, mut f: impl FnMut(&mut Plugin) -> R) -> R { let mut plugin_registry = storage::get_mut::(); @@ -58,40 +57,22 @@ impl ItemImplementation { pub(crate) fn construct(&self, item_id: ItemInstanceId) { self.with_plugin(|p| { - p.wasm_plugin - .call_function_with_argument("new_instance", &(self.item_type, item_id)) - .unwrap() + p.new_instance(self.item_type, item_id); }) } pub(crate) fn destroy(&self, item_id: ItemInstanceId) { self.with_plugin(|p| { - p.wasm_plugin - .call_function_with_argument("destroy_instance", &item_id) - .unwrap() + p.destroy_instance(item_id); }) } pub(crate) fn uses_remaining(&self, item_id: ItemInstanceId) -> Option<(u32, u32)> { - self.with_plugin(|p| { - p.wasm_plugin - .call_function_with_argument("uses_remaining", &item_id) - .unwrap() - }) + self.with_plugin(|p| p.uses_remaining(item_id)) } pub(crate) fn update_shoot(&self, item_id: ItemInstanceId, player: Handle) -> bool { - self.with_plugin(|p| { - let Plugin { - game_api, - wasm_plugin, - } = p; - game_api.with_current_player(player, || { - wasm_plugin - .call_function_with_argument("update_shoot", &(item_id, get_time())) - .unwrap() - }) - }) + self.with_plugin(|p| p.with_current_player(player, |p| p.update_shoot(item_id, get_time()))) } pub(crate) fn update_remote_shoot( @@ -100,44 +81,11 @@ impl ItemImplementation { player: Handle, ) -> bool { self.with_plugin(|p| { - let Plugin { - game_api, - wasm_plugin, - } = p; - game_api.with_remote_player(player, || { - wasm_plugin - .call_function_with_argument("update_remote_shoot", &(item_id, get_time())) - .unwrap() - }) + p.with_current_remote_player(player, |p| p.update_remote_shoot(item_id, get_time())) }) } } -#[cfg(target_arch = "wasm32")] -impl ItemImplementation { - pub(crate) fn construct(&self, item_id: ItemInstanceId) { - } - - pub(crate) fn destroy(&self, item_id: ItemInstanceId) { - } - - pub(crate) fn uses_remaining(&self, item_id: ItemInstanceId) -> Option<(u32, u32)> { - None - } - - pub(crate) fn update_shoot(&self, item_id: ItemInstanceId, player: Handle) -> bool { - true - } - - pub(crate) fn update_remote_shoot( - &self, - item_id: ItemInstanceId, - player: Handle, - ) -> bool { - true - } -} - #[derive(Default)] pub struct ItemImplementationRegistry(HashMap); diff --git a/src/plugin.rs b/src/plugin.rs index 5c3ff34..5970c45 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,8 +1,3 @@ -use std::{ - collections::HashMap, - path::Path, - sync::{Arc, Mutex}, -}; use macroquad::{ audio::{load_sound_data, play_sound_once, Sound}, experimental::{ @@ -12,10 +7,16 @@ use macroquad::{ prelude::*, texture::Image, }; +use std::{ + collections::HashMap, + path::Path, + sync::{Arc, Mutex}, +}; use crate::nodes::{Fish, ItemImplementationRegistry, Player, RemotePlayer}; use plugin_api::{ - AnimatedSpriteDescription, AnimationDescription, ImageDescription, PluginDescription, PluginId, + AnimatedSpriteDescription, AnimationDescription, GameApi, ImageDescription, ItemInstanceId, + ItemType, PluginApi, PluginDescription, PluginId, }; pub fn image_from_desc(desc: ImageDescription) -> Image { @@ -43,212 +44,195 @@ pub fn animated_sprite_from_desc(desc: AnimatedSpriteDescription) -> AnimatedSpr AnimatedSprite::new(desc.tile_width, desc.tile_height, &animations, desc.playing) } -#[cfg(not(target_arch = "wasm32"))] -pub use native_host::*; -#[cfg(target_arch = "wasm32")] -pub use wasm_host::*; - - -#[cfg(not(target_arch = "wasm32"))] -mod native_host { - use super::*; - - use wasm_plugin_host::WasmPlugin; +#[derive(Default, Clone)] +pub struct HostGameApi { + current_player: Arc>>>, + current_remote_player: Arc>>>, + sounds: Arc>>, +} +unsafe impl Send for HostGameApi {} +unsafe impl Sync for HostGameApi {} - pub struct PluginRegistry(HashMap); +enum LocalOrRemotePlayer { + Local(RefMut), + Remote(RefMut), +} +struct FishMut { + node: LocalOrRemotePlayer, +} +impl std::ops::Deref for FishMut { + type Target = Fish; - pub struct Plugin { - pub wasm_plugin: WasmPlugin, - pub game_api: GameApi, + fn deref(&self) -> &Self::Target { + match &self.node { + LocalOrRemotePlayer::Local(player) => &player.fish, + LocalOrRemotePlayer::Remote(player) => &player.fish, + } } - - #[derive(Default, Clone)] - pub struct GameApi { - current_player: Arc>>>, - current_remote_player: Arc>>>, - sounds: Arc>>, +} +impl std::ops::DerefMut for FishMut { + fn deref_mut(&mut self) -> &mut Self::Target { + match &mut self.node { + LocalOrRemotePlayer::Local(player) => &mut player.fish, + LocalOrRemotePlayer::Remote(player) => &mut player.fish, + } } - unsafe impl Send for GameApi {} - unsafe impl Sync for GameApi {} +} - enum LocalOrRemotePlayer { - Local(RefMut), - Remote(RefMut), +impl HostGameApi { + fn current_player(&self) -> Option> { + self.current_player.lock().unwrap().map(|p| p.clone()) } - struct FishMut { - node: LocalOrRemotePlayer, + + fn current_remote_player(&self) -> Option> { + self.current_remote_player + .lock() + .unwrap() + .map(|p| p.clone()) } - impl std::ops::Deref for FishMut { - type Target = Fish; - fn deref(&self) -> &Self::Target { - match &self.node { - LocalOrRemotePlayer::Local(player) => &player.fish, - LocalOrRemotePlayer::Remote(player) => &player.fish, + fn current_fish(&self) -> FishMut { + if let Some(handle) = self.current_player() { + let node = scene::get_node(handle); + FishMut { + node: LocalOrRemotePlayer::Local(node), } - } - } - impl std::ops::DerefMut for FishMut { - fn deref_mut(&mut self) -> &mut Self::Target { - match &mut self.node { - LocalOrRemotePlayer::Local(player) => &mut player.fish, - LocalOrRemotePlayer::Remote(player) => &mut player.fish, + } else { + let node = scene::get_node(self.current_remote_player().unwrap()); + FishMut { + node: LocalOrRemotePlayer::Remote(node), } } } +} - impl GameApi { - pub fn with_current_player(&self, player: Handle, mut f: impl FnMut() -> R) -> R { - self.current_player.lock().unwrap().replace(player); - let result = f(); - self.current_player.lock().unwrap().take(); - result - } +impl GameApi for HostGameApi { + fn spawn_bullet(&self) { + let mut bullets = scene::find_node_by_type::().unwrap(); + let fish = self.current_fish(); + bullets.spawn_bullet(fish.pos, fish.facing); + } - pub fn with_remote_player( - &self, - player: Handle, - mut f: impl FnMut() -> R, - ) -> R { - self.current_remote_player.lock().unwrap().replace(player); - let result = f(); - self.current_remote_player.lock().unwrap().take(); - result + fn hit_rect(&self, rect: [f32; 4]) -> u32 { + if let Some(handle) = self.current_player() { + let hit_box = Rect::new(rect[0], rect[1], rect[2], rect[3]); + let node = &mut *scene::get_node(handle); + let others = scene::find_nodes_by_type::(); + let mut hit_count = 0; + for player in others { + if Rect::new(player.pos().x, player.pos().y, 20., 64.).overlaps(&hit_box) { + hit_count += 1; + let mut net = scene::get_node(node.nakama_realtime); + net.kill(&player.id, !node.fish.facing); + } + } + hit_count + } else { + 0 } + } - fn current_player(&self) -> Option> { - self.current_player.lock().unwrap().map(|p| p.clone()) + fn set_sprite_fx(&self, s: bool) { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { + weapon.fx = s; } + } - fn current_remote_player(&self) -> Option> { - self.current_remote_player - .lock() - .unwrap() - .map(|p| p.clone()) - } + fn get_speed(&self) -> [f32; 2] { + let fish = self.current_fish(); + [fish.speed.x, fish.speed.y] + } - fn current_fish(&self) -> FishMut { - if let Some(handle) = self.current_player() { - let node = scene::get_node(handle); - FishMut { - node: LocalOrRemotePlayer::Local(node), - } - } else { - let node = scene::get_node(self.current_remote_player().unwrap()); - FishMut { - node: LocalOrRemotePlayer::Remote(node), - } - } - } + fn set_speed(&self, speed: [f32; 2]) { + let mut fish = self.current_fish(); + fish.speed.x = speed[0]; + fish.speed.y = speed[1]; + } - fn spawn_bullet(&self) { - let mut bullets = scene::find_node_by_type::().unwrap(); - let fish = self.current_fish(); - bullets.spawn_bullet(fish.pos, fish.facing); + fn set_sprite_animation(&self, animation: u32) { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { + weapon.sprite.set_animation(animation as usize); } + } - fn hit_rect(&self, rect: [f32; 4]) -> u32 { - if let Some(handle) = self.current_player() { - let hit_box = Rect::new(rect[0], rect[1], rect[2], rect[3]); - let node = &mut *scene::get_node(handle); - let others = scene::find_nodes_by_type::(); - let mut hit_count = 0; - for player in others { - if Rect::new(player.pos().x, player.pos().y, 20., 64.).overlaps(&hit_box) { - hit_count += 1; - let mut net = scene::get_node(node.nakama_realtime); - net.kill(&player.id, !node.fish.facing); - } - } - hit_count - } else { - 0 + fn set_fx_sprite_animation(&self, animation: u32) { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { + if let Some(sprite) = &mut weapon.fx_sprite { + sprite.set_animation(animation as usize); } } + } - fn set_sprite_fx(&self, s: bool) { - let mut fish = self.current_fish(); - if let Some(weapon) = &mut fish.weapon { - weapon.fx = s; - } + fn set_sprite_frame(&self, frame: u32) { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { + weapon.sprite.set_frame(frame); } + } - fn get_speed(&self) -> [f32; 2] { - let fish = self.current_fish(); - [fish.speed.x, fish.speed.y] + fn set_fx_sprite_frame(&self, frame: u32) { + let mut fish = self.current_fish(); + if let Some(weapon) = &mut fish.weapon { + if let Some(sprite) = &mut weapon.fx_sprite { + sprite.set_frame(frame); + } } + } - fn set_speed(&self, speed: [f32; 2]) { - let mut fish = self.current_fish(); - fish.speed.x = speed[0]; - fish.speed.y = speed[1]; - } + fn facing_dir(&self) -> f32 { + let fish = self.current_fish(); + fish.facing_dir() + } - fn set_sprite_animation(&self, animation: usize) { - let mut fish = self.current_fish(); - if let Some(weapon) = &mut fish.weapon { - weapon.sprite.set_animation(animation); - } - } + fn position(&self) -> [f32; 2] { + let fish = self.current_fish(); + let pos = fish.pos(); + [pos.x, pos.y] + } - fn set_fx_sprite_animation(&self, animation: usize) { - let mut fish = self.current_fish(); - if let Some(weapon) = &mut fish.weapon { - if let Some(sprite) = &mut weapon.fx_sprite { - sprite.set_animation(animation); - } - } + fn nakama_shoot(&self) { + if let Some(handle) = self.current_player() { + let node = &mut *scene::get_node(handle); + let mut nakama = scene::get_node(node.nakama_realtime); + nakama.shoot(); } + } - fn set_sprite_frame(&self, frame: u32) { - let mut fish = self.current_fish(); - if let Some(weapon) = &mut fish.weapon { - weapon.sprite.set_frame(frame); - } - } + fn disarm(&self) { + let mut fish = self.current_fish(); + fish.disarm(); + } - fn set_fx_sprite_frame(&self, frame: u32) { - let mut fish = self.current_fish(); - if let Some(weapon) = &mut fish.weapon { - if let Some(sprite) = &mut weapon.fx_sprite { - sprite.set_frame(frame); - } - } + fn play_sound_once(&self, name: String) { + if let Some(sound) = self.sounds.lock().unwrap().get(&name) { + play_sound_once(*sound); } + } - fn facing_dir(&self) -> f32 { - let fish = self.current_fish(); - fish.facing_dir() - } + fn debug_print(&self, message: String) { + println!("{}", message); + } +} - fn position(&self) -> [f32; 2] { - let fish = self.current_fish(); - let pos = fish.pos(); - [pos.x, pos.y] - } +#[cfg(not(target_arch = "wasm32"))] +pub use native_host::*; +#[cfg(target_arch = "wasm32")] +pub use wasm_host::*; - fn nakama_shoot(&self) { - if let Some(handle) = self.current_player() { - let node = &mut *scene::get_node(handle); - let mut nakama = scene::get_node(node.nakama_realtime); - nakama.shoot(); - } - } +#[cfg(not(target_arch = "wasm32"))] +mod native_host { + use super::*; - fn disarm(&self) { - let mut fish = self.current_fish(); - fish.disarm(); - } + use wasm_plugin_host::WasmPlugin; - fn play_sound_once(&self, name: String) { - if let Some(sound) = self.sounds.lock().unwrap().get(&name) { - play_sound_once(*sound); - } - } + pub struct PluginRegistry(HashMap); - fn debug_print(&self, message: String) { - println!("{}", message); - } + pub struct Plugin { + pub wasm_plugin: WasmPlugin, + pub game_api: HostGameApi, } impl PluginRegistry { @@ -264,100 +248,101 @@ mod native_host { { if let Ok(entry) = entry { if entry.path().to_str().unwrap().contains(".wasm") { - let game_api = GameApi::default(); - let mut builder = wasm_plugin_host::WasmPluginBuilder::from_file(entry.path()) - .expect(&format!("Failed to load plugin {:?}", entry.path())); + let game_api = HostGameApi::default(); + let mut builder = + wasm_plugin_host::WasmPluginBuilder::from_file(entry.path()) + .expect(&format!("Failed to load plugin {:?}", entry.path())); // TODO: This should probably be a macro or something to reduce boilerplate builder = builder.import_function_with_context( "spawn_bullet", game_api.clone(), - |ctx: &GameApi| { + |ctx: &HostGameApi| { ctx.spawn_bullet(); }, ); builder = builder.import_function_with_context( "hit_rect", game_api.clone(), - |ctx: &GameApi, rect: [f32; 4]| ctx.hit_rect(rect), + |ctx: &HostGameApi, rect: [f32; 4]| ctx.hit_rect(rect), ); builder = builder.import_function_with_context( "set_sprite_fx", game_api.clone(), - |ctx: &GameApi, s: bool| { + |ctx: &HostGameApi, s: bool| { ctx.set_sprite_fx(s); }, ); builder = builder.import_function_with_context( "get_speed", game_api.clone(), - |ctx: &GameApi| ctx.get_speed(), + |ctx: &HostGameApi| ctx.get_speed(), ); builder = builder.import_function_with_context( "set_speed", game_api.clone(), - |ctx: &GameApi, s: [f32; 2]| { + |ctx: &HostGameApi, s: [f32; 2]| { ctx.set_speed(s); }, ); builder = builder.import_function_with_context( "set_sprite_animation", game_api.clone(), - |ctx: &GameApi, animation: usize| { + |ctx: &HostGameApi, animation: u32| { ctx.set_sprite_animation(animation); }, ); builder = builder.import_function_with_context( "set_fx_sprite_animation", game_api.clone(), - |ctx: &GameApi, animation: usize| { + |ctx: &HostGameApi, animation: u32| { ctx.set_fx_sprite_animation(animation); }, ); builder = builder.import_function_with_context( "set_sprite_frame", game_api.clone(), - |ctx: &GameApi, frame: u32| { + |ctx: &HostGameApi, frame: u32| { ctx.set_sprite_frame(frame); }, ); builder = builder.import_function_with_context( "set_fx_sprite_frame", game_api.clone(), - |ctx: &GameApi, frame: u32| { + |ctx: &HostGameApi, frame: u32| { ctx.set_fx_sprite_frame(frame); }, ); builder = builder.import_function_with_context( "facing_dir", game_api.clone(), - |ctx: &GameApi| ctx.facing_dir(), + |ctx: &HostGameApi| ctx.facing_dir(), ); builder = builder.import_function_with_context( "position", game_api.clone(), - |ctx: &GameApi| ctx.position(), + |ctx: &HostGameApi| ctx.position(), ); builder = builder.import_function_with_context( "disarm", game_api.clone(), - |ctx: &GameApi| ctx.disarm(), + |ctx: &HostGameApi| ctx.disarm(), ); builder = builder.import_function_with_context( "play_sound_once", game_api.clone(), - |ctx: &GameApi, sound: String| { + |ctx: &HostGameApi, sound: String| { ctx.play_sound_once(sound); }, ); builder = builder.import_function_with_context( "nakama_shoot", game_api.clone(), - |ctx: &GameApi| ctx.nakama_shoot(), + |ctx: &HostGameApi| ctx.nakama_shoot(), ); builder = builder.import_function_with_context( "debug_print", game_api.clone(), - |ctx: &GameApi, message: String| ctx.debug_print(message), + |ctx: &HostGameApi, message: String| ctx.debug_print(message), ); let mut plugin = builder.finish().unwrap(); let description: PluginDescription = @@ -373,7 +358,10 @@ mod native_host { { let mut sounds = game_api.sounds.lock().unwrap(); for sound in description.sounds { - sounds.insert(sound.name, load_sound_data(&sound.bytes).await.unwrap()); + sounds.insert( + sound.name, + load_sound_data(&sound.bytes).await.unwrap(), + ); } } @@ -395,12 +383,129 @@ mod native_host { self.0.get_mut(&plugin_id) } } + + impl Plugin { + pub fn with_current_player( + &mut self, + player: Handle, + mut f: impl FnMut(&mut Plugin) -> R, + ) -> R { + self.game_api.current_player.lock().unwrap().replace(player); + let result = f(self); + self.game_api.current_player.lock().unwrap().take(); + result + } + + pub fn with_current_remote_player( + &mut self, + player: Handle, + mut f: impl FnMut(&mut Plugin) -> R, + ) -> R { + self.game_api + .current_remote_player + .lock() + .unwrap() + .replace(player); + let result = f(self); + self.game_api.current_remote_player.lock().unwrap().take(); + result + } + } + + impl PluginApi for Plugin { + fn new_instance(&mut self, item_type: ItemType, item_id: ItemInstanceId) { + self.wasm_plugin + .call_function_with_argument("new_instance", &(item_type, item_id)) + .unwrap() + } + + fn destroy_instance(&mut self, item_id: ItemInstanceId) { + self.wasm_plugin + .call_function_with_argument("destroy_instance", &item_id) + .unwrap() + } + + fn uses_remaining(&mut self, item_id: ItemInstanceId) -> Option<(u32, u32)> { + self.wasm_plugin + .call_function_with_argument("uses_remaining", &item_id) + .unwrap() + } + + fn update_shoot(&mut self, item_id: ItemInstanceId, time: f64) -> bool { + self.wasm_plugin + .call_function_with_argument("update_shoot", &(item_id, time)) + .unwrap() + } + + fn update_remote_shoot(&mut self, item_id: ItemInstanceId, time: f64) -> bool { + self.wasm_plugin + .call_function_with_argument("update_remote_shoot", &(item_id, time)) + .unwrap() + } + } } #[cfg(target_arch = "wasm32")] mod wasm_host { use super::*; - pub struct Plugin; + use core_plugin::{ + destroy_instance, new_instance, plugin_description, update_remote_shoot, update_shoot, + uses_remaining, + }; + + pub struct Plugin { + pub game_api: HostGameApi, + } + + impl Plugin { + pub fn with_current_player( + &mut self, + player: Handle, + mut f: impl FnMut(&mut Plugin) -> R, + ) -> R { + self.game_api.current_player.lock().unwrap().replace(player); + let result = f(self); + self.game_api.current_player.lock().unwrap().take(); + result + } + + pub fn with_current_remote_player( + &mut self, + player: Handle, + mut f: impl FnMut(&mut Plugin) -> R, + ) -> R { + self.game_api + .current_remote_player + .lock() + .unwrap() + .replace(player); + let result = f(self); + self.game_api.current_remote_player.lock().unwrap().take(); + result + } + } + + impl PluginApi for Plugin { + fn new_instance(&mut self, item_type: ItemType, item_id: ItemInstanceId) { + new_instance(item_type, item_id); + } + + fn destroy_instance(&mut self, item_id: ItemInstanceId) { + destroy_instance(item_id); + } + + fn uses_remaining(&mut self, item_id: ItemInstanceId) -> Option<(u32, u32)> { + uses_remaining(item_id) + } + + fn update_shoot(&mut self, item_id: ItemInstanceId, time: f64) -> bool { + update_shoot(item_id, time, &self.game_api) + } + + fn update_remote_shoot(&mut self, item_id: ItemInstanceId, time: f64) -> bool { + update_remote_shoot(item_id, time, &self.game_api) + } + } pub struct PluginRegistry; impl PluginRegistry { From b9832dd387d2677e74c2bca041b19bfc22977788 Mon Sep 17 00:00:00 2001 From: Alec Deason Date: Tue, 11 May 2021 10:52:06 -0700 Subject: [PATCH 14/14] Update load_sound_data to load_sound_from_bytes --- src/plugin.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugin.rs b/src/plugin.rs index 5970c45..790f857 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -1,5 +1,5 @@ use macroquad::{ - audio::{load_sound_data, play_sound_once, Sound}, + audio::{load_sound_from_bytes, play_sound_once, Sound}, experimental::{ animation::{AnimatedSprite, Animation}, scene::{self, Handle, RefMut}, @@ -360,7 +360,7 @@ mod native_host { for sound in description.sounds { sounds.insert( sound.name, - load_sound_data(&sound.bytes).await.unwrap(), + load_sound_from_bytes(&sound.bytes).await.unwrap(), ); } }