Added a Global State API in plugins

This commit is contained in:
ccgauche 2021-03-02 17:15:19 +01:00
parent c74cdaeb65
commit 6dffd1a9e2
2 changed files with 64 additions and 18 deletions

View File

@ -2,7 +2,22 @@ extern crate proc_macro;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote; use quote::quote;
use syn::{parse_macro_input, ItemFn}; use syn::{parse_macro_input, ItemFn, ItemStruct};
#[proc_macro_attribute]
pub fn global_state(_args: TokenStream, item: TokenStream) -> TokenStream {
let parsed = parse_macro_input!(item as ItemStruct);
let name = &parsed.ident;
let out: proc_macro2::TokenStream = quote! {
#parsed
type PLUGIN_STATE_TYPE = #name;
static mut PLUGIN_STATE: Option<PLUGIN_STATE_TYPE> = None;
static PLUGIN_STATE_GUARD: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
};
out.into()
}
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn event_handler(_args: TokenStream, item: TokenStream) -> TokenStream { pub fn event_handler(_args: TokenStream, item: TokenStream) -> TokenStream {
@ -13,20 +28,48 @@ pub fn event_handler(_args: TokenStream, item: TokenStream) -> TokenStream {
let fn_args = sig.inputs; // comma separated args let fn_args = sig.inputs; // comma separated args
let fn_return = sig.output; // comma separated args let fn_return = sig.output; // comma separated args
let out: proc_macro2::TokenStream = quote! { let out: proc_macro2::TokenStream = if fn_args.len() == 1 {
#[allow(clippy::unnecessary_wraps)] quote! {
#[no_mangle] #[allow(clippy::unnecessary_wraps)]
pub fn #fn_name(intern__ptr: i64, intern__len: i64) -> i64 { #[no_mangle]
let input = ::veloren_plugin_rt::read_input(intern__ptr as _,intern__len as _).unwrap(); pub fn #fn_name(intern__ptr: i64, intern__len: i64) -> i64 {
#[inline] let input = ::veloren_plugin_rt::read_input(intern__ptr as _,intern__len as _).unwrap();
fn inner(#fn_args) #fn_return { #[inline]
#fn_body fn inner(#fn_args) #fn_return {
#fn_body
}
// Artificially force the event handler to be type-correct
fn force_event<E: ::veloren_plugin_rt::api::Event>(event: E, inner: fn(E) -> E::Response) -> E::Response {
inner(event)
}
::veloren_plugin_rt::write_output(&force_event(input, inner))
} }
// Artificially force the event handler to be type-correct }
fn force_event<E: ::veloren_plugin_rt::api::Event>(event: E, inner: fn(E) -> E::Response) -> E::Response { } else {
inner(event) quote! {
#[allow(clippy::unnecessary_wraps)]
#[no_mangle]
pub fn #fn_name(intern__ptr: i64, intern__len: i64) -> i64 {
let input = ::veloren_plugin_rt::read_input(intern__ptr as _,intern__len as _).unwrap();
#[inline]
fn inner(#fn_args) #fn_return {
#fn_body
}
// Artificially force the event handler to be type-correct
fn force_event<E: ::veloren_plugin_rt::api::Event>(event: E, inner: fn(E, &mut PLUGIN_STATE_TYPE) -> E::Response) -> E::Response {
//let mut plugin_state = PLUGIN_STATE.lock().unwrap();
unsafe {
assert_eq!(PLUGIN_STATE_GUARD.swap(true, std::sync::atomic::Ordering::Acquire), false);
if PLUGIN_STATE.is_none() {
PLUGIN_STATE = Some(PLUGIN_STATE_TYPE::default());
}
let out = inner(event, PLUGIN_STATE.as_mut().unwrap());
PLUGIN_STATE_GUARD.store(false, std::sync::atomic::Ordering::Release);
out
}
}
::veloren_plugin_rt::write_output(&force_event(input, inner))
} }
::veloren_plugin_rt::write_output(&force_event(input, inner))
} }
}; };
out.into() out.into()

View File

@ -1,5 +1,3 @@
use std::sync::atomic::{AtomicBool, Ordering};
use veloren_plugin_rt::{ use veloren_plugin_rt::{
api::{event::*, Action, GameMode}, api::{event::*, Action, GameMode},
*, *,
@ -31,11 +29,16 @@ pub fn on_command_testplugin(command: ChatCommandEvent) -> Result<Vec<String>, S
)]) )])
} }
static COUNTER: AtomicBool = AtomicBool::new(false); #[global_state]
#[derive(Default)]
struct State {
counter: bool,
}
#[event_handler] #[event_handler]
pub fn on_join(input: PlayerJoinEvent) -> PlayerJoinResult { pub fn on_join(input: PlayerJoinEvent, state: &mut State) -> PlayerJoinResult {
if COUNTER.swap(!COUNTER.load(Ordering::SeqCst), Ordering::SeqCst) { state.counter = !state.counter;
if !state.counter {
PlayerJoinResult::Kick(format!("You are a cheater {:?}", input)) PlayerJoinResult::Kick(format!("You are a cheater {:?}", input))
} else { } else {
PlayerJoinResult::None PlayerJoinResult::None