Add method of overriding stdout to client console for gmcl modules
This commit is contained in:
parent
a1f4f9cbac
commit
b2861e3e4d
|
@ -144,7 +144,7 @@ dependencies = [
|
||||||
"fn_abi",
|
"fn_abi",
|
||||||
"fn_has_this",
|
"fn_has_this",
|
||||||
"fn_type_alias",
|
"fn_type_alias",
|
||||||
"gmod-macros 1.0.1",
|
"gmod-macros 1.0.2",
|
||||||
"gmserverplugin",
|
"gmserverplugin",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libloading",
|
"libloading",
|
||||||
|
@ -165,7 +165,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gmod-macros"
|
name = "gmod-macros"
|
||||||
version = "1.0.1"
|
version = "1.0.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "gmod-macros"
|
name = "gmod-macros"
|
||||||
version = "1.0.1"
|
version = "1.0.2"
|
||||||
authors = ["William Venner <william@venner.io>"]
|
authors = ["William Venner <william@venner.io>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
@ -6,7 +6,7 @@ extern crate quote;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use syn::{ReturnType, ItemFn};
|
use syn::{ReturnType, ItemFn, Attribute};
|
||||||
|
|
||||||
fn check_lua_function(input: &mut ItemFn) {
|
fn check_lua_function(input: &mut ItemFn) {
|
||||||
assert!(input.sig.asyncness.is_none(), "Cannot be async");
|
assert!(input.sig.asyncness.is_none(), "Cannot be async");
|
||||||
|
@ -17,11 +17,22 @@ fn check_lua_function(input: &mut ItemFn) {
|
||||||
input.sig.abi = Some(syn::parse_quote!(extern "C-unwind"));
|
input.sig.abi = Some(syn::parse_quote!(extern "C-unwind"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn no_mangle(item_fn: &mut ItemFn) {
|
||||||
|
item_fn.attrs.push(Attribute {
|
||||||
|
path: parse_quote!(no_mangle),
|
||||||
|
tokens: proc_macro2::TokenStream::new(),
|
||||||
|
style: syn::AttrStyle::Outer,
|
||||||
|
pound_token: Default::default(),
|
||||||
|
bracket_token: Default::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||||
let mut input = parse_macro_input!(tokens as ItemFn);
|
let mut input = parse_macro_input!(tokens as ItemFn);
|
||||||
check_lua_function(&mut input);
|
check_lua_function(&mut input);
|
||||||
input.block.stmts.insert(0, syn::parse2(quote!(#[allow(unused_unsafe)] unsafe { ::gmod::lua::load() })).unwrap());
|
input.block.stmts.insert(0, syn::parse2(quote!(#[allow(unused_unsafe)] unsafe { ::gmod::lua::load() })).unwrap());
|
||||||
|
no_mangle(&mut input);
|
||||||
input.into_token_stream().into()
|
input.into_token_stream().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +40,7 @@ pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||||
pub fn gmod13_close(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
pub fn gmod13_close(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||||
let mut input = parse_macro_input!(tokens as ItemFn);
|
let mut input = parse_macro_input!(tokens as ItemFn);
|
||||||
check_lua_function(&mut input);
|
check_lua_function(&mut input);
|
||||||
|
no_mangle(&mut input);
|
||||||
input.into_token_stream().into()
|
input.into_token_stream().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,10 @@ categories = ["api-bindings", "external-ffi-bindings", "game-development", "deve
|
||||||
default = ["hax"]
|
default = ["hax"]
|
||||||
hax = ["ctor", "skidscan", "detour", "fn_type_alias", "fn_abi", "cfg_table", "null_fn", "fn_has_this"]
|
hax = ["ctor", "skidscan", "detour", "fn_type_alias", "fn_abi", "cfg_table", "null_fn", "fn_has_this"]
|
||||||
server-plugin = ["gmserverplugin"]
|
server-plugin = ["gmserverplugin"]
|
||||||
|
gmcl = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gmod-macros = { version = "1.0.1", path = "../gmod-macros" }
|
gmod-macros = { version = "1.0.2", path = "../gmod-macros" }
|
||||||
gmserverplugin = { version = "1", optional = true }
|
gmserverplugin = { version = "1", optional = true }
|
||||||
|
|
||||||
libloading = "0"
|
libloading = "0"
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
use std::{sync::{Arc, Mutex, TryLockError}, time::Duration, os::raw::c_char};
|
||||||
|
|
||||||
|
/// This function will **permanently** redirect stdout to the client console.
|
||||||
|
///
|
||||||
|
/// This allows for `println!()` and friends to print to the client console.
|
||||||
|
pub fn override_stdout() {
|
||||||
|
unsafe {
|
||||||
|
let (_lib, _path) = crate::open_library!("tier0").expect("Failed to open tier0.dll");
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
let ConMsg: extern "C" fn(*const c_char, ...) = *{
|
||||||
|
#[cfg(target_os = "windows")] {
|
||||||
|
_lib.get({
|
||||||
|
#[cfg(all(target_os = "windows", target_pointer_width = "64"))] {
|
||||||
|
b"?ConMsg@@YAXPEBDZZ\0"
|
||||||
|
}
|
||||||
|
#[cfg(all(target_os = "windows", target_pointer_width = "32"))] {
|
||||||
|
b"?ConMsg@@YAXPBDZZ\0"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "linux")] {
|
||||||
|
_lib.get(b"ConMsg\0").or_else(|_| _lib.get(b"_Z6ConMsgPKcz\0"))
|
||||||
|
}
|
||||||
|
}.expect("Failed to find ConMsg");
|
||||||
|
|
||||||
|
let output_buf = Arc::new(Mutex::new(Vec::new()));
|
||||||
|
let output_buf_ref = output_buf.clone();
|
||||||
|
|
||||||
|
// This is actually a really dumb implementation, but appears to be the only way, unfortunately.
|
||||||
|
std::thread::spawn(move || loop {
|
||||||
|
match output_buf.try_lock() {
|
||||||
|
Ok(mut data) => if !data.is_empty() {
|
||||||
|
data.push(0); // cheeky
|
||||||
|
ConMsg(data.as_ptr() as *const i8);
|
||||||
|
|
||||||
|
data.truncate(0);
|
||||||
|
},
|
||||||
|
Err(TryLockError::Poisoned(err)) => panic!("{}", err),
|
||||||
|
Err(TryLockError::WouldBlock) => {
|
||||||
|
std::hint::spin_loop();
|
||||||
|
std::thread::yield_now();
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::thread::sleep(Duration::from_millis(250));
|
||||||
|
});
|
||||||
|
|
||||||
|
std::io::set_output_capture(Some(output_buf_ref));
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
#![feature(c_unwind)]
|
#![feature(c_unwind)]
|
||||||
#![feature(thread_id_value)]
|
#![feature(thread_id_value)]
|
||||||
|
|
||||||
|
#![cfg_attr(feature = "gmcl", feature(internal_output_capture))]
|
||||||
|
|
||||||
pub use cstr;
|
pub use cstr;
|
||||||
pub use libloading;
|
pub use libloading;
|
||||||
pub use gmod_macros::*;
|
pub use gmod_macros::*;
|
||||||
|
@ -38,6 +40,10 @@ pub mod userdata;
|
||||||
/// Net library helpers
|
/// Net library helpers
|
||||||
pub mod net;
|
pub mod net;
|
||||||
|
|
||||||
|
/// Clientside module helpers
|
||||||
|
#[cfg(feature = "gmcl")]
|
||||||
|
pub mod gmcl;
|
||||||
|
|
||||||
/// Returns whether this client is running the x86-64 branch
|
/// Returns whether this client is running the x86-64 branch
|
||||||
pub fn is_x86_64() -> bool {
|
pub fn is_x86_64() -> bool {
|
||||||
#[cfg(target_pointer_width = "64")] {
|
#[cfg(target_pointer_width = "64")] {
|
||||||
|
|
|
@ -74,10 +74,10 @@ impl LuaError {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(not(debug_assertions), repr(transparent))]
|
#[cfg_attr(not(debug_assertions), repr(transparent))]
|
||||||
pub struct LuaSharedInterface(UnsafeCell<*mut LuaShared>, #[cfg(debug_assertions)] AtomicI64);
|
pub struct LuaSharedInterface(pub(crate) UnsafeCell<*mut LuaShared>, #[cfg(debug_assertions)] AtomicI64);
|
||||||
impl LuaSharedInterface {
|
impl LuaSharedInterface {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
fn debug_assertions(&self) {
|
pub(crate) fn debug_assertions(&self) {
|
||||||
assert!(!unsafe { *self.0.get() }.is_null(), "The Lua state has not been initialized yet. Add `#[gmod::gmod13_open]` to your module's gmod13_open function to fix this. You can also manually load the Lua state with `gmod::load_lua_state()` or `gmod::set_lua_state(*mut c_void)`");
|
assert!(!unsafe { *self.0.get() }.is_null(), "The Lua state has not been initialized yet. Add `#[gmod::gmod13_open]` to your module's gmod13_open function to fix this. You can also manually load the Lua state with `gmod::load_lua_state()` or `gmod::set_lua_state(*mut c_void)`");
|
||||||
|
|
||||||
let thread_id = u64::from(std::thread::current().id().as_u64()) as i64;
|
let thread_id = u64::from(std::thread::current().id().as_u64()) as i64;
|
||||||
|
|
Loading…
Reference in New Issue