From b43afd492064865476542d45c0c4f78b20937a54 Mon Sep 17 00:00:00 2001
From: ccgauche <gaucheron.laurent@gmail.com>
Date: Tue, 2 Mar 2021 17:15:19 +0100
Subject: [PATCH] Added a Global State API in plugins

---
 plugin/derive/src/lib.rs    | 69 ++++++++++++++++++++++++++++++-------
 plugin/rt/examples/hello.rs | 13 ++++---
 2 files changed, 64 insertions(+), 18 deletions(-)

diff --git a/plugin/derive/src/lib.rs b/plugin/derive/src/lib.rs
index d275848d87..9687432875 100644
--- a/plugin/derive/src/lib.rs
+++ b/plugin/derive/src/lib.rs
@@ -2,7 +2,22 @@ extern crate proc_macro;
 
 use proc_macro::TokenStream;
 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]
 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_return = sig.output; // comma separated args
 
-    let out: proc_macro2::TokenStream = 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
+    let out: proc_macro2::TokenStream = if fn_args.len() == 1 {
+        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) -> 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 {
-                inner(event)
+        }
+    } else {
+        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()
diff --git a/plugin/rt/examples/hello.rs b/plugin/rt/examples/hello.rs
index f9009b6525..aa8f0c1b71 100644
--- a/plugin/rt/examples/hello.rs
+++ b/plugin/rt/examples/hello.rs
@@ -1,5 +1,3 @@
-use std::sync::atomic::{AtomicBool, Ordering};
-
 use veloren_plugin_rt::{
     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]
-pub fn on_join(input: PlayerJoinEvent) -> PlayerJoinResult {
-    if COUNTER.swap(!COUNTER.load(Ordering::SeqCst), Ordering::SeqCst) {
+pub fn on_join(input: PlayerJoinEvent, state: &mut State) -> PlayerJoinResult {
+    state.counter = !state.counter;
+    if !state.counter {
         PlayerJoinResult::Kick(format!("You are a cheater {:?}", input))
     } else {
         PlayerJoinResult::None