Changed the data_to_send to be a stack so many lines of code can

be pre-processed before the user interacts.
When the /happening api is called it just dequeues the front item
This commit is contained in:
2026-05-19 19:23:19 +01:00
parent 019f1088a3
commit ee34493895
6 changed files with 180 additions and 148 deletions
+18 -16
View File
@@ -1,6 +1,12 @@
import requests import requests
import os import os
import time import time
import sys
debug = False
try:
if sys.argv[1] == "debug": debug = True
except:
debug = False
# Loop and get new api # Loop and get new api
def main(): def main():
@@ -9,22 +15,18 @@ def main():
while True: while True:
try: try:
response = api_get() response = api_get()
if response["id"] != id: if debug: print(response)
id = response["id"] match response["action_type"]:
print(response) case "output":
match response["action_type"]: character = get_character(response["character"])
case "output": output(character, response["content"])
character = get_character(response["character"]) case "choice":
output(character, response["content"]) user_choice = choice(response["choices"])
case "choice": time.sleep(0.5)
user_choice = choice(response["choices"]) continue
time.sleep(0.5) case "end":
continue print("Exitting successfully")
case "end": os._exit(0)
print("Exitting successfully")
os._exit(0)
else:
continue
except: except:
print("Server not up or cannot be reached") print("Server not up or cannot be reached")
input() # Enter to go to next loop (testing) input() # Enter to go to next loop (testing)
+24 -22
View File
@@ -9,6 +9,7 @@ use crate::
HashMap, HashMap,
Arc, Arc,
Mutex, Mutex,
VecDeque,
config, config,
mpsc::Sender, mpsc::Sender,
info, info,
@@ -18,9 +19,8 @@ use crate::
Deserialize, Deserialize,
}; };
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct DataToSend { pub struct DataToSend {
pub id: u32,
pub action_type: String, pub action_type: String,
pub content: String, pub content: String,
pub character: 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 // tx to allow the program executor to move onto the next bit of code
pub async fn api_process pub async fn api_process
( (
data_to_send: Arc<Mutex<DataToSend>>, happening_queue: Arc<Mutex<VecDeque<DataToSend>>>,
characters: Arc<Mutex<HashMap::<String,character::Character>>>, characters: Arc<Mutex<HashMap::<String,character::Character>>>,
tx: Sender<(bool, usize, String)>, tx: Sender<(usize, String)>,
) )
{ {
// This data must be passed through to the api route in order to be used // 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 characters_filter = warp::any().map(move || Arc::clone(&characters));
let tx_filter = warp::any().map(move || tx.clone()); let tx_filter = warp::any().map(move || tx.clone());
let tx_filter2 = tx_filter.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 // The server route is loaded at address:port/happening
let main = warp::path("happening") let main = warp::path("happening")
.and(warp::get()) .and(warp::get())
.and(data_filter) .and(happening_queue_filter)
.and(tx_filter) .and(tx_filter)
// Perform this code on a GET request // Perform this code on a GET request
.map(|state: Arc<Mutex<DataToSend>>, tx_handle: Sender<(bool,usize,String)>| .map(|queue: Arc<Mutex<VecDeque<DataToSend>>>, tx_handle: Sender<(usize,String)>|
{ {
//debug!("GET: {state:?}"); //debug!("GET: {state:?}");
let reply = state.as_ref(); let mut queue = queue.lock().unwrap();
let _ = tx_handle.send((true,0,String::new())); let reply = queue.pop_front().unwrap_or_default();
warp::reply::json(&reply) // Send the reply data (data_to_send formatted as JSON) warp::reply::json(&reply) // Send the reply data (data_to_send formatted as JSON)
}).boxed(); }).boxed();
let characters = warp::path("character") let characters = warp::path("character")
@@ -91,9 +91,9 @@ pub async fn api_process
.and(warp::post()) .and(warp::post())
.and(warp::body::json()) .and(warp::body::json())
.and(tx_filter2) .and(tx_filter2)
.map(|index: usize, tx_handle: Sender<(bool,usize,String)>| { .map(|index: usize, tx_handle: Sender<(usize,String)>| {
debug!("Choice: {index}"); debug!("Choice: {index}");
let _ = tx_handle.send((true,index,String::new())); let _ = tx_handle.send((index,String::new()));
let reply = "ack"; let reply = "ack";
warp::reply::json(&reply) warp::reply::json(&reply)
}).boxed(); }).boxed();
@@ -101,9 +101,9 @@ pub async fn api_process
.and(warp::post()) .and(warp::post())
.and(warp::body::json()) .and(warp::body::json())
.and(tx_filter3) .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"; let reply = "ack";
warp::reply::json(&reply) warp::reply::json(&reply)
}).boxed(); }).boxed();
@@ -117,20 +117,22 @@ pub async fn api_process
// On fail, quit safely // On fail, quit safely
// If successful, return nothing // If successful, return nothing
pub fn modify_data pub fn modify_data // TODO rename
( (
data_to_send: &Arc<Mutex<DataToSend>>, happening_queue: &Arc<Mutex<VecDeque<DataToSend>>>,
action_type: String, action_type: String,
content: String, content: String,
character_name: String, character_name: String,
choices: Vec<String>, choices: Vec<String>,
) )
{ {
let mut data = data_to_send.lock().unwrap_or_exit("Data to send Mutex was poisoned",2); let mut queue = happening_queue.lock().unwrap_or_exit("Data to send Mutex was poisoned",2);
data.id += 1; let new_data = DataToSend {
data.action_type = action_type; action_type: action_type,
data.content = content; content: content,
data.character = character_name; character: character_name,
data.choices = choices; choices: choices,
drop(data); };
queue.push_back(new_data);
drop(queue);
} }
+19 -14
View File
@@ -11,7 +11,11 @@ use std::
env::args, env::args,
fs::File, fs::File,
io::Read, io::Read,
collections::HashMap, collections::
{
HashMap,
VecDeque,
},
sync::{Arc, Mutex, mpsc}, sync::{Arc, Mutex, mpsc},
}; };
use log:: use log::
@@ -70,25 +74,26 @@ async fn main()
}, },
}; };
// Initialise the data strcut that will be sent out during API GET requests // Initialise the data strcut that will be sent out during API GET requests
let data_to_send = Arc::new(Mutex::new(api::DataToSend let happening_stack = Arc::new(Mutex::new(
{ VecDeque::from([api::DataToSend{
id: 0, action_type: "begin".to_owned(),
action_type: "begin".to_owned(), content: String::new(), // TODO send title and description
content: String::new(), // TODO send title and description character: String::new(),
character: String::new(), choices: vec![],
choices: vec![], }])
})); ));
// setup the api stuff // // setup the api stuff //
// Make clones of the data Arc for the two processes // 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 characters_clone1 = Arc::clone(&characters);
let tx_clone = tx; let tx_clone = tx;
// Spawn a thread for warp api server // Spawn a thread for warp api server
tokio::spawn( tokio::spawn(
async move { 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 // // setup the parsing stuff //
@@ -106,16 +111,16 @@ async fn main()
let (tokens, labels) = tokenise::tokenise(&file_contents) let (tokens, labels) = tokenise::tokenise(&file_contents)
.unwrap_or_exit("Unable to tokenise data", 15); .unwrap_or_exit("Unable to tokenise data", 15);
debug!("{tokens:?}\n{labels:?}"); debug!("{tokens:?}\n{labels:?}");
let data_clone2 = Arc::clone(&data_to_send);
let characters_clone2 = Arc::clone(&characters); let characters_clone2 = Arc::clone(&characters);
let happening_stack2 = Arc::clone(&happening_stack);
// Run the parsing process for the DSL // Run the parsing process for the DSL
info!("DSL parsing begun"); 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 // Exit with error or success
Ok(()) => 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 // TODO fix quitting instantly
let _ = rx.recv(); // Wait for the client to respond let _ = rx.recv(); // Wait for the client to respond
info!("Program exited successfully"); info!("Program exited successfully");
+11 -92
View File
@@ -10,12 +10,14 @@ use crate::
mpsc::Receiver, mpsc::Receiver,
Arc, Arc,
Mutex, Mutex,
VecDeque,
info, info,
debug, debug,
warn, warn,
}; };
mod character_parse; mod character_parse;
mod keyword_parse;
// Parse the tokens in a file // Parse the tokens in a file
// Returns success or an error string // Returns success or an error string
@@ -23,16 +25,11 @@ pub fn token_parse(
tokens: &[tokenise::Token], tokens: &[tokenise::Token],
labels: &HashMap<String,usize>, labels: &HashMap<String,usize>,
characters: &Arc<Mutex<HashMap::<String, character::Character>>>, characters: &Arc<Mutex<HashMap::<String, character::Character>>>,
data_to_send: &Arc<Mutex<api::DataToSend>>, happening_queue: &Arc<Mutex<VecDeque<api::DataToSend>>>,
rx: &Receiver<(bool,usize,String)>, rx: &Receiver<(usize,String)>,
) -> Result<(),String> ) -> Result<(),String>
{ {
let mut index: usize = 0; let mut index: usize = 0;
if rx.recv().is_err()
{
warn!("Some issue with api");
// TODO eh?
}
info!("Client has connected"); info!("Client has connected");
// Run an infinite loop // Run an infinite loop
'parse_loop: loop 'parse_loop: loop
@@ -41,7 +38,12 @@ pub fn token_parse(
// Get the next token // Get the next token
let token: String = match tokens.get(index) 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 // Ignore closing braces and jump over opening brace blocks
Some(tokenise::Token::Bracket((bracket,new_index))) => Some(tokenise::Token::Bracket((bracket,new_index))) =>
{ {
@@ -56,7 +58,7 @@ pub fn token_parse(
// Handle a character // Handle a character
Some(tokenise::Token::Character(character_name)) => // TODO add support for narrator 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, Ok(increment) => increment,
Err((err,increment)) => Err((err,increment)) =>
@@ -65,7 +67,6 @@ pub fn token_parse(
increment increment
}, },
}; };
if rx.recv().is_err() { warn!("Some issue with api"); }
continue 'parse_loop continue 'parse_loop
} }
Some(_) => Some(_) =>
@@ -78,88 +79,6 @@ pub fn token_parse(
}; };
debug!("{index}: {token}"); debug!("{index}: {token}");
// The instructions are related to characters // 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<Mutex<api::DataToSend>>,)
-> Result<Vec<usize>, String>
{
let mut next_token: String = "or".to_string();
let mut choices: Vec<String> = Vec::new();
let mut choice_indeces: Vec<usize> = 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)
}
+5 -4
View File
@@ -9,6 +9,7 @@ use crate::
Mutex, Mutex,
Arc, Arc,
HashMap, HashMap,
VecDeque,
info, info,
warn, warn,
debug, debug,
@@ -23,7 +24,7 @@ pub fn character_parse
tokens: &[tokenise::Token], tokens: &[tokenise::Token],
character_name: String, character_name: String,
characters: &Arc<Mutex<HashMap::<String, character::Character>>>, characters: &Arc<Mutex<HashMap::<String, character::Character>>>,
data_to_send: &Arc<Mutex<api::DataToSend>>, happening_queue: &Arc<Mutex<VecDeque<api::DataToSend>>>,
) -> Result<usize,(String,usize)> ) -> Result<usize,(String,usize)>
{ {
let mut sum_index: usize = index; let mut sum_index: usize = index;
@@ -47,7 +48,7 @@ pub fn character_parse
let output = tokenise::get_string_token(tokens, sum_index) let output = tokenise::get_string_token(tokens, sum_index)
.map_err(|err| (err, index))?; .map_err(|err| (err, index))?;
debug!("Saying {output}"); 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" // 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 // 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) && character.set_field(&feature, &string)
.is_err() { warn!("Feature {feature} does not exist") } .is_err() { warn!("Feature {feature} does not exist") }
drop(characters); 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 // 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 // the character and not much other processing needed on the serverside
@@ -74,7 +75,7 @@ pub fn character_parse
sum_index += 1; sum_index += 1;
let content = tokenise::get_keyword_token(tokens, sum_index) let content = tokenise::get_keyword_token(tokens, sum_index)
.map_err(|err| (err, 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 // Catch all condition, if the instruction is unrecognised as a
// character command // character command
+103
View File
@@ -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<Mutex<HashMap::<String, character::Character>>>,
happening_queue: &Arc<Mutex<VecDeque<api::DataToSend>>>,
labels: &HashMap<String, usize>,
rx: &Receiver<(usize,String)>,
)
-> Result<usize, String>
{
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<Mutex<VecDeque<api::DataToSend>>>,)
-> Result<Vec<usize>, String>
{
let mut next_token: String = "or".to_string();
let mut choices: Vec<String> = Vec::new();
let mut choice_indeces: Vec<usize> = 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)
}