diff options
-rw-r--r-- | book/book.js | 4 | ||||
-rw-r--r-- | book/book.typ | 4 | ||||
-rw-r--r-- | data/.gitignore | 1 | ||||
-rw-r--r-- | data/makefile | 7 | ||||
-rw-r--r-- | data/recipes/.gitignore | 1 | ||||
-rw-r--r-- | server/protocol/src/helpers.rs | 110 | ||||
-rw-r--r-- | server/protocol/src/lib.rs | 129 | ||||
-rw-r--r-- | server/src/commands.rs | 2 | ||||
-rw-r--r-- | server/src/data/mod.rs | 19 | ||||
-rw-r--r-- | server/src/entity/book.rs | 2 | ||||
-rw-r--r-- | server/src/entity/customers.rs | 1 | ||||
-rw-r--r-- | server/src/entity/mod.rs | 7 | ||||
-rw-r--r-- | server/src/server.rs | 4 |
13 files changed, 177 insertions, 114 deletions
diff --git a/book/book.js b/book/book.js index 84c0e9dc..094068c0 100644 --- a/book/book.js +++ b/book/book.js @@ -1,5 +1,5 @@ -const tr = t => ({ translated: t }) +const tr = t => ({ translation: { id: t, params: [] } }) const plain = t => ({ text: t }) const par_raw = (s, th, bold) => ({ t: "par", es: [{ @@ -42,7 +42,7 @@ const about = () => ({ par("b.about.name"), { t: "text", s: tr("b.about.image_attrib"), color: "#000000", size: 20, font: "FreeSans" }, { - t: "table", cols: 2, es: [ + t: "table", es: [ [par("b.about.image_attrib.name", false, true), par("b.about.image_attrib.author", false, true)], [par("b.nigiri"), par_raw(plain("Ahtziri Lagarde(unsplash)"))], [par("b.icecream"), par_raw(plain("Markus Spiske(unsplash), adapted"))], diff --git a/book/book.typ b/book/book.typ index 39f3cc46..14e617a0 100644 --- a/book/book.typ +++ b/book/book.typ @@ -21,7 +21,7 @@ } #let tr(s) = translations.at(s, default: text(fill: red)[TRANSLATION: #s]) -#let msgstr(o) = if "translated" in o { tr(o.translated) } else { o.text } +#let msgstr(o) = if "translation" in o { tr(o.translation.id) } else { o.text } #let element(elem) = if elem.t == "document" [ #for e in elem.es [ @@ -50,7 +50,7 @@ ] else if elem.t == "par" [ #par(..elem.es.map(element)) ] else if elem.t == "table" [ - #table(columns: elem.cols, ..elem.es.flatten().map(element)) + #table(columns: elem.es.at(0).len(), ..elem.es.flatten().map(element)) ] else [ #elem ] diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 00000000..08711f05 --- /dev/null +++ b/data/.gitignore @@ -0,0 +1 @@ +/book.json diff --git a/data/makefile b/data/makefile index 4c948a81..a5041803 100644 --- a/data/makefile +++ b/data/makefile @@ -14,11 +14,14 @@ # along with this program. If not, see <https://www.gnu.org/licenses/>. # SETS = default none anticurry -all: $(patsubst %,recipes/%.yaml,$(SETS)) +ALL = $(patsubst %,recipes/%.yaml,$(SETS)) book.json +all: $(ALL) graphs: recipes/default.svg JSR = deno run +book.json: ../book/book.json + cp $< $@ recipes/none.yaml: echo > $@ recipes/anticurry.yaml: recipes/default.yaml @@ -33,4 +36,4 @@ recipes/%.svg: recipes/%.gv.txt dot -Tsvg -Kdot >$@<$< clean: - $(RM) -r recipes/*.{yaml,gv.txt,svg} + $(RM) $(ALL) diff --git a/data/recipes/.gitignore b/data/recipes/.gitignore index a9e52ca2..77c36404 100644 --- a/data/recipes/.gitignore +++ b/data/recipes/.gitignore @@ -1,4 +1,3 @@ *.yaml -!newRecipes.yaml *.gv.txt *.svg diff --git a/server/protocol/src/helpers.rs b/server/protocol/src/helpers.rs new file mode 100644 index 00000000..924d0886 --- /dev/null +++ b/server/protocol/src/helpers.rs @@ -0,0 +1,110 @@ +use std::fmt::Display; + +use crate::{ + DocumentElement, Gamedata, ItemIndex, ItemLocation, PlayerID, Recipe, RecipeIndex, TileIndex, +}; + +impl Gamedata { + pub fn tile_name(&self, index: TileIndex) -> &String { + &self.tile_names[index.0] + } + pub fn is_tile_colliding(&self, index: TileIndex) -> bool { + self.tile_collide[index.0] + } + pub fn is_tile_interactable(&self, index: TileIndex) -> bool { + self.tile_interact[index.0] + } + pub fn item_name(&self, index: ItemIndex) -> &String { + &self.item_names[index.0] + } + pub fn recipe(&self, index: RecipeIndex) -> &Recipe { + &self.recipes[index.0] + } + pub fn get_tile_by_name(&self, name: &str) -> Option<TileIndex> { + self.tile_names + .iter() + .position(|t| t == name) + .map(TileIndex) + } + pub fn get_item_by_name(&self, name: &str) -> Option<ItemIndex> { + self.item_names + .iter() + .position(|t| t == name) + .map(ItemIndex) + } + pub fn recipes(&self) -> impl Iterator<Item = (RecipeIndex, &Recipe)> { + self.recipes + .iter() + .enumerate() + .map(|(i, e)| (RecipeIndex(i), e)) + } +} + +impl Recipe { + pub fn tile(&self) -> Option<TileIndex> { + match self { + Recipe::Passive { tile, .. } => *tile, + Recipe::Active { tile, .. } => *tile, + Recipe::Instant { tile, .. } => *tile, + } + } + pub fn speed(&self) -> Option<f32> { + match self { + Recipe::Passive { speed, .. } => Some(*speed), + Recipe::Active { speed, .. } => Some(*speed), + _ => None, + } + } + pub fn revert_speed(&self) -> Option<f32> { + match self { + Recipe::Passive { revert_speed, .. } => *revert_speed, + _ => None, + } + } + pub fn warn(&self) -> bool { + match self { + Recipe::Passive { warn, .. } => *warn, + _ => false, + } + } + pub fn inputs(&self) -> Vec<ItemIndex> { + match self { + Recipe::Passive { input, .. } => vec![*input], + Recipe::Active { input, .. } => vec![*input], + Recipe::Instant { inputs, .. } => inputs.iter().flat_map(|e| e.to_owned()).collect(), + } + } + pub fn outputs(&self) -> Vec<ItemIndex> { + match self { + Recipe::Passive { output, .. } => output.iter().copied().collect(), + Recipe::Active { outputs, .. } => outputs.iter().flat_map(|e| e.to_owned()).collect(), + Recipe::Instant { outputs, .. } => outputs.iter().flat_map(|e| e.to_owned()).collect(), + } + } + pub fn supports_tile(&self, tile: Option<TileIndex>) -> bool { + if let Some(tile_constraint) = self.tile() { + if let Some(tile) = tile { + tile == tile_constraint + } else { + false + } + } else { + true + } + } +} + +impl Display for ItemLocation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ItemLocation::Tile(pos) => write!(f, "tile({pos})"), + ItemLocation::Player(PlayerID(id)) => write!(f, "player({id})"), + } + } +} + +impl Default for DocumentElement { + fn default() -> Self { + Self::Document { es: vec![] } + } +} diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs index 1acf7997..3337db0a 100644 --- a/server/protocol/src/lib.rs +++ b/server/protocol/src/lib.rs @@ -21,10 +21,11 @@ use bincode::{ }; use glam::{IVec2, Vec2}; use serde::{Deserialize, Serialize}; -use std::{collections::HashSet, fmt::Display, sync::LazyLock}; +use std::{collections::HashSet, sync::LazyLock}; pub use glam; +pub mod helpers; pub mod movement; pub mod registry; @@ -290,7 +291,7 @@ pub enum PacketC { #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] #[serde(rename_all = "snake_case", tag = "menu", content = "data")] pub enum Menu { - Book, + Document(DocumentElement), Score(Score), } @@ -339,96 +340,6 @@ pub enum Recipe { }, } -impl Gamedata { - pub fn tile_name(&self, index: TileIndex) -> &String { - &self.tile_names[index.0] - } - pub fn is_tile_colliding(&self, index: TileIndex) -> bool { - self.tile_collide[index.0] - } - pub fn is_tile_interactable(&self, index: TileIndex) -> bool { - self.tile_interact[index.0] - } - pub fn item_name(&self, index: ItemIndex) -> &String { - &self.item_names[index.0] - } - pub fn recipe(&self, index: RecipeIndex) -> &Recipe { - &self.recipes[index.0] - } - pub fn get_tile_by_name(&self, name: &str) -> Option<TileIndex> { - self.tile_names - .iter() - .position(|t| t == name) - .map(TileIndex) - } - pub fn get_item_by_name(&self, name: &str) -> Option<ItemIndex> { - self.item_names - .iter() - .position(|t| t == name) - .map(ItemIndex) - } - pub fn recipes(&self) -> impl Iterator<Item = (RecipeIndex, &Recipe)> { - self.recipes - .iter() - .enumerate() - .map(|(i, e)| (RecipeIndex(i), e)) - } -} - -impl Recipe { - pub fn tile(&self) -> Option<TileIndex> { - match self { - Recipe::Passive { tile, .. } => *tile, - Recipe::Active { tile, .. } => *tile, - Recipe::Instant { tile, .. } => *tile, - } - } - pub fn speed(&self) -> Option<f32> { - match self { - Recipe::Passive { speed, .. } => Some(*speed), - Recipe::Active { speed, .. } => Some(*speed), - _ => None, - } - } - pub fn revert_speed(&self) -> Option<f32> { - match self { - Recipe::Passive { revert_speed, .. } => *revert_speed, - _ => None, - } - } - pub fn warn(&self) -> bool { - match self { - Recipe::Passive { warn, .. } => *warn, - _ => false, - } - } - pub fn inputs(&self) -> Vec<ItemIndex> { - match self { - Recipe::Passive { input, .. } => vec![*input], - Recipe::Active { input, .. } => vec![*input], - Recipe::Instant { inputs, .. } => inputs.iter().flat_map(|e| e.to_owned()).collect(), - } - } - pub fn outputs(&self) -> Vec<ItemIndex> { - match self { - Recipe::Passive { output, .. } => output.iter().copied().collect(), - Recipe::Active { outputs, .. } => outputs.iter().flat_map(|e| e.to_owned()).collect(), - Recipe::Instant { outputs, .. } => outputs.iter().flat_map(|e| e.to_owned()).collect(), - } - } - pub fn supports_tile(&self, tile: Option<TileIndex>) -> bool { - if let Some(tile_constraint) = self.tile() { - if let Some(tile) = tile { - tile == tile_constraint - } else { - false - } - } else { - true - } - } -} - #[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode, Copy, PartialEq, Eq, Hash)] #[serde(rename_all = "snake_case")] pub enum ItemLocation { @@ -436,11 +347,31 @@ pub enum ItemLocation { Player(PlayerID), } -impl Display for ItemLocation { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ItemLocation::Tile(pos) => write!(f, "tile({pos})"), - ItemLocation::Player(PlayerID(id)) => write!(f, "player({id})"), - } - } +#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)] +#[serde(rename_all = "snake_case", tag = "t")] +pub enum DocumentElement { + Document { + es: Vec<DocumentElement>, + }, + Page { + background: Option<String>, + es: Vec<DocumentElement>, + }, + List { + es: Vec<DocumentElement>, + }, + Table { + es: Vec<Vec<DocumentElement>>, + }, + Par { + es: Vec<DocumentElement>, + }, + Text { + s: Message, + color: String, + size: f32, + font: String, + #[serde(default)] + bold: bool, + }, } diff --git a/server/src/commands.rs b/server/src/commands.rs index feb6ab65..25b51bff 100644 --- a/server/src/commands.rs +++ b/server/src/commands.rs @@ -195,7 +195,7 @@ impl Server { .await .map_err(|e| TrError::Plain(e.to_string()))?; } - Command::Book => replies.push(PacketC::Menu(Menu::Book)), + Command::Book => replies.push(PacketC::Menu(Menu::Document(self.data.book.clone()))), Command::Download { r#type, name } => { let source = match r#type { DownloadType::Map => self.index.read_map(&name).await, diff --git a/server/src/data/mod.rs b/server/src/data/mod.rs index 39684c6f..549574c7 100644 --- a/server/src/data/mod.rs +++ b/server/src/data/mod.rs @@ -20,12 +20,12 @@ pub mod demands; pub mod index; use crate::entity::{construct_entity, Entities, EntityDecl}; -use anyhow::{anyhow, bail, Result}; +use anyhow::{anyhow, bail, Context, Result}; use demands::generate_demands; use hurrycurry_bot::algos::ALGO_CONSTRUCTORS; use hurrycurry_protocol::{ glam::{IVec2, Vec2}, - Gamedata, ItemIndex, MapMetadata, Recipe, TileIndex, + DocumentElement, Gamedata, ItemIndex, MapMetadata, Recipe, TileIndex, }; use serde::{Deserialize, Serialize}; use std::{ @@ -87,14 +87,15 @@ pub struct DemandDecl { points: i64, } -#[derive(Debug,Clone, Default)] +#[derive(Debug, Clone, Default)] #[rustfmt::skip] pub struct Serverdata { pub initial_map: HashMap<IVec2, (TileIndex, Option<ItemIndex>)>, pub chef_spawn: Vec2, pub customer_spawn: Vec2, pub score_baseline: i64, - pub default_timer: Option<Duration> + pub default_timer: Option<Duration>, + pub book: DocumentElement } #[derive(Debug, Deserialize, Default)] @@ -145,7 +146,13 @@ impl DataIndex { .read_recipes(map_in.recipes.as_deref().unwrap_or("default")) .await?, )?; - build_data(&self.maps, map.to_string(), map_in, recipes_in) + let book = serde_json::from_str( + &read_to_string(data_dir().join("book.json")) + .await + .context("loading book")?, + ) + .context("invalid book")?; + build_data(&self.maps, map.to_string(), map_in, recipes_in, book) } } @@ -154,6 +161,7 @@ pub fn build_data( map_name: String, map_in: MapDecl, recipes_in: Vec<RecipeDecl>, + book: DocumentElement, ) -> Result<(Gamedata, Serverdata, Entities)> { let reg = ItemTileRegistry::default(); let mut recipes = Vec::new(); @@ -304,6 +312,7 @@ pub fn build_data( chef_spawn, customer_spawn, default_timer, + book, score_baseline: map_in.score_baseline, }, entities, diff --git a/server/src/entity/book.rs b/server/src/entity/book.rs index 70197764..8b152bb9 100644 --- a/server/src/entity/book.rs +++ b/server/src/entity/book.rs @@ -32,7 +32,7 @@ impl Entity for Book { ) -> Result<bool, TrError> { if pos == Some(self.0) { if let Some(r) = c.replies { - r.push(PacketC::Menu(Menu::Book)); + r.push(PacketC::Menu(Menu::Document(c.serverdata.book.clone()))); } return Ok(true); } diff --git a/server/src/entity/customers.rs b/server/src/entity/customers.rs index 1e8b6b13..0c3ced8d 100644 --- a/server/src/entity/customers.rs +++ b/server/src/entity/customers.rs @@ -65,6 +65,7 @@ impl Entity for Customers { for bot in &mut self.customers { bot.tick(EntityContext { game: c.game, + serverdata: c.serverdata, packet_out: c.packet_out, packet_in: c.packet_in, score_changed: c.score_changed, diff --git a/server/src/entity/mod.rs b/server/src/entity/mod.rs index 87656f0d..044fccab 100644 --- a/server/src/entity/mod.rs +++ b/server/src/entity/mod.rs @@ -25,7 +25,11 @@ pub mod item_portal; pub mod player_portal; pub mod tutorial; -use crate::{data::ItemTileRegistry, message::TrError, scoreboard::ScoreboardStore}; +use crate::{ + data::{ItemTileRegistry, Serverdata}, + message::TrError, + scoreboard::ScoreboardStore, +}; use anyhow::{anyhow, Result}; use book::Book; use campaign::{Gate, GateCondition, Map}; @@ -47,6 +51,7 @@ pub type Entities = Vec<DynEntity>; pub struct EntityContext<'a> { pub game: &'a mut Game, + pub serverdata: &'a Serverdata, pub packet_out: &'a mut VecDeque<PacketC>, pub packet_in: &'a mut VecDeque<PacketS>, pub score_changed: &'a mut bool, diff --git a/server/src/server.rs b/server/src/server.rs index 18f78fc0..d3e9ab59 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -340,6 +340,7 @@ impl Server { packet_in: &mut self.packet_loopback, score_changed: &mut self.score_changed, scoreboard: &self.scoreboard, + serverdata: self.data.as_ref(), replies: None, dt: 0., load_map: &mut None, @@ -447,6 +448,7 @@ impl Server { packet_in: &mut self.packet_loopback, score_changed: &mut self.score_changed, load_map: &mut None, + serverdata: &self.data, scoreboard: &self.scoreboard, replies: Some(replies), dt: 0., @@ -699,6 +701,7 @@ impl Server { score_changed: &mut self.score_changed, packet_in: &mut self.packet_loopback, scoreboard: &self.scoreboard, + serverdata: &self.data, replies: None, dt, }) { @@ -714,6 +717,7 @@ impl Server { score_changed: &mut self.score_changed, packet_in: &mut self.packet_loopback, scoreboard: &self.scoreboard, + serverdata: &self.data, replies: None, dt: 0., }); |