diff --git a/apps/server/src/game/initialize_game.rs b/apps/server/src/game/initialize_game.rs index 222ab51..ff87eaf 100644 --- a/apps/server/src/game/initialize_game.rs +++ b/apps/server/src/game/initialize_game.rs @@ -1,21 +1,13 @@ -use crate::game::game; use crate::game::game_state::GameResult; use super::board::Tile; -use super::game_state::{GameConfig, Move}; +use super::game_state::{Game, GameConfig, Move}; pub(crate) fn initialize_game( script_1: &str, script_2: &str, config: GameConfig, ) -> (GameResult, Vec>, Vec) { - let std = - std::fs::read_to_string("../scripts/std.lua").expect("Could not load standard library"); - - let mut game_session = game::new(std, config); - return game::start( - &mut game_session, - script_1.to_string(), - script_2.to_string(), - ); + let mut game_session = Game::new(config); + return game_session.start(script_1.to_string(), script_2.to_string()); } diff --git a/apps/server/src/game/mod.rs b/apps/server/src/game/mod.rs index 1592066..5e9530e 100644 --- a/apps/server/src/game/mod.rs +++ b/apps/server/src/game/mod.rs @@ -4,6 +4,8 @@ pub(crate) mod execute_move; pub(crate) mod game; pub(crate) mod game_state; pub(crate) mod initialize_game; +pub(crate) mod load_script; +pub(crate) mod load_script_with_validation; pub(crate) mod map_mirroring; pub(crate) mod parsing; pub(crate) mod path_find; diff --git a/apps/server/src/game/sandbox/mod.rs b/apps/server/src/game/sandbox/mod.rs index dad9c97..424b104 100644 --- a/apps/server/src/game/sandbox/mod.rs +++ b/apps/server/src/game/sandbox/mod.rs @@ -1,2 +1 @@ pub(crate) mod sandbox_executor; -pub(crate) mod terminate_thread; diff --git a/apps/server/src/game/sandbox/sandbox_executor.rs b/apps/server/src/game/sandbox/sandbox_executor.rs index eabf466..50d3c4b 100644 --- a/apps/server/src/game/sandbox/sandbox_executor.rs +++ b/apps/server/src/game/sandbox/sandbox_executor.rs @@ -3,10 +3,8 @@ use std::{ time::Duration, }; -use crate::game::{ - game::get_active_player_type, game_state::ErrorType, - sandbox::terminate_thread::terminate_thread, -}; +use crate::game::{game::get_active_player_type, game_state::ErrorType}; +use terminate_thread::Thread; use crate::game::{ board::{populate_board, serialize_board}, @@ -15,11 +13,6 @@ use crate::game::{ player::Player, }; -pub struct ThreadReturn { - thread_id: Option, - player_move: Result, -} - pub(crate) fn execute_lua_in_sandbox( player_one_sandbox_mutex: Arc>, player_two_sandbox_mutex: Arc>, @@ -29,16 +22,10 @@ pub(crate) fn execute_lua_in_sandbox( player_one_turn: bool, lua_function: String, ) -> Result { - let (tx, rx) = std::sync::mpsc::channel::(); + let (tx, rx) = std::sync::mpsc::channel::>(); // Sandbox execution of script // Limit execution time to 1 second - std::thread::spawn(move || { - tx.send(ThreadReturn { - thread_id: Some(thread_id::get()), - player_move: Ok(String::new()), - }) - .unwrap(); - + let terminatable_thread = Thread::spawn(move || { let starting_script = get_lua_script( lua_function, create_lua_game_object(walls, player_one_turn, player_one, player_two), @@ -51,35 +38,19 @@ pub(crate) fn execute_lua_in_sandbox( } if let Err(err) = active_sandbox.context(|ctx| ctx.load(&starting_script).exec()) { - tx.send(ThreadReturn { - thread_id: None, - player_move: Err(err), - }) - .unwrap(); + tx.send(Err(err)).unwrap(); } let raw_player_move = active_sandbox.context(|ctx| ctx.globals().get::<_, String>("ExternalGlobalVarResult")); drop(active_sandbox); - tx.send(ThreadReturn { - thread_id: None, - player_move: raw_player_move, - }) - .unwrap(); + tx.send(raw_player_move).unwrap(); }); - // First time we send the thread id through - // This does not have to be timed checked since this is before we - // execute the script. - let sandbox_thread_id = match rx.recv().unwrap().thread_id { - Some(id) => id, - _ => panic!("Could not get thread id"), - }; - // Second time we either get the result or a timeout error let player_move = match rx.recv_timeout(Duration::from_millis(500)) { - Ok(returned) => match returned.player_move { + Ok(returned) => match returned { Ok(move_string) => move_string, Err(error) => { return Err(ErrorType::RuntimeError { @@ -89,8 +60,7 @@ pub(crate) fn execute_lua_in_sandbox( } }, Err(_) => { - println!("Timed out"); - terminate_thread(sandbox_thread_id); + terminatable_thread.terminate(); return Err(ErrorType::TurnTimeout { fault: Some(get_active_player_type(player_one_turn)), }); @@ -148,3 +118,84 @@ fn serialize_player(player: &Player) -> String { player.x, player.y, player.wall_count ); } + +#[cfg(test)] +mod tests { + use crate::game::{ + game_state::{ErrorType, Game, GameConfig}, + load_script_with_validation::load_script_with_validation, + player::PlayerType, + }; + + use super::execute_lua_in_sandbox; + + fn mock_game() -> Game { + let config = GameConfig::new(); + let game = Game::new(config); + return game; + } + + #[test] + fn terminates_thread_on_long_execution() { + let game = mock_game(); + + load_script_with_validation( + &game.player_one_sandbox, + r#" + function onTurn(context) + return "0" + end + function onJump(context) + return "0" + end + "# + .to_string(), + PlayerType::Flipped, + ) + .unwrap(); + + assert!(execute_lua_in_sandbox( + game.player_one_sandbox, + game.player_two_sandbox, + game.walls, + game.player_one, + game.player_two, + true, + "onTurn".to_string(), + ) + .is_ok()); + + let game = mock_game(); + load_script_with_validation( + &game.player_one_sandbox, + r#" + function onTurn(context) + while true do + end + return "0" + end + function onJump(context) + return "0" + end + "# + .to_string(), + PlayerType::Flipped, + ) + .unwrap(); + + match execute_lua_in_sandbox( + game.player_one_sandbox, + game.player_two_sandbox, + game.walls, + game.player_one, + game.player_two, + true, + "onTurn".to_string(), + ) { + Err(ErrorType::TurnTimeout { + fault: Some(PlayerType::Flipped), + }) => assert!(true), + data => panic!("Expected timeout, got {:?}", data), + } + } +} diff --git a/apps/server/src/game/tests/game_tests.rs b/apps/server/src/game/tests/game_tests.rs index 65b30c7..0e35cee 100644 --- a/apps/server/src/game/tests/game_tests.rs +++ b/apps/server/src/game/tests/game_tests.rs @@ -1,8 +1,7 @@ #[cfg(test)] mod tests { use crate::game::{ - game, - game_state::{ErrorType, GameConfig, GameResult, MAP_SIZE}, + game_state::{ErrorType, Game, GameConfig, GameResult, MAP_SIZE}, player::{Player, PlayerType}, tests::util::{ _run_core_test, _run_test_with_custom_game_session, aj, load_script, load_std, @@ -391,7 +390,7 @@ mod tests { _run_test_with_custom_game_session( script, forward, - &mut game::custom_new( + &mut Game::custom_new( Player { player_type: PlayerType::Flipped, x: 4, diff --git a/apps/server/src/game/tests/on_jump_tests.rs b/apps/server/src/game/tests/on_jump_tests.rs index 8d043db..6e5963d 100644 --- a/apps/server/src/game/tests/on_jump_tests.rs +++ b/apps/server/src/game/tests/on_jump_tests.rs @@ -1,7 +1,6 @@ #[cfg(test)] mod tests { use crate::game::{ - game::custom_new, game_state::{ErrorType, Game, GameConfig, GameResult, Wall}, player::{Player, PlayerType}, tests::util::{_run_core_test, _run_test_with_custom_game_session, aj, at, mock_player}, @@ -79,7 +78,7 @@ mod tests { } fn horizontal_spawn() -> Game { - return custom_new( + return Game::custom_new( Player { x: 0, y: 4, @@ -173,7 +172,7 @@ mod tests { _run_test_with_custom_game_session( script.clone(), script, - &mut custom_new( + &mut Game::custom_new( mock_player(0, 4, 0, PlayerType::Regular), mock_player(0, 5, 0, PlayerType::Flipped), vec![Wall { @@ -206,7 +205,7 @@ mod tests { _run_test_with_custom_game_session( script.clone(), script, - &mut custom_new( + &mut Game::custom_new( mock_player(0, 0, 0, PlayerType::Regular), mock_player(0, 1, 0, PlayerType::Flipped), Vec::new(), @@ -230,7 +229,7 @@ mod tests { _run_test_with_custom_game_session( sideways.clone(), sideways, - &mut custom_new( + &mut Game::custom_new( mock_player(8, 4, 0, PlayerType::Regular), mock_player(7, 4, 0, PlayerType::Flipped), Vec::new(), diff --git a/apps/server/src/game/tests/util.rs b/apps/server/src/game/tests/util.rs index f808313..ecfcb7d 100644 --- a/apps/server/src/game/tests/util.rs +++ b/apps/server/src/game/tests/util.rs @@ -3,14 +3,13 @@ use rlua::Lua; use crate::{ external_related::readme_factory::{get_match_from_tiles, write_file}, game::{ - game, game_state::{ErrorType, Game, GameConfig, GameResult}, player::{Player, PlayerType}, }, }; pub(crate) fn _run_core_test(script: String, script2: String, is_equal: fn(GameResult) -> bool) { - let mut game_session = game::new(load_std(), GameConfig::new()); + let mut game_session = Game::new(GameConfig::new()); _run_test_with_custom_game_session(script, script2, &mut game_session, is_equal); } @@ -20,7 +19,7 @@ pub(crate) fn _run_test_with_custom_game_session( session: &mut Game, is_equal: fn(GameResult) -> bool, ) { - let (game_state_result, mut turns, _) = game::start(session, script, script2); + let (game_state_result, mut turns, _) = Game::start(session, script, script2); turns.reverse(); write_file("test_dump.temp.md", get_match_from_tiles(turns)).unwrap(); @@ -119,6 +118,6 @@ fn convert_uuid_to_variable(uuid: String) -> String { } pub(super) fn load_script(filename: &str) -> String { - std::fs::read_to_string(format!("{}{}.lua", "../scripts/", filename)) + std::fs::read_to_string(format!("{}{}.lua", "./scripts/", filename)) .expect("Could not load script") }