live_reload/
lib.rs

1#![deny(missing_docs)]
2
3//! A library for doing live-reloading game development.
4//!
5//! This is inspired by the article ["Interactive Programming in C"][] by Chris
6//! Wellons, and the video ["Loading Game Code Dynamically"][] from Handmade
7//! Hero by Casey Muratori.
8//!
9//! ["Interactive Programming in C"]: http://nullprogram.com/blog/2014/12/23/
10//! ["Loading Game Code Dynamically"]: https://www.youtube.com/watch?v=WMSBRk5WG58
11//!
12//! The general idea is that your main host program is a wrapper around a
13//! dynamic library that does all the interesting work of your game. This means
14//! that you can simply reload the library while the game is still running, and
15//! have your game update live. As a consequence however, you can't have any
16//! global state in your library, everything must be owned by the host in order
17//! to avoid getting unloaded with the library.
18//!
19//! In order to call back into the host program, you specify a `Host` type
20//! containing function pointers to services in the host program. This `Host`
21//! struct will be passed along with your program state. The `Host` type should
22//! always be defined in a separate module so that both the host program and the
23//! reloadable library use a consistent host type. The current recommended
24//! method is to define your host type in its own module. Then use that module
25//! from both the host and the reloadable library. If your project organization
26//! puts the common module in a parent, you can always use the `#[path=...]`
27//! meta on the module, for example:
28//!
29//! ```rust,ignore
30//! #[path="../host_api.rs"]
31//! mod host_api;
32//! ```
33//!
34//! While designing your host and library, keep in mind the role of the two
35//! communication types:
36//!
37//! - State is isolated to the reloadable library, the main program knows
38//!   nothing about it except for its size so that it can keep it allocated.
39//!   This lets you change the contents of the State struct without restarting
40//!   the whole program. This is intended to handle the state of the game world,
41//!   independent of the host program.
42//!
43//! - Host is defined by the host program and the layout is visible to both
44//!   sides of the bridge. This means that it has to remain the same during a
45//!   run of the game engine. This should hold resources that can only be
46//!   produced by the host program, and function pointers to services that can
47//!   only be provided by the host program. (Anything that requires global state
48//!   like system allocators, graphics contexts, input handling, etc etc.)
49//!
50//! See the Host Example and Library Example sections for instructions on how to
51//! build a reloadable application.
52//!
53//! # Host Example
54//!
55//! A program that hosts a reloadable library will need to load the library, and
56//! then periodically reload it. The [`Reloadable`][] automatically installs a
57//! filesystem watcher for you so that it knows when the library file has been
58//! updated or replaced, and the [`reload`][] method will only actually perform
59//! a reload if the file has changed. The core of your main loop will therefore
60//! usually look something like this:
61//!
62//! ```rust,no_run
63//! use std::thread;
64//!
65//! mod host_api {
66//!     // This should always be in a different file
67//!     pub struct HostApi {
68//!         pub print: fn(&str),
69//!     }
70//! }
71//! use host_api::HostApi;
72//!
73//! type App = live_reload::Reloadable<HostApi>;
74//!
75//! fn print(msg: &str) {
76//!     print!("{}", msg);
77//! }
78//!
79//! fn main() {
80//!     let mut prog = App::new(
81//!         "target/debug/libreload.dylib",
82//!         HostApi { print: print },
83//!     ).expect("Should successfully load");
84//!     'main: loop {
85//!         if prog.update() == live_reload::ShouldQuit::Yes {
86//!             break 'main;
87//!         }
88//!         prog.reload().expect("Should successfully reload");
89//!     }
90//! }
91//! ```
92//!
93//! # Library Example
94//!
95//! A live-reloadable library needs to register its entry-points so that the
96//! host program can find them. The [`live_reload!`][] macro lets you do this
97//! conveniently.
98//!
99//! The lifecycle of your reloadable library will happen in a few stages:
100//!
101//! - `init` gets called at the very beginning of the program, when the host
102//!   starts for the first time.
103//! - `reload` gets called on each library load, including the first time. This
104//!   should be usually empty, but when you're in development, you might want to
105//!   reset things here, or migrate data, or things like that. The pointer
106//!   you're passed will refer to the same struct that you had when the previous
107//!   library was unloaded, so it might not be properly initialized. You should
108//!   try to make your struct be `#[repr(C)]`, and only add members at the end
109//!   to minimize the problems of reloading.
110//! - `update` gets called at the host program's discretion. You'll probably end
111//!   up calling this once per frame. In addition to doing whatever work you
112//!   were interested in, `update` also returns a value indicating whether the
113//!   host program should quit.
114//! - `unload` gets called before a library unloads. This will probably be empty
115//!   even more often than `reload`, but you might need it for some debugging or
116//!   data migration purpose.
117//! - `deinit` gets called when the host program is actually shutting down--it's
118//!   called on the drop of the [`Reloadable`][].
119//!
120//! Here's an example of a live-reloadable library that handles a counter.
121//!
122//! ```rust
123//! #[macro_use] extern crate live_reload;
124//! # fn main() {}
125//! use live_reload::ShouldQuit;
126//!
127//! mod host_api {
128//!     // This should always be in a different file.
129//!     pub struct Host {
130//!         pub print: fn(&str),
131//!     }
132//! }
133//!
134//! use host_api::Host;
135//!
136//! live_reload! {
137//!     host: Host;
138//!     state: State;
139//!     init: my_init;
140//!     reload: my_reload;
141//!     update: my_update;
142//!     unload: my_unload;
143//!     deinit: my_deinit;
144//! }
145//!
146//! struct State {
147//!     counter: u64,
148//! }
149//!
150//! fn my_init(host: &mut Host, state: &mut State) {
151//!     state.counter = 0;
152//!     (host.print)("Init! Counter: 0.");
153//! }
154//!
155//! fn my_reload(host: &mut Host, state: &mut State) {
156//!     (host.print)(&format!("Reloaded at {}.", state.counter));
157//! }
158//!
159//! fn my_update(host: &mut Host, state: &mut State) -> ShouldQuit {
160//!     state.counter += 1;
161//!     (host.print)(&format!("Counter: {}.", state.counter));
162//!     ShouldQuit::No
163//! }
164//!
165//! fn my_unload(host: &mut Host, state: &mut State) {
166//!     (host.print)(&format!("Unloaded at {}.", state.counter));
167//! }
168//!
169//! fn my_deinit(host: &mut Host, state: &mut State) {
170//!     (host.print)(&format!("Goodbye! Reached a final value of {}.", state.counter));
171//! }
172//! ```
173//!
174//! [`Reloadable`]: struct.Reloadable.html
175//! [`reload`]: struct.Reloadable.html#method.reload
176//! [`live_reload!`]: macro.live_reload.html
177
178extern crate notify;
179extern crate libloading;
180
181use std::path::{Path, PathBuf};
182use std::time::Duration;
183use std::sync::mpsc::{channel, Receiver};
184
185use notify::{Watcher, RecommendedWatcher};
186use libloading::Library;
187
188#[cfg(unix)]
189type Symbol<T> = libloading::os::unix::Symbol<T>;
190#[cfg(windows)]
191type Symbol<T> = libloading::os::windows::Symbol<T>;
192
193struct AppSym<Host> {
194    /// This needs to be present so that the library will be closed on drop
195    _lib: Library,
196    api: Symbol<*mut internals::ReloadApi<Host>>,
197}
198
199// @Todo: Flesh out this documentation
200/// A `Reloadable` represents a handle to library that can be live reloaded.
201pub struct Reloadable<Host> {
202    path: PathBuf,
203    sym: Option<AppSym<Host>>,
204    state: Vec<u64>,
205    _watcher: RecommendedWatcher,
206    rx: Receiver<notify::DebouncedEvent>,
207    host: Host,
208}
209
210/// The errors that can occur while working with a `Reloadable` object.
211#[derive(Debug)]
212pub enum Error {
213    /// An I/O error occurred while trying to load or reload the library. This
214    /// can indicate that the file is missing, or that the library didn't have
215    /// the expected `RELOAD_API` symbol.
216    // @Diagnostics: Add an error type to distinguish this latter situation
217    Io(std::io::Error),
218    /// An error occurred while creating the filesystem watcher.
219    Watch(notify::Error),
220    /// The `Host` type of the host and library don't match.
221    MismatchedHost,
222}
223
224impl From<std::io::Error> for Error {
225    fn from(err: std::io::Error) -> Error {
226        Error::Io(err)
227    }
228}
229
230impl From<notify::Error> for Error {
231    fn from(err: notify::Error) -> Error {
232        Error::Watch(err)
233    }
234}
235
236impl std::fmt::Display for Error {
237    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
238        write!(fmt, "{:?}", self)
239    }
240}
241
242impl std::error::Error for Error {
243    fn description(&self) -> &str {
244        match *self {
245            Error::Io(ref err) => err.description(),
246            Error::Watch(ref err) => err.description(),
247            Error::MismatchedHost => "mismatch between host and library's Host types",
248        }
249    }
250}
251
252impl<Host> AppSym<Host> {
253    fn new<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
254        let library = Library::new(path.as_ref())?;
255        let api = unsafe {
256            library
257                .get::<*mut internals::ReloadApi<Host>>(b"RELOAD_API")?
258                .into_raw()
259        };
260        Ok(AppSym {
261            _lib: library,
262            api: api,
263        })
264    }
265}
266
267impl<Host> Reloadable<Host> {
268    /// Create a new Reloadable library.
269    ///
270    /// This takes the path to a dynamic library containing a `RELOAD_API`
271    /// symbol that exports the functions needed for live reloading. In order to
272    /// define this symbol in your own reloadable libraries, see the
273    /// [`live_reload!`][] macro. This will load the library and initialize a
274    /// filesystem watcher pointing to the file in order to know when the
275    /// library has changed.
276    ///
277    /// [`live_reload!`]: macro.live_reload.html
278    pub fn new<P: AsRef<Path>>(path: P, host: Host) -> Result<Self, Error> {
279        let sym = AppSym::new(&path)?;
280        let size = (unsafe { &**sym.api }.size)();
281        let (tx, rx) = channel();
282        let mut watcher = notify::watcher(tx, Duration::from_secs(1))?;
283        let mut new_path = PathBuf::new();
284        new_path.push(path);
285        watcher.watch(
286            new_path.parent().unwrap(),
287            notify::RecursiveMode::NonRecursive,
288        )?;
289        let mut app = Reloadable {
290            path: new_path.canonicalize()?,
291            sym: Some(sym),
292            state: Vec::new(),
293            _watcher: watcher,
294            rx: rx,
295            host: host,
296        };
297        app.realloc_buffer(size);
298        if let Some(AppSym { ref mut api, .. }) = app.sym {
299            (unsafe { &***api }.init)(&mut app.host, Self::get_state_ptr(&mut app.state));
300        }
301        Ok(app)
302    }
303
304    /// Reload the library if it has changed, otherwise do nothing.
305    ///
306    /// This will consult with the filesystem watcher, and if the library has
307    /// been recreated or updated, it will reload the library. See
308    /// [`reload_now`][] for details on what happens when a library is reloaded.
309    ///
310    /// [`reload_now`]: struct.Reloadable.html#method.reload_now
311    pub fn reload(&mut self) -> Result<(), Error> {
312        let mut should_reload = false;
313        while let Ok(evt) = self.rx.try_recv() {
314            use notify::DebouncedEvent::*;
315            match evt {
316                NoticeWrite(ref path) |
317                Write(ref path) |
318                Create(ref path) => {
319                    if *path == self.path {
320                        should_reload = true;
321                    }
322                }
323                _ => {}
324            }
325        }
326
327        if should_reload || self.sym.is_none() {
328            self.reload_now()
329        } else {
330            Ok(())
331        }
332    }
333
334    /// Immediately reload the library without checking whether it has changed.
335    ///
336    /// This first calls `unload` on the currently loaded library, then unloads
337    /// the dynamic library. Next, it loads the new dynamic library, and calls
338    /// `reload` on that. If the new library fails to load, this method will
339    /// return an `Err` and the `Reloadable` will be left with no library
340    /// loaded.
341    ///
342    /// [`update`]: struct.Reloadable.html#method.update
343    pub fn reload_now(&mut self) -> Result<(), Error> {
344        if let Some(AppSym { ref mut api, .. }) = self.sym {
345            (unsafe { &***api }.unload)(&mut self.host, Self::get_state_ptr(&mut self.state));
346        }
347        self.sym = None;
348        let sym = AppSym::new(&self.path)?;
349        // @Avoid reallocating if unnecessary
350        self.realloc_buffer((unsafe { &**sym.api }.size)());
351        (unsafe { &**sym.api }.reload)(&mut self.host, Self::get_state_ptr(&mut self.state));
352        self.sym = Some(sym);
353
354        Ok(())
355    }
356
357    /// Call the update method on the library.
358    ///
359    /// If no library is currently loaded, this does nothing and returns
360    /// [`ShouldQuit::No`](enum.ShouldQuit.html#).
361    pub fn update(&mut self) -> ShouldQuit {
362        if let Some(AppSym { ref mut api, .. }) = self.sym {
363            (unsafe { &***api }.update)(&mut self.host, Self::get_state_ptr(&mut self.state))
364        } else {
365            ShouldQuit::No
366        }
367    }
368
369    /// Reallocate the buffer used to store the `State`.
370    fn realloc_buffer(&mut self, size: usize) {
371        let alloc_size_u64s = (size + 7) / 8;
372        self.state.resize(alloc_size_u64s, 0);
373    }
374
375    /// Get a void pointer to the `State` buffer.
376    fn get_state_ptr(buffer: &mut Vec<u64>) -> *mut () {
377        buffer.as_mut_ptr() as *mut ()
378    }
379
380    /// Get a reference to the `Host` struct>
381    pub fn host(&self) -> &Host { &self.host }
382
383    /// Get a mutable reference to the `Host` struct.
384    pub fn host_mut(&mut self) -> &mut Host { &mut self.host }
385}
386
387impl<Host> Drop for Reloadable<Host> {
388    fn drop(&mut self) {
389        if let Some(AppSym { ref mut api, .. }) = self.sym {
390            unsafe {
391                ((***api).deinit)(&mut self.host, Self::get_state_ptr(&mut self.state));
392            }
393        }
394    }
395}
396
397
398/// Should the main program quit? More self-documenting than a boolean!
399///
400/// This type is returned by the [`update`][] method, since with a boolean it's
401/// often unclear if `true` means "should continue" or "should quit".
402///
403/// [`update`]: struct.Reloadable.html#method.update
404#[derive(Debug, PartialEq, Eq)]
405pub enum ShouldQuit {
406    /// The wrapped library thinks the main program should continue running.
407    No = 0,
408    /// The wrapped library thinks the main program should quit now.
409    Yes = 1,
410}
411
412/// Exported for compilation reasons but not useful, only look if you're curious.
413///
414/// This module holds to the `ReloadApi` struct, which is what what is looked up
415/// by the `Reloadable` in order to communicate with the reloadable library. It
416/// needs to be exported in order to avoid forcing the type definition into the
417/// pub symbols of the wrapped library. An instance of `ReloadApi` called
418/// `RELOAD_API` is generated by the [`live_reload!`][] macro.
419///
420/// [`live_reload!`]: ../macro.live_reload.html
421pub mod internals {
422    /// Contains function pointers for all the parts of the reloadable object lifecycle.
423    #[repr(C)]
424    pub struct ReloadApi<Host> {
425        /// Returns the size of the State struct so that the host can allocate
426        /// space for it.
427        pub size: fn() -> usize,
428        /// Initializes the State struct when the program is first started.
429        pub init: fn(&mut Host, *mut ()),
430        /// Makes any necessary updates when the program is reloaded.
431        ///
432        /// This will probably be normally empty. If you changed the State
433        /// struct since the last compile, then it won't necessarily be
434        /// correctly initialized. For safety, you should make your State struct
435        /// `#[repr(C)]` and only add members at the end.
436        pub reload: fn(&mut Host, *mut ()),
437        /// Update the
438        pub update: fn(&mut Host, *mut ()) -> super::ShouldQuit,
439        /// Prepare for the library to be unloaded before a new version loads.
440        ///
441        /// This will probably normally be empty except for short periods in
442        /// development when you're making lots of live changes and need to do
443        /// some kind of migration.
444        pub unload: fn(&mut Host, *mut ()),
445        /// Do final shutdowns before the program completely quits.
446        pub deinit: fn(&mut Host, *mut ()),
447    }
448}
449
450/// Declare the API functions for a live-reloadable library.
451///
452/// This generates wrappers around higher-level lifecycle functions, and then
453/// exports them in a struct that the reloader can find.
454///
455/// You need to to specify the host API type, define a struct that represents
456/// the state of your program, and then define methods for `init`, `reload`,
457/// `update`, `unload`, and `deinit`. `init` and `deinit` are called at the very
458/// beginning and end of the program, and `reload` and `unload` are called
459/// immediately after and before the library is loaded/reloaded. `update` is
460/// called by the wrapping application as needed.
461///
462/// # Example
463///
464/// ```rust
465/// # #[macro_use] extern crate live_reload;
466/// # fn main() {}
467/// # #[repr(C)] struct State {}
468/// # mod host_api { pub struct Host; }
469/// # use host_api::Host;
470/// # fn my_init(_: &mut Host, _: &mut State) {}
471/// # fn my_reload(_: &mut Host, _: &mut State) {}
472/// # fn my_unload(_: &mut Host, _: &mut State) {}
473/// # fn my_deinit(_: &mut Host, _: &mut State) {}
474/// # use live_reload::ShouldQuit;
475/// # fn my_update(_: &mut Host, _: &mut State) -> ShouldQuit { ShouldQuit::No }
476/// live_reload! {
477///     host: host_api::Host;
478///     state: State;
479///     init: my_init;
480///     reload: my_reload;
481///     update: my_update;
482///     unload: my_unload;
483///     deinit: my_deinit;
484/// }
485/// ```
486#[macro_export]
487macro_rules! live_reload {
488    (host: $Host:ty;
489     state: $State:ty;
490     init: $init:ident;
491     reload: $reload:ident;
492     update: $update:ident;
493     unload: $unload:ident;
494     deinit: $deinit:ident;) => {
495
496        fn cast<'a>(raw_state: *mut ()) -> &'a mut $State {
497            unsafe { &mut *(raw_state as *mut $State) }
498        }
499
500        fn init_wrapper(host: &mut $Host, raw_state: *mut ()) {
501            $init(host, cast(raw_state))
502        }
503
504        fn reload_wrapper(host: &mut $Host, raw_state: *mut ()) {
505            $reload(host, cast(raw_state))
506        }
507
508        fn update_wrapper(host: &mut $Host, raw_state: *mut ())
509            -> ::live_reload::ShouldQuit
510        {
511            $update(host, cast(raw_state))
512        }
513
514        fn unload_wrapper(host: &mut $Host, raw_state: *mut ()) {
515            $unload(host, cast(raw_state))
516        }
517
518        fn deinit_wrapper(host: &mut $Host, raw_state: *mut ()) {
519            $deinit(host, cast(raw_state))
520        }
521
522        #[no_mangle]
523        pub static RELOAD_API: ::live_reload::internals::ReloadApi<$Host> =
524            ::live_reload::internals::ReloadApi
525        {
526            size: ::std::mem::size_of::<$State>,
527            init: init_wrapper,
528            reload: reload_wrapper,
529            update: update_wrapper,
530            unload: unload_wrapper,
531            deinit: deinit_wrapper,
532        };
533    }
534}