Skip to content

Commit

Permalink
refactor: game struct and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hampfh committed Jul 13, 2024
1 parent c45aa74 commit e811895
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 62 deletions.
14 changes: 3 additions & 11 deletions apps/server/src/game/initialize_game.rs
Original file line number Diff line number Diff line change
@@ -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<Tile>>, Vec<Move>) {
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());
}
2 changes: 2 additions & 0 deletions apps/server/src/game/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 0 additions & 1 deletion apps/server/src/game/sandbox/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
pub(crate) mod sandbox_executor;
pub(crate) mod terminate_thread;
127 changes: 89 additions & 38 deletions apps/server/src/game/sandbox/sandbox_executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -15,11 +13,6 @@ use crate::game::{
player::Player,
};

pub struct ThreadReturn {
thread_id: Option<usize>,
player_move: Result<String, rlua::Error>,
}

pub(crate) fn execute_lua_in_sandbox(
player_one_sandbox_mutex: Arc<Mutex<rlua::Lua>>,
player_two_sandbox_mutex: Arc<Mutex<rlua::Lua>>,
Expand All @@ -29,16 +22,10 @@ pub(crate) fn execute_lua_in_sandbox(
player_one_turn: bool,
lua_function: String,
) -> Result<String, ErrorType> {
let (tx, rx) = std::sync::mpsc::channel::<ThreadReturn>();
let (tx, rx) = std::sync::mpsc::channel::<Result<String, rlua::Error>>();
// 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),
Expand All @@ -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 {
Expand All @@ -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)),
});
Expand Down Expand Up @@ -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),
}
}
}
5 changes: 2 additions & 3 deletions apps/server/src/game/tests/game_tests.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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,
Expand Down
9 changes: 4 additions & 5 deletions apps/server/src/game/tests/on_jump_tests.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -79,7 +78,7 @@ mod tests {
}

fn horizontal_spawn() -> Game {
return custom_new(
return Game::custom_new(
Player {
x: 0,
y: 4,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(),
Expand All @@ -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(),
Expand Down
7 changes: 3 additions & 4 deletions apps/server/src/game/tests/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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();
Expand Down Expand Up @@ -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")
}

0 comments on commit e811895

Please sign in to comment.