diff options
| -rw-r--r-- | Cargo.lock | 42 | ||||
| -rw-r--r-- | data/recipes.yaml | 98 | ||||
| -rw-r--r-- | data/recipes_raw.yaml | 37 | ||||
| -rw-r--r-- | server/Cargo.toml | 1 | ||||
| -rw-r--r-- | server/src/game.rs | 133 | ||||
| -rw-r--r-- | server/src/interaction.rs | 66 | ||||
| -rw-r--r-- | server/src/lib.rs | 2 | ||||
| -rw-r--r-- | server/src/main.rs | 8 | ||||
| -rw-r--r-- | server/src/protocol.rs | 38 | ||||
| -rw-r--r-- | server/src/recipes.rs | 73 | ||||
| -rw-r--r-- | test-client/main.ts | 19 | ||||
| -rw-r--r-- | test-client/protocol.ts | 31 | 
12 files changed, 349 insertions, 199 deletions
| @@ -206,6 +206,12 @@ dependencies = [  ]  [[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]]  name = "fnv"  version = "1.0.7"  source = "registry+https://github.com/rust-lang/crates.io-index" @@ -292,6 +298,12 @@ dependencies = [  ]  [[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]]  name = "hermit-abi"  version = "0.3.9"  source = "registry+https://github.com/rust-lang/crates.io-index" @@ -321,6 +333,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"  checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"  [[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]]  name = "is_terminal_polyfill"  version = "1.70.0"  source = "registry+https://github.com/rust-lang/crates.io-index" @@ -576,6 +598,19 @@ dependencies = [  ]  [[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]]  name = "sha1"  version = "0.10.6"  source = "registry+https://github.com/rust-lang/crates.io-index" @@ -728,6 +763,7 @@ dependencies = [   "log",   "serde",   "serde_json", + "serde_yaml",   "tokio",   "tokio-tungstenite",  ] @@ -739,6 +775,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"  checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"  [[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]]  name = "utf-8"  version = "0.7.6"  source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/data/recipes.yaml b/data/recipes.yaml index c35bee30..de8de444 100644 --- a/data/recipes.yaml +++ b/data/recipes.yaml @@ -1,86 +1,78 @@ -# tomato pipeline -- tile: counter +- tile: floor +  action: !never # tomato pipeline + +- tile: meat-spawn +  action: !instant +  outputs: [raw-meat] + + +- tile: table    inputs: [tomato]    outputs: [sliced-tomato] -  action: hold -  duration: 3 -- tile: counter -  inputs: [sliced-tomato,plate] +  action: !active 3 +- tile: table +  inputs: [sliced-tomato, plate]    outputs: [sliced-tomato-meal] -  action: instant -  duration: 0 +  action: !instant # bread pipeline -# bread pipeline -- tile: counter + +- tile: table    inputs: [flour]    outputs: [dough] -  action: hold -  duration: 5 +  action: !active 5  - tile: oven    inputs: [dough]    outputs: [bread] -  action: wait -  duration: 20 -- tile: counter -  inputs: [bread,plate] +  action: !passive 20 +- tile: table +  inputs: [bread, plate]    outputs: [bread-meal] -  action: instant -  duration: 0 +  action: !instant # steak pipeline -# steak pipeline -- tile: oven + +- tile: pan    inputs: [raw-steak]    outputs: [steak] -  action: wait -  duration: 15 -- tile: counter -  inputs: [steak,plate] +  action: !passive 15 +- tile: table +  inputs: [steak, plate]    outputs: [steak-meal] -  action: instant -  duration: 0 +  action: !instant # combination meals + -# combination meals -- tile: counter -  inputs: [steak-meal,bread] +- tile: table +  inputs: [steak-meal, bread]    outputs: [burger-meal] -  action: instant -  duration: 0 -- tile: counter -  inputs: [steak-meal,tomato] +  action: !instant +- tile: table +  inputs: [steak-meal, tomato]    outputs: [tomatosteak-meal] -  action: instant -  duration: 0 -- tile: counter -  inputs: [burger-meal,tomato] +  action: !instant +- tile: table +  inputs: [burger-meal, tomato]    outputs: [tomatoburger-meal] -  action: instant -  duration: 0 -- tile: counter -  inputs: [tomatosteak-meal,bread] +  action: !instant +- tile: table +  inputs: [tomatosteak-meal, bread]    outputs: [tomatoburger-meal] -  action: instant -  duration: 0 +  action: !instant # cups + -# cups  - tile: watercooler    inputs: [glass]    outputs: [water] -  action: wait -  duration: 2 +  action: !passive 2  - tile: sink    inputs: [water]    outputs: [glass] -  action: instant -  duration: 0 +  action: !instant # cleaning + -# cleaning  - tile: sink    inputs: [dirty-glass]    outputs: [glass] -  action: hold -  duration: 5 +  action: !active 5  - tile: sink    inputs: [dirty-plate]    outputs: [plate] -  action: hold -  duration: 5 +  action: !active 5 diff --git a/data/recipes_raw.yaml b/data/recipes_raw.yaml deleted file mode 100644 index 75939c11..00000000 --- a/data/recipes_raw.yaml +++ /dev/null @@ -1,37 +0,0 @@ - -- tile: cutting-plate -  inputs: [tomato] -  outputs: [sliced-tomato] -  duration: 3 -  active: true - -- tile: sink -  inputs: [empty-bottle] -  outputs: [water-bottle] -  instant: true - -- tile: dishwasher -  inputs: [dirty-dish] -  output: [empty-dish] -  duration: 10 - -- tile: table -  inputs: [steak, herbs] -  outputs: [steak-with-herbs] -  ordered: true -  instant: true - -- tile: table -  inputs: [apple-juice-bottle, carbonated-water-bottle] -  outputs: [apply-soda-bottle, empty-bottle] -  instant: true - -- tile: table -  inputs: [empty-cup, <a>-bottle] -  outputs: [<a>-cup, empty-bottle] -  instant: true - -- tile: flour-bag -  outputs: [flour] -  instant: true - diff --git a/server/Cargo.toml b/server/Cargo.toml index 27e6f4b7..447a3ff1 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -13,3 +13,4 @@ tokio = { version = "1.38.0", features = ["full"] }  serde_json = "1.0.117"  tokio-tungstenite = "0.23.1"  futures-util = "0.3.30" +serde_yaml = "0.9.34+deprecated" diff --git a/server/src/game.rs b/server/src/game.rs index a9ee8f3d..a7a07881 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -1,60 +1,81 @@ -use crate::protocol::{Item, PacketC, PacketS, Tile, ID}; +use crate::{ +    protocol::{ItemID, ItemIndex, PacketC, PacketS, PlayerID, TileIndex}, +    recipes::Gamedata, +};  use anyhow::{anyhow, Result};  use glam::IVec2; -use log::info; -use std::collections::{HashMap, VecDeque}; +use std::{ +    collections::{HashMap, VecDeque}, +    ops::Deref, +    sync::Arc, +}; -struct TileData { -    kind: Tile, -    items: Vec<ID>, +pub struct Tile { +    kind: TileIndex, +    items: Vec<ItemID>,      active: bool,      progress: f32,  }  struct Player {      name: String, -    hand: Option<ID>, +    hand: Option<ItemID>,  } -#[derive(Default)]  pub struct Game { -    item_id_counter: ID, -    tiles: HashMap<IVec2, TileData>, -    items: HashMap<ID, Item>, -    players: HashMap<ID, Player>, +    data: Arc<Gamedata>, +    item_id_counter: ItemID, +    tiles: HashMap<IVec2, Tile>, +    items: HashMap<ItemID, ItemIndex>, +    players: HashMap<PlayerID, Player>,      packet_out: VecDeque<PacketC>,  }  impl Game { -    pub fn new() -> Self { -        let mut g = Self::default(); +    pub fn new(gamedata: Arc<Gamedata>) -> Self { +        let mut g = Self { +            data: gamedata.clone(), +            item_id_counter: 0, +            items: Default::default(), +            packet_out: Default::default(), +            players: Default::default(), +            tiles: Default::default(), +        };          for x in -5..5 {              for y in -5..5 {                  g.tiles -                    .insert(IVec2 { x, y }, Tile("floor".to_string()).into()); +                    .insert(IVec2 { x, y }, gamedata.get_tile("floor").unwrap().into());              }          }          for x in -5..5 { -            g.tiles -                .insert(IVec2 { x, y: -5 }, Tile("table".to_string()).into()); -            g.tiles -                .insert(IVec2 { x, y: 4 }, Tile("table".to_string()).into()); +            g.tiles.insert( +                IVec2 { x, y: -5 }, +                gamedata.get_tile("table").unwrap().into(), +            ); +            g.tiles.insert( +                IVec2 { x, y: 4 }, +                gamedata.get_tile("table").unwrap().into(), +            );          }          for y in -5..5 { -            g.tiles -                .insert(IVec2 { x: -5, y }, Tile("table".to_string()).into()); -            g.tiles -                .insert(IVec2 { x: 4, y }, Tile("table".to_string()).into()); +            g.tiles.insert( +                IVec2 { x: -5, y }, +                gamedata.get_tile("table").unwrap().into(), +            ); +            g.tiles.insert( +                IVec2 { x: 4, y }, +                gamedata.get_tile("table").unwrap().into(), +            );          }          g.tiles.extend( -            [([-5, 1], "pan"), ([-5, 2], "pan"), ([4, 3], "flour_bag")].map(|(k, v)| { +            [([-5, 1], "pan"), ([-5, 2], "pan"), ([4, 3], "meat-spawn")].map(|(k, v)| {                  (                      IVec2::from_array(k), -                    TileData { +                    Tile {                          active: false,                          items: vec![], -                        kind: Tile(v.to_string()).into(), +                        kind: gamedata.get_tile(v).unwrap().into(),                          progress: 0.,                      },                  ) @@ -68,7 +89,7 @@ impl Game {          self.packet_out.pop_front()      } -    pub fn prime_client(&self, id: ID) -> Vec<PacketC> { +    pub fn prime_client(&self, id: PlayerID) -> Vec<PacketC> {          let mut out = Vec::new();          for (&id, player) in &self.players {              out.push(PacketC::AddPlayer { @@ -90,11 +111,14 @@ impl Game {                  })              }          } -        out.push(PacketC::Joined { id }); +        out.push(PacketC::Joined { +            id, +            data: self.data.deref().to_owned(), +        });          out      } -    pub fn packet_in(&mut self, player: ID, packet: PacketS) -> Result<()> { +    pub fn packet_in(&mut self, player: PlayerID, packet: PacketS) -> Result<()> {          match packet {              PacketS::Join { name } => {                  self.players.insert( @@ -126,50 +150,27 @@ impl Game {                      .push_back(PacketC::Position { player, pos, rot });              }              PacketS::Interact { pos, edge } => { -                if !edge { -                    return Ok(()); -                } -                let tile = self -                    .tiles -                    .get_mut(&pos) -                    .ok_or(anyhow!("interacting with empty tile"))?; -                let player_data = self -                    .players -                    .get_mut(&player) -                    .ok_or(anyhow!("player does not exist"))?; -                if tile.kind.0 == "flour_bag" { -                    info!("new flour"); -                    let item = Item("flour".to_string()); -                    self.items.insert(self.item_id_counter, item.clone()); -                    tile.items.push(self.item_id_counter); -                    self.packet_out.push_back(PacketC::ProduceItem { -                        id: self.item_id_counter, -                        pos, -                        kind: item, -                    }); -                    self.item_id_counter += 1; -                } -                if let Some(item) = player_data.hand.take() { -                    info!("put {item}"); -                    tile.items.push(item); -                    self.packet_out.push_back(PacketC::PutItem { item, pos }) -                } else { -                    if let Some(item) = tile.items.pop() { -                        info!("take {item}"); -                        player_data.hand = Some(item); -                        self.packet_out -                            .push_back(PacketC::TakeItem { item, player }) -                    } -                } +                // if let Some(item) = player_data.hand.take() { +                //     info!("put {item}"); +                //     tile.items.push(item); +                //     self.packet_out.push_back(PacketC::PutItem { item, pos }) +                // } else { +                //     if let Some(item) = tile.items.pop() { +                //         info!("take {item}"); +                //         player_data.hand = Some(item); +                //         self.packet_out +                //             .push_back(PacketC::TakeItem { item, player }) +                //     } +                // }              }          }          Ok(())      }  } -impl From<Tile> for TileData { -    fn from(kind: Tile) -> Self { +impl From<TileIndex> for Tile { +    fn from(kind: TileIndex) -> Self {          Self {              kind,              progress: 0., diff --git a/server/src/interaction.rs b/server/src/interaction.rs new file mode 100644 index 00000000..5f8b0097 --- /dev/null +++ b/server/src/interaction.rs @@ -0,0 +1,66 @@ +use crate::{ +    protocol::{ItemIndex, TileIndex}, +    recipes::{Action, Gamedata}, +}; +use std::collections::BTreeSet; + +pub enum Out { +    Take(usize), +    Put, +    Produce(ItemIndex), +    Consume(usize), +} +use Out::*; + +pub fn interact( +    data: &Gamedata, +    edge: bool, +    tile: TileIndex, +    items: &[ItemIndex], +    hand: &Option<ItemIndex>, +    mut out: impl FnMut(Out), +) { +    let mut allowed = BTreeSet::new(); +    for r in &data.recipes { +        if r.tile == tile { +            allowed.extend(r.inputs.clone()) +        } +    } +    if !edge { +        return; +    } + +    let mut put_item = None; +    if let Some(hand) = hand { +        if allowed.contains(hand) { +            out(Put); +            put_item = Some(*hand); +        } +    } + +    for r in &data.recipes { +        let ok = r +            .inputs +            .iter() +            .all(|e| items.contains(e) || put_item == Some(*e)) +            && r.inputs.len() == items.len(); +        if ok { +            match r.action { +                Action::Passive(_) => todo!(), +                Action::Active(_) => todo!(), +                Action::Instant => { +                    for i in 0..items.len() { +                        out(Consume(i)) +                    } +                    for i in &r.outputs { +                        out(Produce(*i)); +                    } +                    if !r.outputs.is_empty() { +                        out(Take(r.outputs.len() - 1)); +                    } +                } +                Action::Never => (), +            } +        } +    } +} diff --git a/server/src/lib.rs b/server/src/lib.rs index 4659e440..5bb09e41 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -1,2 +1,4 @@  pub mod game;  pub mod protocol; +pub mod recipes; +pub mod interaction; diff --git a/server/src/main.rs b/server/src/main.rs index 7426e27e..441487e8 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,7 +1,7 @@  use anyhow::Result;  use futures_util::{SinkExt, StreamExt};  use log::{debug, info, warn}; -use std::{sync::Arc, time::Duration}; +use std::{fs::File, sync::Arc, time::Duration};  use tokio::{      io::{AsyncBufReadExt, AsyncWriteExt, BufReader},      net::TcpListener, @@ -13,6 +13,7 @@ use tokio_tungstenite::tungstenite::Message;  use undercooked::{      game::Game,      protocol::{PacketC, PacketS}, +    recipes::build_gamedata,  };  #[tokio::main] @@ -26,7 +27,10 @@ async fn main() -> Result<()> {      );      info!("listening for websockets on {}", ws_listener.local_addr()?); -    let game = Arc::new(RwLock::new(Game::new())); +    let data = +        build_gamedata(serde_yaml::from_reader(File::open("data/recipes.yaml").unwrap()).unwrap()); + +    let game = Arc::new(RwLock::new(Game::new(data.into())));      let (tx, rx) = broadcast::channel::<PacketC>(1024);      { diff --git a/server/src/protocol.rs b/server/src/protocol.rs index a318a9b2..ae87ffb4 100644 --- a/server/src/protocol.rs +++ b/server/src/protocol.rs @@ -1,15 +1,12 @@  use glam::{IVec2, Vec2};  use serde::{Deserialize, Serialize}; -pub type ID = u32; +use crate::recipes::Gamedata; -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(transparent)] -pub struct Item(pub String); - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(transparent)] -pub struct Tile(pub String); +pub type PlayerID = usize; +pub type ItemID = usize; +pub type ItemIndex = usize; +pub type TileIndex = usize;  #[derive(Debug, Clone, Serialize, Deserialize)]  #[serde(rename_all = "snake_case")] @@ -24,36 +21,37 @@ pub enum PacketS {  #[serde(rename_all = "snake_case")]  pub enum PacketC {      Joined { -        id: ID, +        data: Gamedata, +        id: PlayerID,      },      AddPlayer { -        id: ID, +        id: PlayerID,          name: String, -        hand: Option<(ID, Item)>, +        hand: Option<(ItemID, ItemIndex)>,      },      RemovePlayer { -        id: ID, +        id: PlayerID,      },      Position { -        player: ID, +        player: PlayerID,          pos: Vec2,          rot: f32,      },      TakeItem { -        item: ID, -        player: ID, +        item: ItemID, +        player: PlayerID,      },      PutItem { -        item: ID, +        item: ItemID,          pos: IVec2,      },      ProduceItem { -        id: ID, +        id: ItemID,          pos: IVec2, -        kind: Item, +        kind: ItemIndex,      },      ConsumeItem { -        id: ID, +        id: ItemID,          pos: IVec2,      },      SetActive { @@ -62,6 +60,6 @@ pub enum PacketC {      },      UpdateMap {          pos: IVec2, -        tile: Tile, +        tile: TileIndex,      },  } diff --git a/server/src/recipes.rs b/server/src/recipes.rs new file mode 100644 index 00000000..25a4be98 --- /dev/null +++ b/server/src/recipes.rs @@ -0,0 +1,73 @@ +use crate::protocol::{ItemIndex, TileIndex}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize, Clone, Copy)] +#[serde(rename_all = "snake_case")] +pub enum Action { +    Passive(f32), +    Active(f32), +    Instant, +    Never, +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Recipe<T = TileIndex, I = ItemIndex> { +    pub tile: T, +    #[serde(default)] +    pub inputs: Vec<I>, +    #[serde(default)] +    pub outputs: Vec<I>, +    pub action: Action, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Gamedata { +    pub recipes: Vec<Recipe>, +    pub item_names: Vec<String>, +    pub tile_names: Vec<String>, +} +pub fn build_gamedata(recipes_in: Vec<Recipe<String, String>>) -> Gamedata { +    let mut item_names = Vec::new(); +    let mut tile_names = Vec::new(); +    let mut recipes = Vec::new(); + +    for r in recipes_in { +        recipes.push(Recipe { +            action: r.action, +            tile: register(&mut tile_names, r.tile.clone()), +            inputs: r +                .inputs +                .clone() +                .into_iter() +                .map(|e| register(&mut item_names, e)) +                .collect(), +            outputs: r +                .outputs +                .clone() +                .into_iter() +                .map(|e| register(&mut item_names, e)) +                .collect(), +        }) +    } + +    Gamedata { +        recipes, +        item_names, +        tile_names, +    } +} +fn register(db: &mut Vec<String>, name: String) -> usize { +    if let Some(index) = db.iter().position(|e| e == &name) { +        index +    } else { +        let index = db.len(); +        db.push(name); +        index +    } +} + +impl Gamedata { +    pub fn get_tile(&self, name: &str) -> Option<TileIndex> { +        self.tile_names.iter().position(|t| t == name) +    } +} diff --git a/test-client/main.ts b/test-client/main.ts index 3e04dc05..5f4475f2 100644 --- a/test-client/main.ts +++ b/test-client/main.ts @@ -1,6 +1,6 @@  /// <reference lib="dom" /> -import { ID, Item, PacketC, PacketS, Tile } from "./protocol.ts"; +import { Gamedata, ItemID, ItemIndex, PacketC, PacketS, PlayerID, TileIndex } from "./protocol.ts";  import { FALLBACK_TILE, TILES } from "./tiles.ts";  import { V2, add_v2, ceil_v2, floor_v2, length, lerp_exp_v2_mut, normalize } from "./util.ts"; @@ -32,14 +32,16 @@ document.addEventListener("DOMContentLoaded", () => {      setInterval(tick_update, 1000 / 25);  }) -interface PlayerData { x: number; y: number, name: string, rot: number, hand?: ID, facing: V2 } -const players = new Map<number, PlayerData>() -interface ItemData { kind: Item, tile?: V2, player?: ID, tracking_player: boolean, x: number, y: number } -const items = new Map<number, ItemData>() -interface TileData { x: number; y: number, kind: Tile, items: ID[], active: boolean } +interface PlayerData { x: number; y: number, name: string, rot: number, hand?: ItemID, facing: V2 } +const players = new Map<PlayerID, PlayerData>() +interface ItemData { kind: ItemIndex, tile?: V2, player?: PlayerID, tracking_player: boolean, x: number, y: number } +const items = new Map<ItemID, ItemData>() +interface TileData { x: number; y: number, kind: TileIndex, items: ItemID[], active: boolean }  const tiles = new Map<string, TileData>() -let my_id: number = -1 +let data: Gamedata = { item_names: [], tile_names: [] } + +let my_id: PlayerID = -1  const camera: V2 = { x: 0, y: 0 }  const interact_target_anim: V2 = { x: 0, y: 0 }  let scale = 0 @@ -49,6 +51,7 @@ function packet(p: PacketC) {      if (!("position" in p)) console.log(p);      if ("joined" in p) {          my_id = p.joined.id +        data = p.joined.data      } else if ("add_player" in p) {          if (p.add_player.hand) items.set(p.add_player.hand[0], { kind: p.add_player.hand[1], player: p.add_player.id, tracking_player: true, x: 0, y: 0 })          players.set(p.add_player.id, { x: 0, y: 0, name: p.add_player.name, rot: 0, hand: p.add_player.hand?.[0], facing: { x: 0, y: 1 } }) @@ -188,7 +191,7 @@ function draw_ingame() {      for (const [_, tile] of tiles) {          ctx.save()          ctx.translate(tile.x, tile.y) -        const comps = TILES[tile.kind] ?? FALLBACK_TILE +        const comps = TILES[data.tile_names[tile.kind]] ?? FALLBACK_TILE          for (const c of comps) {              c(ctx)          } diff --git a/test-client/protocol.ts b/test-client/protocol.ts index 707c09a3..d5cb2034 100644 --- a/test-client/protocol.ts +++ b/test-client/protocol.ts @@ -1,7 +1,13 @@ -export type ID = number;  export type Vec2 = [number, number] -export type Item = string -export type Tile = string +export type PlayerID = number +export type ItemID = number +export type ItemIndex = number +export type TileIndex = number + +export interface Gamedata { +    item_names: string[], +    tile_names: string[], +}  export type PacketS =      { join: { name: string } } @@ -9,16 +15,15 @@ export type PacketS =      | { position: { pos: Vec2, rot: number } }      | { interact: { pos: Vec2, edge: boolean } } -  export type PacketC = -    { joined: { id: ID } } -    | { add_player: { id: ID, name: string, hand?: [number, Item] } } -    | { remove_player: { id: ID } } -    | { position: { player: ID, pos: Vec2, rot: number } } -    | { take_item: { item: ID, player: ID } } -    | { put_item: { item: ID, pos: Vec2 } } -    | { produce_item: { id: ID, pos: Vec2, kind: Item } } -    | { consume_item: { id: ID, pos: Vec2 } } +    { joined: { id: PlayerID, data: Gamedata } } +    | { add_player: { id: PlayerID, name: string, hand?: [ItemID, ItemIndex] } } +    | { remove_player: { id: PlayerID } } +    | { position: { player: PlayerID, pos: Vec2, rot: number } } +    | { take_item: { item: ItemID, player: PlayerID } } +    | { put_item: { item: ItemID, pos: Vec2 } } +    | { produce_item: { id: ItemID, pos: Vec2, kind: ItemIndex } } +    | { consume_item: { id: ItemID, pos: Vec2 } }      | { set_active: { tile: Vec2 } } -    | { update_map: { pos: Vec2, tile: Tile } } +    | { update_map: { pos: Vec2, tile: TileIndex } } | 
