Skip to content

Commit

Permalink
switch call context to function argument
Browse files Browse the repository at this point in the history
  • Loading branch information
BrettMayson committed Sep 12, 2024
1 parent d99361b commit 74afe94
Show file tree
Hide file tree
Showing 14 changed files with 332 additions and 174 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,18 +121,20 @@ pub fn group() -> arma_rs::Group {

## Call Context

Since Arma v2.11 additional context is provided each time the extension is called. This context can be accessed through the optional `Context` argument.
Since Arma v2.11 additional context is provided each time the extension is called. This context can be accessed through the optional `ArmaCallContext` argument.

Since Arma v2.18 the context is only requested from Arma when the functionh has `ArmaCallContext` as an argument.

```rust
use arma_rs::Context;
use arma_rs::ArmaCallContext;

pub fn call_context(ctx: Context) -> String {
pub fn call_context(call_context: ArmaCallContext) -> String {
format!(
"{:?},{:?},{:?},{:?}",
ctx.caller(),
ctx.source(),
ctx.mission(),
ctx.server()
call_context.caller(),
call_context.source(),
call_context.mission(),
call_context.server()
)
}

Expand All @@ -141,8 +143,6 @@ pub fn group() -> arma_rs::Group {
}
```

Support for this context can be can be toggled using the `call-context` feature flag, which is enabled by default.

## Persistent State

Both the extension and command groups allow for type based persistent state values with at most one instance per type. These state values can then be accessed through the optional `Context` argument.
Expand Down
5 changes: 2 additions & 3 deletions arma-rs-example/src/call_context.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use arma_rs::{Context, Group};
use arma_rs::{ArmaCallContext, Group};

pub fn current(ctx: Context) -> String {
let call_context = ctx.call_context();
pub fn current(call_context: ArmaCallContext) -> String {
format!(
"{:?},{:?},{:?},{:?}",
call_context.caller(),
Expand Down
4 changes: 2 additions & 2 deletions arma-rs-proc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pub fn arma(_attr: TokenStream, item: TokenStream) -> TokenStream {
#ext_init
if let Some(ext) = &RV_EXTENSION {
if ext.allow_no_args() {
ext.handle_call(function, output, size, None, None);
ext.handle_call(function, output, size, None, None, true);
}
}
}
Expand All @@ -94,7 +94,7 @@ pub fn arma(_attr: TokenStream, item: TokenStream) -> TokenStream {
pub unsafe extern #extern_type fn #argfn(output: *mut arma_rs_libc::c_char, size: arma_rs_libc::size_t, function: *mut arma_rs_libc::c_char, args: *mut *mut arma_rs_libc::c_char, arg_count: arma_rs_libc::c_int) -> arma_rs_libc::c_int {
#ext_init
if let Some(ext) = &RV_EXTENSION {
ext.handle_call(function, output, size, Some(args), Some(arg_count))
ext.handle_call(function, output, size, Some(args), Some(arg_count), true)
} else {
0
}
Expand Down
125 changes: 120 additions & 5 deletions arma-rs/src/command.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::ext_result::IntoExtResult;
use crate::value::{FromArma, Value};
use crate::Context;
use crate::{ArmaCallContext, ArmaContextManager, Context};

type HandlerFunc = Box<
dyn Fn(
Context,
&ArmaContextManager,
*mut libc::c_char,
libc::size_t,
Option<*mut *mut i8>,
Expand All @@ -28,12 +29,13 @@ where
Handler {
handler: Box::new(
move |context: Context,
acm: &ArmaContextManager,
output: *mut libc::c_char,
size: libc::size_t,
args: Option<*mut *mut i8>,
count: Option<libc::c_int>|
-> libc::c_int {
unsafe { command.call(context, output, size, args, count) }
unsafe { command.call(context, acm, output, size, args, count) }
},
),
}
Expand All @@ -47,6 +49,7 @@ pub trait Executor: 'static {
unsafe fn call(
&self,
context: Context,
acm: &ArmaContextManager,
output: *mut libc::c_char,
size: libc::size_t,
args: Option<*mut *mut i8>,
Expand All @@ -65,6 +68,7 @@ pub trait Factory<A, R> {
unsafe fn call(
&self,
context: Context,
acm: &ArmaContextManager,
output: *mut libc::c_char,
size: libc::size_t,
args: Option<*mut *mut i8>,
Expand All @@ -81,12 +85,13 @@ macro_rules! factory_tuple ({ $c: expr, $($param:ident)* } => {
unsafe fn call(
&self,
context: Context,
acm: &ArmaContextManager,
output: *mut libc::c_char,
size: libc::size_t,
args: Option<*mut *mut i8>,
count: Option<libc::c_int>,
) {
self.call(context, output, size, args, count);
self.call(context, acm, output, size, args, count);
}
}

Expand All @@ -98,7 +103,7 @@ macro_rules! factory_tuple ({ $c: expr, $($param:ident)* } => {
$($param: FromArma,)*
{
#[allow(non_snake_case)]
unsafe fn call(&self, _: Context, output: *mut libc::c_char, size: libc::size_t, args: Option<*mut *mut i8>, count: Option<libc::c_int>) -> libc::c_int {
unsafe fn call(&self, _: Context, _: &ArmaContextManager, output: *mut libc::c_char, size: libc::size_t, args: Option<*mut *mut i8>, count: Option<libc::c_int>) -> libc::c_int {
let count = count.unwrap_or_else(|| 0);
if count != $c {
return format!("2{}", count).parse::<libc::c_int>().unwrap();
Expand Down Expand Up @@ -152,7 +157,7 @@ macro_rules! factory_tuple ({ $c: expr, $($param:ident)* } => {
$($param: FromArma,)*
{
#[allow(non_snake_case)]
unsafe fn call(&self, context: Context, output: *mut libc::c_char, size: libc::size_t, args: Option<*mut *mut i8>, count: Option<libc::c_int>) -> libc::c_int {
unsafe fn call(&self, context: Context, _: &ArmaContextManager, output: *mut libc::c_char, size: libc::size_t, args: Option<*mut *mut i8>, count: Option<libc::c_int>) -> libc::c_int {
let count = count.unwrap_or_else(|| 0);
if count != $c {
return format!("2{}", count).parse::<libc::c_int>().unwrap();
Expand Down Expand Up @@ -197,6 +202,116 @@ macro_rules! factory_tuple ({ $c: expr, $($param:ident)* } => {
}
}
}

// Call Context
impl<Func, $($param,)* ER> Factory<(ArmaCallContext, $($param,)*), ER> for Func
where
ER: IntoExtResult + 'static,
Func: Fn(ArmaCallContext, $($param),*) -> ER,
$($param: FromArma,)*
{
#[allow(non_snake_case)]
unsafe fn call(&self, _: Context, acm: &ArmaContextManager, output: *mut libc::c_char, size: libc::size_t, args: Option<*mut *mut i8>, count: Option<libc::c_int>) -> libc::c_int {
let call_context = acm.request();
let count = count.unwrap_or_else(|| 0);
if count != $c {
return format!("2{}", count).parse::<libc::c_int>().unwrap();
}
if $c == 0 {
handle_output_and_return(
(self)(call_context, $($param::from_arma("".to_string()).unwrap(),)*),
output,
size
)
} else {
#[allow(unused_variables, unused_mut)]
let mut argv: Vec<String> = {
let argv: &[*mut libc::c_char; $c] = &*(args.unwrap() as *const [*mut i8; $c]);
let mut argv = argv
.to_vec()
.into_iter()
.map(|s| {
std::ffi::CStr::from_ptr(s).to_string_lossy().to_string()
})
.collect::<Vec<String>>();
argv.reverse();
argv
};
#[allow(unused_variables, unused_mut)] // Caused by the 0 loop
let mut c = 0;
#[allow(unused_assignments, clippy::mixed_read_write_in_expression)]
handle_output_and_return(
{
(self)(call_context, $(
if let Ok(val) = $param::from_arma(argv.pop().unwrap()) {
c += 1;
val
} else {
return format!("3{}", c).parse::<libc::c_int>().unwrap()
},
)*)
},
output,
size
)
}
}
}

// Context & Call Context
impl<Func, $($param,)* ER> Factory<(Context, ArmaCallContext, $($param,)*), ER> for Func
where
ER: IntoExtResult + 'static,
Func: Fn(Context, ArmaCallContext, $($param),*) -> ER,
$($param: FromArma,)*
{
#[allow(non_snake_case)]
unsafe fn call(&self, context: Context, acm: &ArmaContextManager, output: *mut libc::c_char, size: libc::size_t, args: Option<*mut *mut i8>, count: Option<libc::c_int>) -> libc::c_int {
let call_context = acm.request();
let count = count.unwrap_or_else(|| 0);
if count != $c {
return format!("2{}", count).parse::<libc::c_int>().unwrap();
}
if $c == 0 {
handle_output_and_return(
(self)(context, call_context, $($param::from_arma("".to_string()).unwrap(),)*),
output,
size
)
} else {
#[allow(unused_variables, unused_mut)]
let mut argv: Vec<String> = {
let argv: &[*mut libc::c_char; $c] = &*(args.unwrap() as *const [*mut i8; $c]);
let mut argv = argv
.to_vec()
.into_iter()
.map(|s| {
std::ffi::CStr::from_ptr(s).to_string_lossy().to_string()
})
.collect::<Vec<String>>();
argv.reverse();
argv
};
#[allow(unused_variables, unused_mut)] // Caused by the 0 loop
let mut c = 0;
#[allow(unused_assignments, clippy::mixed_read_write_in_expression)]
handle_output_and_return(
{
(self)(context, call_context, $(
if let Ok(val) = $param::from_arma(argv.pop().unwrap()) {
c += 1;
val
} else {
return format!("3{}", c).parse::<libc::c_int>().unwrap()
},
)*)
},
output,
size
)
}
}
}
});

unsafe fn handle_output_and_return<R>(
Expand Down
13 changes: 13 additions & 0 deletions arma-rs/src/context/call.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::path::Path;

#[derive(Clone, Default)]
/// Context of the Arma call.
pub struct ArmaCallContext {
pub(super) caller: Caller,
pub(super) source: Source,
Expand All @@ -23,18 +24,30 @@ impl ArmaCallContext {
}
}

#[must_use]
/// Player that called the extension. Can be [`Caller::Unknown`] when the player's steamID64 is unavailable
/// # Note
/// Unlike <https://community.bistudio.com/wiki/getPlayerUID> [`Caller::Steam`] isn't limited to multiplayer.
pub fn caller(&self) -> &Caller {
&self.caller
}

#[must_use]
/// Source from where the extension was called.
pub fn source(&self) -> &Source {
&self.source
}

#[must_use]
/// Current mission's name.
/// # Note
/// Can result in [`Mission::None`] in missions made prior to Arma v2.02.
pub fn mission(&self) -> &Mission {
&self.mission
}

#[must_use]
/// Current server's name
pub fn server(&self) -> &Server {
&self.server
}
Expand Down
40 changes: 40 additions & 0 deletions arma-rs/src/context/manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use std::{cell::RefCell, rc::Rc};

use crate::ContextRequest;

use super::ArmaCallContext;

/// Manages requesting and replacing the ArmaCallContext
pub struct ArmaContextManager {
pub(crate) request: RefCell<ContextRequest>,
state: Rc<RefCell<Option<ArmaCallContext>>>,
}

impl ArmaContextManager {
/// Create a new ArmaContextManager
pub fn new(request: ContextRequest) -> Self {
Self {
request: RefCell::new(request),
state: Rc::new(RefCell::new(None)),
}
}

/// Request a new ArmaCallContext from Arma
pub fn request(&self) -> ArmaCallContext {
// When the request is called, Arma will send the request to the extension
// The extension will set the state to the request it just received
unsafe {
(self.request.borrow())();
}
// When the request function returns, the state has been set by Arma
// It can now be taken and sent to the Context
self.state
.replace(None)
.expect("Arma should've set the state")
}

/// Replace the current ArmaCallContext with a new one
pub fn replace(&self, value: Option<ArmaCallContext>) {
*self.state.borrow_mut() = value;
}
}
Loading

0 comments on commit 74afe94

Please sign in to comment.