summaryrefslogtreecommitdiff
path: root/server/editor/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server/editor/src/main.rs')
-rw-r--r--server/editor/src/main.rs196
1 files changed, 148 insertions, 48 deletions
diff --git a/server/editor/src/main.rs b/server/editor/src/main.rs
index c5b6f61d..2d37859b 100644
--- a/server/editor/src/main.rs
+++ b/server/editor/src/main.rs
@@ -10,10 +10,10 @@ use hurrycurry_protocol::{
movement::MovementBase,
};
use log::{debug, info, warn};
-use save::export_state;
+use save::{export_state, import_state};
use std::{
collections::{HashMap, HashSet},
- fs::File,
+ fs::{File, read_to_string},
io::Write,
net::SocketAddr,
thread::{sleep, spawn},
@@ -33,7 +33,16 @@ struct Args {
#[clap(multicall = true)]
pub enum Command {
Play,
- Save,
+ Save {
+ name: Option<String>,
+ },
+ Load {
+ name: Option<String>,
+ },
+ Spawn {
+ #[arg(short, long)]
+ customer: bool,
+ },
}
#[tokio::main]
@@ -41,9 +50,10 @@ async fn main() -> Result<()> {
env_logger::init_from_env("LOG");
let args = Args::parse();
let ws_listener = TcpListener::bind((args.bind_addr, args.port)).await?;
+ let mut ms = None;
loop {
let (sock, addr) = ws_listener.accept().await?;
- if let Err(e) = handle_conn(sock, addr).await {
+ if let Err(e) = handle_conn(sock, addr, &mut ms).await {
warn!("client error: {e}");
}
}
@@ -58,12 +68,39 @@ const TILES: &[(&str, char, u8)] = &[
("cuttingboard", 'f', 0),
("chair", 'g', 1),
("table", 'h', 0),
- ("wall", 'h', 2),
- ("counter-window", 'h', 0),
+ ("wall", 'i', 2),
+ ("tree", 'j', 2),
+ ("counter-window", 'k', 0),
+ ("tomato-crate", 'l', 0),
+ ("leek-crate", 'm', 0),
+ ("lettuce-crate", 'n', 0),
+ ("cheese-crate", 'o', 0),
+ ("potato-crate", 'p', 0),
+ ("chili-crate", 'q', 0),
+ ("fish-crate", 'r', 0),
+ ("steak-crate", 's', 0),
+ ("flour-crate", 't', 0),
+ ("coconut-crate", 'u', 0),
+ ("strawberry-crate", 'v', 0),
+ ("rice-crate", 'w', 0),
+ ("wall-window", 'x', 0),
+ ("freezer", 'y', 0),
+ ("trash", 'z', 0),
+ ("sink", 'A', 0),
+ ("street", 'B', 1),
+ ("conveyor", 'C', 0),
+ ("lamp", 'D', 1),
+ ("fence", 'E', 2),
+ ("door", 'F', 1),
+ ("path", 'G', 1),
];
#[allow(unused_assignments)]
-async fn handle_conn(sock: TcpStream, addr: SocketAddr) -> Result<()> {
+async fn handle_conn(
+ sock: TcpStream,
+ addr: SocketAddr,
+ mapname_save: &mut Option<String>,
+) -> Result<()> {
let sock = tokio_tungstenite::accept_async(sock).await?;
info!("{addr} connected via websocket");
@@ -77,6 +114,8 @@ async fn handle_conn(sock: TcpStream, addr: SocketAddr) -> Result<()> {
tile: TileIndex(0),
chef_spawn: ivec2(0, 0),
customer_spawn: ivec2(0, 0),
+ mapname: "editor".to_string(),
+ dirty_tiles: HashSet::new(),
};
state.out.push(PacketC::Version {
@@ -98,7 +137,13 @@ async fn handle_conn(sock: TcpStream, addr: SocketAddr) -> Result<()> {
lobby: false, // very ironic
});
- state.build_start_platform();
+ if let Some(name) = mapname_save.clone() {
+ state.load(&name)?;
+ state.spawn(false);
+ } else {
+ state.build_start_platform();
+ }
+ state.flush();
let (mut write, mut read) = sock.split();
@@ -136,6 +181,8 @@ async fn handle_conn(sock: TcpStream, addr: SocketAddr) -> Result<()> {
error: true,
});
}
+ state.flush();
+ *mapname_save = Some(state.mapname.clone());
}
Ok(())
}
@@ -151,46 +198,59 @@ pub struct State {
customer_spawn: IVec2,
chef_spawn: IVec2,
+
+ dirty_tiles: HashSet<IVec2>,
+
+ mapname: String,
}
impl State {
pub fn set_tile(&mut self, pos: IVec2, tile: TileIndex) {
self.tiles.insert(pos, tile);
self.walkable.insert(pos);
- self.out.push(PacketC::UpdateMap {
- tile: pos,
- kind: Some(tile),
- neighbors: [
- self.tiles.get(&(pos + IVec2::NEG_Y)).copied(),
- self.tiles.get(&(pos + IVec2::NEG_X)).copied(),
- self.tiles.get(&(pos + IVec2::Y)).copied(),
- self.tiles.get(&(pos + IVec2::X)).copied(),
- ],
- })
+ self.dirty_tiles.insert(pos);
+ }
+ pub fn clear(&mut self) {
+ self.dirty_tiles.extend(self.tiles.keys());
+ self.tiles.clear();
}
pub fn flush(&mut self) {
- // send every existing tile once move because client does not remember
- // TODO remove when client is fixed
- for (tile, _) in &self.tiles {
- self.out.push(PacketC::UpdateMap {
- tile: *tile,
- kind: None,
- neighbors: [None; 4],
- })
- }
- for (tile, kind) in &self.tiles {
- self.out.push(PacketC::UpdateMap {
- tile: *tile,
- kind: Some(*kind),
- neighbors: [
- self.tiles.get(&(tile + IVec2::NEG_Y)).copied(),
- self.tiles.get(&(tile + IVec2::NEG_X)).copied(),
- self.tiles.get(&(tile + IVec2::Y)).copied(),
- self.tiles.get(&(tile + IVec2::X)).copied(),
- ],
- })
+ if !self.dirty_tiles.is_empty() {
+ for p in self.dirty_tiles.drain() {
+ self.out.push(PacketC::UpdateMap {
+ tile: p,
+ kind: self.tiles.get(&p).copied(),
+ neighbors: [
+ self.tiles.get(&(p + IVec2::NEG_Y)).copied(),
+ self.tiles.get(&(p + IVec2::NEG_X)).copied(),
+ self.tiles.get(&(p + IVec2::Y)).copied(),
+ self.tiles.get(&(p + IVec2::X)).copied(),
+ ],
+ })
+ }
+ // send every existing tile once move because client does not remember
+ // TODO remove when client is fixed
+ for (tile, _) in &self.tiles {
+ self.out.push(PacketC::UpdateMap {
+ tile: *tile,
+ kind: None,
+ neighbors: [None; 4],
+ })
+ }
+ for (tile, kind) in &self.tiles {
+ self.out.push(PacketC::UpdateMap {
+ tile: *tile,
+ kind: Some(*kind),
+ neighbors: [
+ self.tiles.get(&(tile + IVec2::NEG_Y)).copied(),
+ self.tiles.get(&(tile + IVec2::NEG_X)).copied(),
+ self.tiles.get(&(tile + IVec2::Y)).copied(),
+ self.tiles.get(&(tile + IVec2::X)).copied(),
+ ],
+ })
+ }
+ self.out.push(PacketC::FlushMap);
}
- self.out.push(PacketC::FlushMap);
}
pub fn build_start_platform(&mut self) {
for x in 0..10 {
@@ -198,7 +258,6 @@ impl State {
self.set_tile(ivec2(x, y), TileIndex(1));
}
}
- self.flush();
}
pub fn handle_packet(&mut self, packet: PacketS) -> Result<()> {
@@ -277,23 +336,56 @@ impl State {
}
Ok(())
}
+ pub fn spawn(&mut self, c: bool) {
+ self.movement.position = if c {
+ self.customer_spawn
+ } else {
+ self.chef_spawn
+ }
+ .as_vec2();
+ self.out.push(PacketC::Movement {
+ player: PlayerID(0),
+ pos: self.movement.position,
+ rot: 0.,
+ dir: Vec2::X,
+ boost: false,
+ });
+ self.out.push(PacketC::MovementSync {
+ player: PlayerID(0),
+ });
+ }
- pub fn save(&mut self) -> Result<()> {
+ pub fn save(&mut self, name: &str) -> Result<()> {
let e = export_state(&self);
- File::create("data/maps/editor.yaml")?.write_all(e.as_bytes())?;
+ File::create(format!("data/maps/{name}.yaml"))?.write_all(e.as_bytes())?;
self.out.push(PacketC::ServerMessage {
message: Message::Text(format!("Map saved.")),
error: false,
});
+ self.mapname = name.to_owned();
+ Ok(())
+ }
+ pub fn load(&mut self, name: &str) -> Result<()> {
+ let e = read_to_string(format!("data/maps/{name}.yaml"))?;
+ self.clear();
+ import_state(self, &e)?;
+ self.out.push(PacketC::ServerMessage {
+ message: Message::Text(format!("Map loaded.")),
+ error: false,
+ });
+ self.mapname = name.to_owned();
Ok(())
}
pub fn handle_command(&mut self, command: Command) -> Result<()> {
match command {
Command::Play => {
- self.save()?;
+ self.save(&self.mapname.clone())?;
+ let mapname = self.mapname.clone();
spawn(move || {
- if let Err(e) = start_map_bot("ws://127.0.0.1:27032", "editor") {
+ if let Err(e) =
+ start_map_bot("ws://127.0.0.1:27032", "ws://127.0.0.1:27035", &mapname)
+ {
warn!("editor bot: {e}")
}
});
@@ -301,15 +393,21 @@ impl State {
uri: vec!["ws://127.0.0.1:27032".to_string()],
});
}
- Command::Save => {
- self.save()?;
+ Command::Save { name } => {
+ self.save(name.as_ref().unwrap_or(&self.mapname.clone()))?;
+ }
+ Command::Load { name } => {
+ self.load(name.as_ref().unwrap_or(&self.mapname.clone()))?;
+ }
+ Command::Spawn { customer } => {
+ self.spawn(customer);
}
}
Ok(())
}
}
-fn start_map_bot(address: &str, mapname: &str) -> Result<()> {
+fn start_map_bot(address: &str, own_addr: &str, mapname: &str) -> Result<()> {
let mut network = Network::connect(address)?;
network.queue_out.push_back(PacketS::Join {
@@ -330,7 +428,9 @@ fn start_map_bot(address: &str, mapname: &str) -> Result<()> {
PacketC::Joined { id } => {
network.queue_out.push_back(PacketS::Communicate {
player: *id,
- message: Some(Message::Text(format!("/start {mapname}"))),
+ message: Some(Message::Text(format!(
+ "/set-editor-address {own_addr}\n/start {mapname}"
+ ))),
timeout: None,
pin: None,
});