use async_std::task;
use futures::executor::{LocalPool, LocalSpawner};
use futures::task::LocalSpawnExt;
use sm_ext::{native, register_natives, CallableParam, ExecType, Executable, GameFrameHookId, HandleId, HandleRef, HandleType, HasHandleType, IExtension, IExtensionInterface, IForwardManager, IHandleSys, IPluginContext, IPluginFunction, IShareSys, ISourceMod, SMExtension, SMInterfaceApi};
use std::cell::RefCell;
use std::error::Error;
use std::time::Duration;
struct IntHandle(i32);
impl HasHandleType for IntHandle {
fn handle_type<'ty>() -> &'ty HandleType<Self> {
MyExtension::handle_type()
}
}
#[native]
fn native_handle_read(_ctx: &IPluginContext, handle: HandleRef<IntHandle>) -> Result<i32, Box<dyn Error>> {
Ok(handle.0)
}
#[native]
fn test_native(ctx: &IPluginContext, mut func: IPluginFunction) -> Result<(), Box<dyn Error>> {
let mut forward = MyExtension::forwardsys().create_private_forward(None, ExecType::Ignore, &[HandleId::param_type()])?;
forward.add_function(&mut func);
let mut this = IntHandle(0).into_handle()?;
let handle = this.clone_handle(ctx.get_identity())?;
MyExtension::spawner().spawn_local(async move {
let future = async move {
task::sleep(Duration::from_secs(5)).await;
this.0 = 42;
forward.push(handle)?;
forward.execute()?;
Ok(())
};
if let Result::<(), Box<dyn Error>>::Err(e) = future.await {
println!(">>> Async error: {}", e);
}
})?;
Ok(())
}
extern "C" fn on_game_frame(_simulating: bool) {
MyExtension::get().pool.borrow_mut().run_until_stalled()
}
#[derive(Default, SMExtension)]
#[extension(name = "Rusty", description = "Sample extension written in Rust")]
pub struct MyExtension {
myself: Option<IExtension>,
sharesys: Option<IShareSys>,
forwardsys: Option<IForwardManager>,
sourcemod: Option<ISourceMod>,
frame_hook: Option<GameFrameHookId>,
handle_type: Option<HandleType<IntHandle>>,
pool: RefCell<LocalPool>,
}
impl MyExtension {
fn get() -> &'static Self {
EXTENSION_GLOBAL.with(|ext| unsafe { &(*ext.borrow().unwrap()).delegate })
}
fn forwardsys() -> &'static IForwardManager {
Self::get().forwardsys.as_ref().unwrap()
}
fn handle_type() -> &'static HandleType<IntHandle> {
Self::get().handle_type.as_ref().unwrap()
}
fn spawner() -> LocalSpawner {
Self::get().pool.borrow().spawner()
}
}
impl IExtensionInterface for MyExtension {
fn on_extension_load(&mut self, myself: IExtension, sys: IShareSys, late: bool) -> Result<(), Box<dyn std::error::Error>> {
println!(">>> Rusty extension loaded! me = {:?}, sys = {:?}, late = {:?}", myself, sys, late);
let forward_manager: IForwardManager = sys.request_interface(&myself)?;
println!(">>> Got interface: {:?} v{:?}", forward_manager.get_interface_name(), forward_manager.get_interface_version());
let sourcemod: ISourceMod = sys.request_interface(&myself)?;
println!(">>> Got interface: {:?} v{:?}", sourcemod.get_interface_name(), sourcemod.get_interface_version());
let handlesys: IHandleSys = sys.request_interface(&myself)?;
println!(">>> Got interface: {:?} v{:?}", handlesys.get_interface_name(), handlesys.get_interface_version());
self.frame_hook = Some(sourcemod.add_game_frame_hook(on_game_frame));
self.handle_type = Some(handlesys.create_type("IntHandle", myself.get_identity())?);
register_natives!(
&sys,
&myself,
[
("Rust_Test", test_native), ("RustHandle.Read", native_handle_read), ]
);
self.myself = Some(myself);
self.sharesys = Some(sys);
self.forwardsys = Some(forward_manager);
self.sourcemod = Some(sourcemod);
Ok(())
}
fn on_extension_unload(&mut self) {
self.frame_hook = None;
self.handle_type = None;
}
}