diff options
Diffstat (limited to 'server/src/game.rs')
-rw-r--r-- | server/src/game.rs | 119 |
1 files changed, 82 insertions, 37 deletions
diff --git a/server/src/game.rs b/server/src/game.rs index 1c50c7c2..b3b23ce0 100644 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -20,16 +20,18 @@ use crate::{ data::Gamedata, entity::{Entity, EntityT}, interaction::{interact, tick_slot, InteractEffect, TickEffect}, + spatial_index::SpatialIndex, }; use anyhow::{anyhow, bail, Result}; use hurrycurry_protocol::{ glam::{IVec2, Vec2}, + movement::MovementBase, ClientGamedata, ItemIndex, ItemLocation, Message, PacketC, PacketS, PlayerID, RecipeIndex, TileIndex, }; use log::{info, warn}; use std::{ - collections::{HashMap, VecDeque}, + collections::{HashMap, HashSet, VecDeque}, sync::Arc, time::{Duration, Instant}, }; @@ -55,17 +57,22 @@ pub struct Tile { pub struct Player { pub name: String, pub character: i32, - pub position: Vec2, - pub last_position_ts: Instant, pub interacting: Option<IVec2>, pub item: Option<Item>, pub communicate_persist: Option<Message>, + + movement: MovementBase, + direction: Vec2, + boost: bool, + last_position_update: Instant, } pub struct Game { pub data: Arc<Gamedata>, tiles: HashMap<IVec2, Tile>, + walkable: HashSet<IVec2>, pub players: HashMap<PlayerID, Player>, + players_spatial_index: SpatialIndex<PlayerID>, packet_out: VecDeque<PacketC>, demand: Option<DemandState>, pub points: i64, @@ -80,9 +87,11 @@ impl Game { packet_out: Default::default(), players: HashMap::new(), tiles: HashMap::new(), + walkable: HashSet::new(), demand: None, end: None, entities: vec![], + players_spatial_index: SpatialIndex::default(), points: 0, } } @@ -130,15 +139,27 @@ impl Game { }), }, ); + if !self.data.tile_collide[tile.0] { + self.walkable.insert(p); + } } for (id, (name, character)) in players { self.players.insert( id, Player { item: None, - last_position_ts: Instant::now(), character, - position: self.data.chef_spawn, + movement: MovementBase { + position: self.data.chef_spawn, + facing: Vec2::X, + rotation: 0., + velocity: Vec2::ZERO, + boosting: false, + stamina: 0., + }, + last_position_update: Instant::now(), + boost: false, + direction: Vec2::ZERO, communicate_persist: None, interacting: None, name: name.clone(), @@ -189,7 +210,7 @@ impl Game { for (&id, player) in &self.players { out.push(PacketC::AddPlayer { id, - position: player.position, + position: player.movement.position, character: player.character, name: player.name.clone(), }); @@ -262,9 +283,18 @@ impl Game { player, Player { item: None, - last_position_ts: Instant::now(), character, - position, + movement: MovementBase { + position: self.data.chef_spawn, + facing: Vec2::X, + rotation: 0., + velocity: Vec2::ZERO, + boosting: false, + stamina: 0., + }, + last_position_update: Instant::now(), + boost: false, + direction: Vec2::ZERO, communicate_persist: None, interacting: None, name: name.clone(), @@ -282,8 +312,11 @@ impl Game { .players .remove(&player) .ok_or(anyhow!("player does not exist"))?; + + self.players_spatial_index.remove_entry(player); + if let Some(item) = p.item { - let pos = p.position.floor().as_ivec2(); + let pos = p.movement.position.floor().as_ivec2(); if let Some(tile) = self.tiles.get_mut(&pos) { if tile.item.is_none() { self.packet_out.push_back(PacketC::SetItem { @@ -297,37 +330,25 @@ impl Game { self.packet_out .push_back(PacketC::RemovePlayer { id: player }) } - PacketS::Position { pos, rot, boosting } => { - let pid = player; + PacketS::Movement { + pos, + boosting, + direction, + } => { let player = self .players .get_mut(&player) .ok_or(anyhow!("player does not exist"))?; - // let dt = player.last_position_ts.elapsed().as_secs_f32(); - // let dist = pos.distance(player.position); - // let speed = dist / dt; - // let interact_dist = player - // .interacting - // .map(|p| (p.as_vec2() + Vec2::splat(0.5)).distance(player.position)) - // .unwrap_or_default(); - // let movement_ok = speed < PLAYER_SPEED_LIMIT && dist < 1. && interact_dist < 2.; - // if movement_ok { - player.position = pos; - player.last_position_ts = Instant::now(); - // } - self.packet_out.push_back(PacketC::Position { - player: pid, - pos: player.position, - rot, - boosting, - }); - // if !movement_ok { - // bail!( - // "{:?} moved to quickly. speed={speed:.02} dist={dist:.02}", - // player.name - // ) - // } + player.direction = direction; + player.boost = boosting; + + if let Some(pos) = pos { + let dt = player.last_position_update.elapsed(); + player.last_position_update += dt; + player.movement.position += + (pos - player.movement.position).clamp_length_max(dt.as_secs_f32()); + } } PacketS::Collide { player, force } => { self.packet_out @@ -348,7 +369,7 @@ impl Game { }; let entpos = pos.as_vec2() + Vec2::splat(0.5); - if edge && entpos.distance(player.position) > 2. { + if edge && entpos.distance(player.movement.position) > 2. { bail!("interacting too far from player"); } @@ -364,7 +385,7 @@ impl Game { let other_pid = if !self.data.is_tile_interactable(tile.kind) { self.players .iter() - .find(|(id, p)| **id != pid && p.position.distance(entpos) < 0.7) + .find(|(id, p)| **id != pid && p.movement.position.distance(entpos) < 0.7) .map(|(&id, _)| id) } else { None @@ -497,6 +518,30 @@ impl Game { } for (&pid, player) in &mut self.players { + player + .movement + .update(&self.walkable, player.direction, player.boost, dt); + + self.players_spatial_index + .update_entry(pid, player.movement.position); + } + + self.players_spatial_index.all(|p1, pos1| { + self.players_spatial_index.query(pos1, 2., |p2, _pos2| { + if let Some([a, b]) = self.players.get_many_mut([&p1, &p2]) { + a.movement.collide(&mut b.movement, dt) + } + }) + }); + + for (&pid, player) in &mut self.players { + self.packet_out.push_back(PacketC::Position { + player: pid, + pos: player.movement.position, + boosting: player.movement.boosting, + rot: player.movement.rotation, + }); + if let Some(effect) = tick_slot(dt, &self.data, None, &mut player.item) { match effect { TickEffect::Progress(warn) => self.packet_out.push_back(PacketC::SetProgress { |