Skip to content

Commit 076903b

Browse files
authored
Turned quill::plugin into a proc macro (#528)
1 parent b2ecc8c commit 076903b

File tree

14 files changed

+142
-113
lines changed

14 files changed

+142
-113
lines changed

Cargo.lock

Lines changed: 11 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ members = [
1515
"quill/sys",
1616
"quill/common",
1717
"quill/api",
18+
"quill/api/plugin-macro",
1819
"quill/plugin-format",
1920
"quill/cargo-quill",
2021

quill/api/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ authors = ["caelunshun <caelunshun@gmail.com>"]
55
edition = "2018"
66

77
[dependencies]
8+
plugin-macro = { path = "./plugin-macro" }
89
libcraft-core = { path = "../../libcraft/core" }
910
libcraft-particles = { path = "../../libcraft/particles" }
1011
libcraft-blocks = { path = "../../libcraft/blocks" }

quill/api/plugin-macro/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "plugin-macro"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[lib]
7+
proc-macro = true
8+
9+
[dependencies]
10+
syn = { version = "1.0.86", features = ["full"] }
11+
quote = "1.0.14"

quill/api/plugin-macro/src/lib.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use proc_macro::TokenStream;
2+
use quote::quote;
3+
use syn::{parse_macro_input, Item};
4+
5+
/// Invoke this macro in your plugin's main.rs.
6+
///
7+
/// Give it the name of your struct implementing `Plugin`.
8+
///
9+
/// # Example
10+
/// ```ignore
11+
/// // main.rs
12+
/// use quill::{Plugin, Setup, Game};
13+
///
14+
/// #[quill::plugin]
15+
/// pub struct MyPlugin {
16+
/// // plugin state goes here
17+
/// }
18+
///
19+
/// impl Plugin for MyPlugin {
20+
/// fn enable(game: &mut Game, setup: &mut Setup<Self>) -> Self {
21+
/// // Initialize plugin state...
22+
/// Self {}
23+
/// }
24+
///
25+
/// fn disable(self, game: &mut Game) {
26+
/// // Clean up...
27+
/// }
28+
/// }
29+
/// ```
30+
#[proc_macro_attribute]
31+
pub fn plugin(_attr: TokenStream, mut item: TokenStream) -> TokenStream {
32+
let cloned_item = item.clone();
33+
let input = parse_macro_input!(cloned_item as Item);
34+
35+
let name = match input {
36+
Item::Enum(itm_enum) => itm_enum.ident,
37+
Item::Struct(itm_str) => itm_str.ident,
38+
_ => panic!("Only structs or enums can be #[quill::plugin]!"),
39+
};
40+
let res = quote! {
41+
// `static mut` can be used without synchronization because the host
42+
// guarantees it will not invoke plugin systems outside of the main thread.
43+
static mut PLUGIN: Option<#name> = None;
44+
45+
// Exports to the host required for all plugins
46+
#[no_mangle]
47+
#[doc(hidden)]
48+
#[cfg(target_arch = "wasm32")]
49+
pub unsafe extern "C" fn quill_setup() {
50+
let plugin: #name =
51+
quill::Plugin::enable(&mut ::quill::Game::new(), &mut ::quill::Setup::new());
52+
PLUGIN = Some(plugin);
53+
}
54+
55+
#[no_mangle]
56+
#[doc(hidden)]
57+
#[cfg(not(target_arch = "wasm32"))]
58+
pub unsafe extern "C" fn quill_setup(
59+
context: *const (),
60+
vtable_ptr: *const u8,
61+
vtable_len: usize,
62+
) {
63+
// Set up vtable and host context for quill_sys.
64+
let vtable_bytes = ::std::slice::from_raw_parts(vtable_ptr, vtable_len);
65+
let vtable: ::std::collections::HashMap<&str, usize> =
66+
::quill::bincode::deserialize(vtable_bytes).expect("invalid vtable");
67+
68+
::quill::sys::init_host_context(context);
69+
::quill::sys::init_host_vtable(&vtable)
70+
.expect("invalid vtable (check that the plugin and host are up to date)");
71+
72+
let plugin: #name =
73+
quill::Plugin::enable(&mut ::quill::Game::new(), &mut ::quill::Setup::new());
74+
PLUGIN = Some(plugin);
75+
}
76+
77+
#[no_mangle]
78+
#[doc(hidden)]
79+
pub unsafe extern "C" fn quill_allocate(size: usize, align: usize) -> *mut u8 {
80+
std::alloc::alloc(std::alloc::Layout::from_size_align_unchecked(size, align))
81+
}
82+
83+
#[no_mangle]
84+
#[doc(hidden)]
85+
pub unsafe extern "C" fn quill_deallocate(ptr: *mut u8, size: usize, align: usize) {
86+
std::alloc::dealloc(
87+
ptr,
88+
std::alloc::Layout::from_size_align_unchecked(size, align),
89+
)
90+
}
91+
92+
#[no_mangle]
93+
#[doc(hidden)]
94+
pub unsafe extern "C" fn quill_run_system(data: *mut u8) {
95+
let system = &mut *data.cast::<Box<dyn FnMut(&mut #name, &mut ::quill::Game)>>();
96+
let plugin = PLUGIN.as_mut().expect("quill_setup never called");
97+
system(plugin, &mut ::quill::Game::new());
98+
}
99+
100+
/// Never called by Quill, but this is needed
101+
/// to avoid linker errors with WASI.
102+
#[doc(hidden)]
103+
fn main() {}
104+
};
105+
item.extend(TokenStream::from(res));
106+
107+
item
108+
}

quill/api/src/lib.rs

Lines changed: 2 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub extern crate bincode;
3232
#[doc(hidden)]
3333
pub extern crate quill_sys as sys;
3434

35+
pub use plugin_macro::plugin;
36+
3537
/// Implement this trait for your plugin's struct.
3638
pub trait Plugin: Sized {
3739
/// Invoked when the plugin is enabled.
@@ -55,98 +57,3 @@ pub trait Plugin: Sized {
5557
/// the server is shutting down when this method is called.
5658
fn disable(self, game: &mut Game);
5759
}
58-
59-
/// Invoke this macro in your plugin's main.rs.
60-
///
61-
/// Give it the name of your struct implementing `Plugin`.
62-
///
63-
/// # Example
64-
/// ```no_run
65-
/// // main.rs
66-
/// use quill::{Plugin, Setup, Game};
67-
///
68-
/// quill::plugin!(MyPlugin);
69-
///
70-
/// pub struct MyPlugin {
71-
/// // plugin state goes here
72-
/// }
73-
///
74-
/// impl Plugin for MyPlugin {
75-
/// fn enable(game: &mut Game, setup: &mut Setup<Self>) -> Self {
76-
/// // Initialize plugin state...
77-
/// Self {}
78-
/// }
79-
///
80-
/// fn disable(self, game: &mut Game) {
81-
/// // Clean up...
82-
/// }
83-
/// }
84-
/// ```
85-
#[macro_export]
86-
macro_rules! plugin {
87-
($plugin:ident) => {
88-
// `static mut` can be used without synchronization because the host
89-
// guarantees it will not invoke plugin systems outside of the main thread.
90-
static mut PLUGIN: Option<$plugin> = None;
91-
92-
// Exports to the host required for all plugins
93-
#[no_mangle]
94-
#[doc(hidden)]
95-
#[cfg(target_arch = "wasm32")]
96-
pub unsafe extern "C" fn quill_setup() {
97-
let plugin: $plugin =
98-
quill::Plugin::enable(&mut $crate::Game::new(), &mut $crate::Setup::new());
99-
PLUGIN = Some(plugin);
100-
}
101-
102-
#[no_mangle]
103-
#[doc(hidden)]
104-
#[cfg(not(target_arch = "wasm32"))]
105-
pub unsafe extern "C" fn quill_setup(
106-
context: *const (),
107-
vtable_ptr: *const u8,
108-
vtable_len: usize,
109-
) {
110-
// Set up vtable and host context for quill_sys.
111-
let vtable_bytes = ::std::slice::from_raw_parts(vtable_ptr, vtable_len);
112-
let vtable: ::std::collections::HashMap<&str, usize> =
113-
$crate::bincode::deserialize(vtable_bytes).expect("invalid vtable");
114-
115-
$crate::sys::init_host_context(context);
116-
$crate::sys::init_host_vtable(&vtable)
117-
.expect("invalid vtable (check that the plugin and host are up to date)");
118-
119-
let plugin: $plugin =
120-
quill::Plugin::enable(&mut $crate::Game::new(), &mut $crate::Setup::new());
121-
PLUGIN = Some(plugin);
122-
}
123-
124-
#[no_mangle]
125-
#[doc(hidden)]
126-
pub unsafe extern "C" fn quill_allocate(size: usize, align: usize) -> *mut u8 {
127-
std::alloc::alloc(std::alloc::Layout::from_size_align_unchecked(size, align))
128-
}
129-
130-
#[no_mangle]
131-
#[doc(hidden)]
132-
pub unsafe extern "C" fn quill_deallocate(ptr: *mut u8, size: usize, align: usize) {
133-
std::alloc::dealloc(
134-
ptr,
135-
std::alloc::Layout::from_size_align_unchecked(size, align),
136-
)
137-
}
138-
139-
#[no_mangle]
140-
#[doc(hidden)]
141-
pub unsafe extern "C" fn quill_run_system(data: *mut u8) {
142-
let system = &mut *data.cast::<Box<dyn FnMut(&mut $plugin, &mut $crate::Game)>>();
143-
let plugin = PLUGIN.as_mut().expect("quill_setup never called");
144-
system(plugin, &mut $crate::Game::new());
145-
}
146-
147-
/// Never called by Quill, but this is needed
148-
/// to avoid linker errors with WASI.
149-
#[doc(hidden)]
150-
fn main() {}
151-
};
152-
}

quill/example-plugins/block-access/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
33
use quill::{entities::Player, BlockState, Game, Plugin, Position};
44

5-
quill::plugin!(BlockAccess);
6-
5+
#[quill::plugin]
76
pub struct BlockAccess;
87

98
impl Plugin for BlockAccess {

quill/example-plugins/block-place/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
33
use quill::{events::BlockPlacementEvent, Game, Plugin};
44

5-
quill::plugin!(BlockPlace);
6-
5+
#[quill::plugin]
76
pub struct BlockPlace;
87

98
impl Plugin for BlockPlace {

quill/example-plugins/observe-creativemode-flight-event/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ use quill::{
99
Game, Plugin, Setup,
1010
};
1111

12-
quill::plugin!(FlightPlugin);
13-
12+
#[quill::plugin]
1413
struct FlightPlugin {}
1514

1615
impl Plugin for FlightPlugin {

quill/example-plugins/particle-example/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use quill::{Game, Particle, ParticleKind, Plugin, Position};
22

3-
quill::plugin!(ParticleExample);
4-
3+
#[quill::plugin]
54
struct ParticleExample {}
65

76
impl Plugin for ParticleExample {

0 commit comments

Comments
 (0)