diff --git a/client/main.py b/client/main.py index 086c7bc..e2adea1 100644 --- a/client/main.py +++ b/client/main.py @@ -1,6 +1,12 @@ import requests import os import time +import sys +debug = False +try: + if sys.argv[1] == "debug": debug = True +except: + debug = False # Loop and get new api def main(): @@ -9,22 +15,18 @@ def main(): while True: try: response = api_get() - if response["id"] != id: - id = response["id"] - print(response) - match response["action_type"]: - case "output": - character = get_character(response["character"]) - output(character, response["content"]) - case "choice": - user_choice = choice(response["choices"]) - time.sleep(0.5) - continue - case "end": - print("Exitting successfully") - os._exit(0) - else: - continue + if debug: print(response) + match response["action_type"]: + case "output": + character = get_character(response["character"]) + output(character, response["content"]) + case "choice": + user_choice = choice(response["choices"]) + time.sleep(0.5) + continue + case "end": + print("Exitting successfully") + os._exit(0) except: print("Server not up or cannot be reached") input() # Enter to go to next loop (testing) diff --git a/server/src/api.rs b/server/src/api.rs index aef52fb..dcda98b 100644 --- a/server/src/api.rs +++ b/server/src/api.rs @@ -9,6 +9,7 @@ use crate:: HashMap, Arc, Mutex, + VecDeque, config, mpsc::Sender, info, @@ -18,9 +19,8 @@ use crate:: Deserialize, }; -#[derive(Debug, Deserialize, Serialize, Clone)] +#[derive(Debug, Deserialize, Serialize, Clone, Default)] pub struct DataToSend { - pub id: u32, pub action_type: String, pub content: String, pub character: String, @@ -32,13 +32,13 @@ pub struct DataToSend { // tx to allow the program executor to move onto the next bit of code pub async fn api_process ( - data_to_send: Arc>, + happening_queue: Arc>>, characters: Arc>>, - tx: Sender<(bool, usize, String)>, + tx: Sender<(usize, String)>, ) { // This data must be passed through to the api route in order to be used - let data_filter = warp::any().map(move || Arc::clone(&data_to_send)); + let happening_queue_filter = warp::any().map(move || Arc::clone(&happening_queue)); let characters_filter = warp::any().map(move || Arc::clone(&characters)); let tx_filter = warp::any().map(move || tx.clone()); let tx_filter2 = tx_filter.clone(); @@ -49,14 +49,14 @@ pub async fn api_process // The server route is loaded at address:port/happening let main = warp::path("happening") .and(warp::get()) - .and(data_filter) + .and(happening_queue_filter) .and(tx_filter) // Perform this code on a GET request - .map(|state: Arc>, tx_handle: Sender<(bool,usize,String)>| + .map(|queue: Arc>>, tx_handle: Sender<(usize,String)>| { //debug!("GET: {state:?}"); - let reply = state.as_ref(); - let _ = tx_handle.send((true,0,String::new())); + let mut queue = queue.lock().unwrap(); + let reply = queue.pop_front().unwrap_or_default(); warp::reply::json(&reply) // Send the reply data (data_to_send formatted as JSON) }).boxed(); let characters = warp::path("character") @@ -91,9 +91,9 @@ pub async fn api_process .and(warp::post()) .and(warp::body::json()) .and(tx_filter2) - .map(|index: usize, tx_handle: Sender<(bool,usize,String)>| { + .map(|index: usize, tx_handle: Sender<(usize,String)>| { debug!("Choice: {index}"); - let _ = tx_handle.send((true,index,String::new())); + let _ = tx_handle.send((index,String::new())); let reply = "ack"; warp::reply::json(&reply) }).boxed(); @@ -101,9 +101,9 @@ pub async fn api_process .and(warp::post()) .and(warp::body::json()) .and(tx_filter3) - .map(|input: String, tx_handle: Sender<(bool, usize, String)>| + .map(|input: String, tx_handle: Sender<(usize, String)>| { - let _ = tx_handle.send((true,0,input)); + let _ = tx_handle.send((0,input)); let reply = "ack"; warp::reply::json(&reply) }).boxed(); @@ -117,20 +117,22 @@ pub async fn api_process // On fail, quit safely // If successful, return nothing -pub fn modify_data +pub fn modify_data // TODO rename ( - data_to_send: &Arc>, + happening_queue: &Arc>>, action_type: String, content: String, character_name: String, choices: Vec, ) { - let mut data = data_to_send.lock().unwrap_or_exit("Data to send Mutex was poisoned",2); - data.id += 1; - data.action_type = action_type; - data.content = content; - data.character = character_name; - data.choices = choices; - drop(data); + let mut queue = happening_queue.lock().unwrap_or_exit("Data to send Mutex was poisoned",2); + let new_data = DataToSend { + action_type: action_type, + content: content, + character: character_name, + choices: choices, + }; + queue.push_back(new_data); + drop(queue); } diff --git a/server/src/main.rs b/server/src/main.rs index cd51982..52f6463 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -11,7 +11,11 @@ use std:: env::args, fs::File, io::Read, - collections::HashMap, + collections:: + { + HashMap, + VecDeque, + }, sync::{Arc, Mutex, mpsc}, }; use log:: @@ -70,25 +74,26 @@ async fn main() }, }; // Initialise the data strcut that will be sent out during API GET requests - let data_to_send = Arc::new(Mutex::new(api::DataToSend - { - id: 0, - action_type: "begin".to_owned(), - content: String::new(), // TODO send title and description - character: String::new(), - choices: vec![], - })); + let happening_stack = Arc::new(Mutex::new( + VecDeque::from([api::DataToSend{ + action_type: "begin".to_owned(), + content: String::new(), // TODO send title and description + character: String::new(), + choices: vec![], + }]) + )); // setup the api stuff // // Make clones of the data Arc for the two processes - let data_clone1 = Arc::clone(&data_to_send); + //let data_clone1 = Arc::clone(&data_to_send); + let happening_stack1 = Arc::clone(&happening_stack); let characters_clone1 = Arc::clone(&characters); let tx_clone = tx; // Spawn a thread for warp api server tokio::spawn( async move { - api::api_process(data_clone1, characters_clone1, tx_clone).await; + api::api_process(happening_stack1, characters_clone1, tx_clone).await; }); // setup the parsing stuff // @@ -106,16 +111,16 @@ async fn main() let (tokens, labels) = tokenise::tokenise(&file_contents) .unwrap_or_exit("Unable to tokenise data", 15); debug!("{tokens:?}\n{labels:?}"); - let data_clone2 = Arc::clone(&data_to_send); let characters_clone2 = Arc::clone(&characters); + let happening_stack2 = Arc::clone(&happening_stack); // Run the parsing process for the DSL info!("DSL parsing begun"); - match parsing::token_parse(&tokens, &labels, &characters_clone2, &data_clone2, &rx) + match parsing::token_parse(&tokens, &labels, &characters_clone2, &happening_stack2, &rx) { // Exit with error or success Ok(()) => { - api::modify_data(&data_to_send, "end".to_string(), String::new(), String::new(), vec![]); + api::modify_data(&happening_stack, "end".to_string(), String::new(), String::new(), vec![]); // TODO fix quitting instantly let _ = rx.recv(); // Wait for the client to respond info!("Program exited successfully"); diff --git a/server/src/parsing.rs b/server/src/parsing.rs index 2b11547..2de44ae 100644 --- a/server/src/parsing.rs +++ b/server/src/parsing.rs @@ -10,12 +10,14 @@ use crate:: mpsc::Receiver, Arc, Mutex, + VecDeque, info, debug, warn, }; mod character_parse; +mod keyword_parse; // Parse the tokens in a file // Returns success or an error string @@ -23,16 +25,11 @@ pub fn token_parse( tokens: &[tokenise::Token], labels: &HashMap, characters: &Arc>>, - data_to_send: &Arc>, - rx: &Receiver<(bool,usize,String)>, + happening_queue: &Arc>>, + rx: &Receiver<(usize,String)>, ) -> Result<(),String> { let mut index: usize = 0; - if rx.recv().is_err() - { - warn!("Some issue with api"); - // TODO eh? - } info!("Client has connected"); // Run an infinite loop 'parse_loop: loop @@ -41,7 +38,12 @@ pub fn token_parse( // Get the next token let token: String = match tokens.get(index) { - Some(tokenise::Token::Keyword(s)) => s.clone(), + Some(tokenise::Token::Keyword(token)) => + { + if token.to_lowercase().as_str() == "end" { return Ok(()); }; + index = keyword_parse::keyword_parse(tokens, token.to_string(), index, characters, happening_queue, labels, rx).unwrap(); + continue 'parse_loop; + }, // Ignore closing braces and jump over opening brace blocks Some(tokenise::Token::Bracket((bracket,new_index))) => { @@ -56,7 +58,7 @@ pub fn token_parse( // Handle a character Some(tokenise::Token::Character(character_name)) => // TODO add support for narrator { - index = match character_parse::character_parse(index+1,tokens,character_name.clone(),characters,data_to_send) + index = match character_parse::character_parse(index+1,tokens,character_name.clone(),characters,happening_queue) { Ok(increment) => increment, Err((err,increment)) => @@ -65,7 +67,6 @@ pub fn token_parse( increment }, }; - if rx.recv().is_err() { warn!("Some issue with api"); } continue 'parse_loop } Some(_) => @@ -78,88 +79,6 @@ pub fn token_parse( }; debug!("{index}: {token}"); // The instructions are related to characters - match token.to_lowercase().as_str() - { - "end" => - { - info!("END command, exiting"); - return Ok(()) // quit successfully - }, - "choice" => - { - let choice_indeces = choice_parse(tokens, index, data_to_send)?; - debug!("{choice_indeces:?}"); - if rx.recv().is_err() { warn!("Error sending choices to client"); } - let choice = match rx.recv() - { - Ok((_,choice,_)) => choice_indeces[choice], - Err(err) => - { - warn!("Error receiving choice from client, defaulting to choice 0 {err}"); - 0 - } - }; - index = choice; - continue 'parse_loop - }, - "or" => - { - info!("OR command, jumping over"); - index += 2; - let new_index = tokenise::get_closing_index(tokens, index)?; - index = new_index; - continue 'parse_loop - }, - // Jump to a particular index based on a label eg GOTO character_check - "goto" => - { - index += 1; - let label = tokenise::get_keyword_token(tokens, index)?; - index = if let Some(label_index) = labels.get(&label) { *label_index } - else - { - warn!("Label {label} does not exist"); - index + 1 - }; - debug!("Jumping to {index}"); - continue 'parse_loop - } - _ => - { - warn!("Invalid command: {token}"); - index += 1; - } - } - if rx.recv().is_err() { warn!("Some issue with api"); } } } -fn choice_parse(tokens: &[tokenise::Token], mut index: usize, data_to_send: &Arc>,) --> Result, String> -{ - let mut next_token: String = "or".to_string(); - let mut choices: Vec = Vec::new(); - let mut choice_indeces: Vec = Vec::new(); - while next_token == "or" - { - index += 1; - choices.push - ( - tokenise::get_string_token(tokens, index)? - ); - index += 1; - choice_indeces.push(index+1); - index = match tokenise::get_closing_index(tokens,index) - { - Ok(new_index) => new_index + 1, - Err(_) => break, - }; - next_token = match tokenise::get_keyword_token(tokens, index) - { - Ok(string) => string, - Err(_) => break, - } - }; - api::modify_data(data_to_send, "choice".to_string(), String::new(), String::new(), choices); - Ok(choice_indeces) -} diff --git a/server/src/parsing/character_parse.rs b/server/src/parsing/character_parse.rs index 0d760d3..a179c89 100644 --- a/server/src/parsing/character_parse.rs +++ b/server/src/parsing/character_parse.rs @@ -9,6 +9,7 @@ use crate:: Mutex, Arc, HashMap, + VecDeque, info, warn, debug, @@ -23,7 +24,7 @@ pub fn character_parse tokens: &[tokenise::Token], character_name: String, characters: &Arc>>, - data_to_send: &Arc>, + happening_queue: &Arc>>, ) -> Result { let mut sum_index: usize = index; @@ -47,7 +48,7 @@ pub fn character_parse let output = tokenise::get_string_token(tokens, sum_index) .map_err(|err| (err, index))?; debug!("Saying {output}"); - api::modify_data(data_to_send, "output".to_string(), output, character_name, vec![]); + api::modify_data(happening_queue, "output".to_string(), output, character_name, vec![]); }, // Change the property of the selected character eg @tim CHANGE name "Bill Buffins" // will change the character with ID tim to "Bill Buffins"; a character's ID cannot change @@ -65,7 +66,7 @@ pub fn character_parse && character.set_field(&feature, &string) .is_err() { warn!("Feature {feature} does not exist") } drop(characters); - api::modify_data(data_to_send, "change".to_string(), String::new(), character_name, vec![]); + api::modify_data(happening_queue, "change".to_string(), String::new(), character_name, vec![]); }, // These two are mainly just actions performed by the frontend client, so just tell the client to move/animate // the character and not much other processing needed on the serverside @@ -74,7 +75,7 @@ pub fn character_parse sum_index += 1; let content = tokenise::get_keyword_token(tokens, sum_index) .map_err(|err| (err, index))?; - api::modify_data(data_to_send, keyword.to_lowercase(), content, character_name, vec![]); + api::modify_data(happening_queue, keyword.to_lowercase(), content, character_name, vec![]); }, // Catch all condition, if the instruction is unrecognised as a // character command diff --git a/server/src/parsing/keyword_parse.rs b/server/src/parsing/keyword_parse.rs new file mode 100644 index 0000000..70bde61 --- /dev/null +++ b/server/src/parsing/keyword_parse.rs @@ -0,0 +1,103 @@ +use crate:: +{ + tokenise, + character, + api, + + HashMap, + Arc, + Mutex, + VecDeque, + warn, + debug, + info, + mpsc::Receiver, +}; + +pub fn keyword_parse( + tokens: &[tokenise::Token], + token: String, + mut index: usize, + characters: &Arc>>, + happening_queue: &Arc>>, + labels: &HashMap, + rx: &Receiver<(usize,String)>, +) +-> Result +{ + + match token.to_lowercase().as_str() + { + "choice" => + { + let choice_indeces = choice_parse(tokens, index, happening_queue)?; + debug!("{choice_indeces:?}"); + let choice = match rx.recv() + { + Ok((choice,_)) => choice_indeces[choice], + Err(err) => + { + warn!("Error receiving choice from client, defaulting to choice 0 {err}"); + 0 + } + }; + index = choice; + }, + "or" => + { + info!("OR command, jumping over"); + index += 2; + let new_index = tokenise::get_closing_index(tokens, index)?; + index = new_index; + }, + // Jump to a particular index based on a label eg GOTO character_check + "goto" => + { + index += 1; + let label = tokenise::get_keyword_token(tokens, index)?; + index = if let Some(label_index) = labels.get(&label) { *label_index } + else + { + warn!("Label {label} does not exist"); + index + 1 + }; + debug!("Jumping to {index}"); + } + _ => + { + warn!("Invalid command: {token}"); + index += 1; + } + } + Ok(index) +} + +fn choice_parse(tokens: &[tokenise::Token], mut index: usize, happening_queue: &Arc>>,) +-> Result, String> +{ + let mut next_token: String = "or".to_string(); + let mut choices: Vec = Vec::new(); + let mut choice_indeces: Vec = Vec::new(); + while next_token == "or" + { + index += 1; + choices.push + ( + tokenise::get_string_token(tokens, index)? + ); + index += 1; + choice_indeces.push(index+1); + index = match tokenise::get_closing_index(tokens,index) + { + Ok(new_index) => new_index + 1, + Err(_) => break, + }; + next_token = match tokenise::get_keyword_token(tokens, index) + { + Ok(string) => string, + Err(_) => break, + } + }; + api::modify_data(happening_queue, "choice".to_string(), String::new(), String::new(), choices); + Ok(choice_indeces) +}