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 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)
+24 -22
View File
@@ -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<Mutex<DataToSend>>,
happening_queue: Arc<Mutex<VecDeque<DataToSend>>>,
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
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<Mutex<DataToSend>>, tx_handle: Sender<(bool,usize,String)>|
.map(|queue: Arc<Mutex<VecDeque<DataToSend>>>, 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<Mutex<DataToSend>>,
happening_queue: &Arc<Mutex<VecDeque<DataToSend>>>,
action_type: String,
content: String,
character_name: String,
choices: Vec<String>,
)
{
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);
}
+19 -14
View File
@@ -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");
+11 -92
View File
@@ -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<String,usize>,
characters: &Arc<Mutex<HashMap::<String, character::Character>>>,
data_to_send: &Arc<Mutex<api::DataToSend>>,
rx: &Receiver<(bool,usize,String)>,
happening_queue: &Arc<Mutex<VecDeque<api::DataToSend>>>,
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<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,
Arc,
HashMap,
VecDeque,
info,
warn,
debug,
@@ -23,7 +24,7 @@ pub fn character_parse
tokens: &[tokenise::Token],
character_name: String,
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)>
{
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
+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)
}