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_has_this",
|
||||
"fn_type_alias",
|
||||
"gmod-macros 1.0.1",
|
||||
"gmod-macros 1.0.2",
|
||||
"gmserverplugin",
|
||||
"lazy_static",
|
||||
"libloading",
|
||||
|
@ -165,7 +165,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gmod-macros"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gmod-macros"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
authors = ["William Venner <william@venner.io>"]
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
|
|
|
@ -6,7 +6,7 @@ extern crate quote;
|
|||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{ReturnType, ItemFn};
|
||||
use syn::{ReturnType, ItemFn, Attribute};
|
||||
|
||||
fn check_lua_function(input: &mut ItemFn) {
|
||||
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"));
|
||||
}
|
||||
|
||||
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]
|
||||
pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||
let mut input = parse_macro_input!(tokens as ItemFn);
|
||||
check_lua_function(&mut input);
|
||||
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()
|
||||
}
|
||||
|
||||
|
@ -29,6 +40,7 @@ pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
|||
pub fn gmod13_close(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||
let mut input = parse_macro_input!(tokens as ItemFn);
|
||||
check_lua_function(&mut input);
|
||||
no_mangle(&mut input);
|
||||
input.into_token_stream().into()
|
||||
}
|
||||
|
||||
|
|
|
@ -13,9 +13,10 @@ categories = ["api-bindings", "external-ffi-bindings", "game-development", "deve
|
|||
default = ["hax"]
|
||||
hax = ["ctor", "skidscan", "detour", "fn_type_alias", "fn_abi", "cfg_table", "null_fn", "fn_has_this"]
|
||||
server-plugin = ["gmserverplugin"]
|
||||
gmcl = []
|
||||
|
||||
[dependencies]
|
||||
gmod-macros = { version = "1.0.1", path = "../gmod-macros" }
|
||||
gmod-macros = { version = "1.0.2", path = "../gmod-macros" }
|
||||
gmserverplugin = { version = "1", optional = true }
|
||||
|
||||
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(thread_id_value)]
|
||||
|
||||
#![cfg_attr(feature = "gmcl", feature(internal_output_capture))]
|
||||
|
||||
pub use cstr;
|
||||
pub use libloading;
|
||||
pub use gmod_macros::*;
|
||||
|
@ -38,6 +40,10 @@ pub mod userdata;
|
|||
/// Net library helpers
|
||||
pub mod net;
|
||||
|
||||
/// Clientside module helpers
|
||||
#[cfg(feature = "gmcl")]
|
||||
pub mod gmcl;
|
||||
|
||||
/// Returns whether this client is running the x86-64 branch
|
||||
pub fn is_x86_64() -> bool {
|
||||
#[cfg(target_pointer_width = "64")] {
|
||||
|
|
|
@ -74,10 +74,10 @@ impl LuaError {
|
|||
}
|
||||
|
||||
#[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 {
|
||||
#[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)`");
|
||||
|
||||
let thread_id = u64::from(std::thread::current().id().as_u64()) as i64;
|
||||
|
|
Loading…
Reference in New Issue