├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── example ├── Cargo.toml ├── core │ ├── Cargo.toml │ └── src │ │ └── main.rs └── game_mod │ ├── Cargo.toml │ └── src │ └── lib.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | 4 | Cargo.lock 5 | /example/Cargo.lock 6 | /example/target 7 | 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bevy-modding" 3 | version = "0.1.0" 4 | authors = ["Arkadiusz Zylkowski "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | bevy = "0.5.0" 11 | bevy-modding = { version = "0.1.0", path = "../../../bevy-modding"} -------------------------------------------------------------------------------- /example/core/src/main.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::*; 2 | use bevy_modding::*; 3 | 4 | fn main() { 5 | App::build() 6 | .add_plugin(ModdingPlugin::new( "target/debug/mods")) 7 | .run(); 8 | } 9 | -------------------------------------------------------------------------------- /example/game_mod/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "game_mod" 3 | version = "0.1.0" 4 | authors = ["Arkadiusz Zylkowski "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [lib] 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | live-reload = "0.2.0" 14 | bevy = "0.5.0" 15 | bevy-modding = { version = "0.1.0", path = "../../../bevy-modding"} -------------------------------------------------------------------------------- /example/game_mod/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] // Let's you use macro from other crate 2 | 3 | use bevy::prelude::*; 4 | use bevy_modding::*; 5 | 6 | #[repr(C)] 7 | struct State; // State struct, it's needed by crate that loads your mod 8 | 9 | mod_loader!{ 10 | host: Host; 11 | state: State; 12 | init: init; // Passing initialize function for your mod 13 | } 14 | 15 | fn hello_from_mod(){ 16 | println!("Hello from mod!"); 17 | } 18 | 19 | fn init(host: &mut Host, _state: &mut State) { 20 | host.app_builder 21 | .add_system(hello_from_mod.system()); 22 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate live_reload; 2 | 3 | use bevy::prelude::*; 4 | use walkdir::WalkDir; 5 | 6 | /// Live reload api for communication between mod and main game 7 | pub struct Host<'a> { 8 | pub app_builder: &'a mut AppBuilder, 9 | } 10 | 11 | type ModLoader<'a> = live_reload::Reloadable>; 12 | pub struct ModdingPlugin<'a> { 13 | mod_folder_path: &'a str, 14 | } 15 | 16 | impl<'a> ModdingPlugin<'a> { 17 | pub fn new(mod_folder_path: &'a str) -> Self { 18 | ModdingPlugin { mod_folder_path } 19 | } 20 | } 21 | 22 | impl Plugin for ModdingPlugin<'static> { 23 | fn build(&self, app: &mut AppBuilder) { 24 | for entry in WalkDir::new(self.mod_folder_path) 25 | .follow_links(true) 26 | .into_iter() 27 | .filter_map(|e| e.ok()) 28 | { 29 | let f_name = entry.file_name().to_string_lossy(); 30 | if f_name.ends_with(".so"){ 31 | let host_api = Host { app_builder: app }; 32 | ModLoader::new(entry.path(),host_api).expect("Cannot load the mod!"); 33 | } 34 | } 35 | } 36 | } 37 | 38 | #[macro_export] 39 | macro_rules! mod_loader { 40 | (host: $Host:ty; 41 | state: $State:ty; 42 | init: $init:ident;) => { 43 | fn cast<'a>(raw_state: *mut ()) -> &'a mut $State { 44 | unsafe { &mut *(raw_state as *mut $State) } 45 | } 46 | 47 | fn init_wrapper(host: &mut $Host, raw_state: *mut ()) { 48 | $init(host, cast(raw_state)) 49 | } 50 | 51 | fn reload_wrapper(host: &mut $Host, raw_state: *mut ()) {} 52 | 53 | fn update_wrapper(host: &mut $Host, raw_state: *mut ()) -> ::live_reload::ShouldQuit { 54 | live_reload::ShouldQuit::No 55 | } 56 | 57 | fn unload_wrapper(host: &mut $Host, raw_state: *mut ()) {} 58 | 59 | fn deinit_wrapper(host: &mut $Host, raw_state: *mut ()) {} 60 | 61 | #[no_mangle] 62 | pub static RELOAD_API: ::live_reload::internals::ReloadApi<$Host> = 63 | ::live_reload::internals::ReloadApi { 64 | size: ::std::mem::size_of::<$State>, 65 | init: init_wrapper, 66 | reload: reload_wrapper, 67 | update: update_wrapper, 68 | unload: unload_wrapper, 69 | deinit: deinit_wrapper, 70 | }; 71 | }; 72 | } 73 | --------------------------------------------------------------------------------