Skip to content

Commit

Permalink
Fix multithreading crashes (#14)
Browse files Browse the repository at this point in the history
Fixes #12

* -lua_run
* -lua_openscript
* +lua_run_cl
* +lua_openscript_cl
* +lua_run_menu
* +lua_openscript_menu

I can add lua_run/lua_openscript back but won't be adding it right now
  • Loading branch information
Vurv78 authored Aug 2, 2021
1 parent 3b57280 commit 6bf714d
Show file tree
Hide file tree
Showing 12 changed files with 362 additions and 275 deletions.
12 changes: 10 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,29 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --release --verbose
run: |
rustup default nightly
cargo build --release --verbose
- name: Clippy
run: |
rustup component add clippy
cargo clippy
- name: Upload DLL
uses: actions/upload-artifact@v2
with:
name: 64 Bit
path: target/release/Autorun.dll

# In the future make this run on an x86 machine https://github.com/actions/runner/issues/423
buildx86:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: |
rustup default nightly
rustup target add i686-pc-windows-msvc
cargo build --release --verbose --target=i686-pc-windows-msvc
Expand Down
9 changes: 3 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "autorun"
version = "0.4.1"
version = "0.5.0"
authors = ["Vurv78 <[email protected]>"]
edition = "2018"

Expand All @@ -10,19 +10,16 @@ edition = "2018"
crate-type = ["dylib"]

[dependencies]

# lua_shared bindings
rglua = { git = "https://github.com/Vurv78/rglua" }

# Detour lua_loadbufferx and other functions
detour = { version = "0.8.0", default-features = false }
detour = "0.8.1"

# Global Mutable Variables
once_cell = "1.8.0"
atomic = "0.5.0"

# Misc
dirs = "3.0.2" # To get your home directory.
home = "0.5.3"
anyhow = "1.0.42"

# Logging
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Here are the fields for the ``sautorun`` table that gets passed in scripthook.
| CODE | string | The contents of the script |
| IP | string | IP of the server you are currently connected to |
| STARTUP | boolean | Whether the script is running from ``autorun.lua`` (true) or false |
| log | function<string, uint?> | A function that logs to your autorun console. Second param is level ascending with urgency, 5 being error, 4 warning, 3, info, 2 debug, 1 trace. Default 3 |
| log | function<string, uint?> | A function that logs to your autorun console. Second param is level ascending with urgency, 1 being error, 2 warning, 3, info, 4 debug, 5 trace. Default 3 |

### Examples
__hook.lua__
Expand All @@ -60,6 +60,11 @@ end
__autorun.lua__
This will be the first lua script to run on your client when you join a server, use this to make detours and whatnot.
```lua
local ERROR, WARN, INFO, DEBUG, TRACE = 5, 4, 3, 2, 1
local ERROR, WARN, INFO, DEBUG, TRACE = 1, 2, 3, 4, 5
sautorun.log( "Connected to server " .. sautorun.IP, DEBUG )
```

### Building
See the build_win_32.bat and build_win_64.bat files for basic builds.
**This requires Nightly**, make sure your default toolchain is nightly.
Until ``"thiscall"`` and ``static_detour!`` are stabilized, it will not be able to be built on stable.
4 changes: 2 additions & 2 deletions build_win_32.bat
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@echo off
rustup target add i686-pc-windows-msvc
cargo build --release --target=i686-pc-windows-msvc
move %cd%\target\i686-pc-windows-msvc\release\Autorun.dll %cd%\gmsv_autorun_win32.dll
cargo build --target=i686-pc-windows-msvc
move %cd%\target\i686-pc-windows-msvc\debug\Autorun.dll %cd%\gmsv_autorun_win32.dll
pause
4 changes: 2 additions & 2 deletions build_win_64.bat
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@echo off
cargo build --release
move %cd%\target\release\Autorun.dll %cd%\gmsv_autorun_win64.dll
cargo build
move %cd%\target\debug\Autorun.dll %cd%\gmsv_autorun_win64.dll
pause
173 changes: 173 additions & 0 deletions src/detours/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
use std::{fs, io::prelude::*, sync::atomic::Ordering};

use crate::sys::{
funcs::{getAutorunHandle, getClientState, setClientState},
runlua::runLuaEnv, statics::*
};

use rglua::{
lua_shared::{self, *},
types::*,
rstring,
interface::IPanel
};

use detour::static_detour;

static_detour! {
pub static luaL_newstate_h: extern "C" fn() -> LuaState;
pub static luaL_loadbufferx_h: extern "C" fn(LuaState, CharBuf, SizeT, CharBuf, CharBuf) -> CInt;
pub static joinserver_h: extern "C" fn(LuaState) -> CInt;
pub static paint_traverse_h: extern "thiscall" fn(&'static IPanel, usize, bool, bool);
}

fn luaL_newstate() -> LuaState {
let state = luaL_newstate_h.call();
info!("Got client state through luaL_newstate");
setClientState(state);
state
}

fn luaL_loadbufferx(state: LuaState, code: CharBuf, size: SizeT, identifier: CharBuf, mode: CharBuf) -> CInt {
use crate::sys::funcs::initMenuState;
if MENU_STATE.get().is_none() {
initMenuState(state)
.expect("Couldn't initialize menu state");
}

// Todo: Check if you're in menu state (Not by checking MENU_DLL because that can be modified by lua) and if so, don't dump files.
// Dump the file to sautorun-rs/lua_dumps/IP/...
let raw_path = &rstring!(identifier)[1 ..]; // Remove the @ from the beginning of the path.
let server_ip = CURRENT_SERVER_IP.load( Ordering::Relaxed );

let mut autoran = false;
let mut do_run = true;
if raw_path == "lua/includes/init.lua" {
if HAS_AUTORAN.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed).is_ok() {
// This will only run once when HAS_AUTORAN is false, setting it to true.
// Will be reset by JoinServer.
if let Ok(script) = fs::read_to_string(&*AUTORUN_SCRIPT_PATH) {
// Try to run here
if let Err(why) = runLuaEnv(&script, identifier, code, server_ip, true) {
error!("{}", why);
}
autoran = true;
} else {
error!( "Couldn't read your autorun script file at {}/{}", SAUTORUN_DIR.display(), AUTORUN_SCRIPT_PATH.display() );
}
}
}

if !autoran {
if let Ok(script) = fs::read_to_string(&*HOOK_SCRIPT_PATH) {
match runLuaEnv(&script, identifier, code, server_ip, false) {
Ok(_) => {
// If you return ``true`` in your sautorun/hook.lua file, then don't run the sautorun.CODE that is about to run.
if lua_type(state, 1) == rglua::globals::Lua::Type::Bool as i32 {
do_run = lua_toboolean(state, 1) == 0;
lua_pop(state, 1);
}
},
Err(why) => error!("{}", why)
}
}

}

if let Some(mut file) = getAutorunHandle(raw_path, server_ip) {
if let Err(why) = file.write_all( unsafe { std::ffi::CStr::from_ptr(code) }.to_bytes() ) {
error!("Couldn't write to file made from lua path [{}]. {}", raw_path, why);
}
}

if do_run {
// Call the original function and return the value.
return luaL_loadbufferx_h.call( state, code, size, identifier, mode );
}
0
}

// Since the first lua state will always be the menu state, just keep a variable for whether joinserver has been hooked or not,
// If not, then hook it.
pub fn joinserver(state: LuaState) -> CInt {
let ip = rstring!( lua_tolstring(state, 1, 0) );
info!("Joining Server with IP {}!", ip);

CURRENT_SERVER_IP.store(ip, Ordering::Relaxed); // Set the IP so we know where to write files in loadbufferx.
HAS_AUTORAN.store(false, Ordering::Relaxed);

joinserver_h.call(state)
}

fn paint_traverse(this: &'static IPanel, panel_id: usize, force_repaint: bool, force_allow: bool) {
paint_traverse_h.call(this, panel_id, force_repaint, force_allow);

let script_queue = &mut *LUA_SCRIPTS
.lock()
.unwrap();

if script_queue.len() > 0 {
let (realm, script) = script_queue.remove(0);

let state = match realm {
REALM_MENU => MENU_STATE.get().unwrap().load(Ordering::Acquire), // Code will never get into the queue without a menu state already existing.
REALM_CLIENT => getClientState()
};

if state == std::ptr::null_mut() { return; }

if luaL_loadbufferx_h.call(
state,
script.as_ptr() as *const i8,
script.len(),
"@RunString\0".as_ptr() as CharBuf,
"bt\0".as_ptr() as CharBuf
) != 0 || lua_pcall(state, 0, 0, 0) != 0 {
let err = lua_tostring(state, -1);
lua_pop(state, 1);
error!("{}", rstring!(err));
} else {
info!("Code [len {}] ran successfully.", script.len())
}
}
}

pub unsafe fn init() -> Result<(), detour::Error> {
luaL_loadbufferx_h
.initialize(*lua_shared::luaL_loadbufferx, luaL_loadbufferx)?
.enable()?;

luaL_newstate_h
.initialize(*lua_shared::luaL_newstate, luaL_newstate)?
.enable()?;

use rglua::interface::*;

let vgui_interface = get_from_interface( "VGUI_Panel009", get_interface_handle("vgui2.dll").unwrap() )
.unwrap() as *mut IPanel;

let panel_interface = vgui_interface.as_ref().unwrap();

type PaintTraverseFn = extern "thiscall" fn(&'static IPanel, usize, bool, bool);
// Get painttraverse raw function object to detour.
let painttraverse: PaintTraverseFn = std::mem::transmute(
(panel_interface.vtable as *mut *mut CVoid)
.offset(41)
.read()
);

paint_traverse_h
.initialize( painttraverse, paint_traverse )?
.enable()?;

Ok(())
}

pub unsafe fn cleanup() -> Result<(), detour::Error>{
luaL_loadbufferx_h.disable()?;
luaL_newstate_h.disable()?;
joinserver_h.disable()?;
paint_traverse_h.disable()?;

Ok(())
}
115 changes: 0 additions & 115 deletions src/hooks.rs

This file was deleted.

Loading

0 comments on commit 6bf714d

Please sign in to comment.