summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-08-11 01:30:59 +0200
committermetamuffin <metamuffin@disroot.org>2024-08-11 01:35:05 +0200
commit218da36b4f963a5dfcd619d543cb95c5c196d214 (patch)
tree244ecd10f4ca3d9798cedf7149394a1d4827b5d9
parentf753629acd906ff7acd42a18a266cc93a883330c (diff)
downloadhurrycurry-218da36b4f963a5dfcd619d543cb95c5c196d214.tar
hurrycurry-218da36b4f963a5dfcd619d543cb95c5c196d214.tar.bz2
hurrycurry-218da36b4f963a5dfcd619d543cb95c5c196d214.tar.zst
update movement in protocol and all clients
-rw-r--r--Cargo.lock2
-rw-r--r--client/game.gd4
-rw-r--r--client/multiplayer.gd21
-rw-r--r--pixel-client/src/game.rs17
-rw-r--r--server/bot/Cargo.toml2
-rw-r--r--server/bot/src/main.rs59
-rw-r--r--server/client-lib/src/lib.rs44
-rw-r--r--server/client-lib/src/network/sync.rs14
-rw-r--r--server/protocol/src/lib.rs12
-rw-r--r--server/protocol/src/movement.rs19
-rw-r--r--server/src/entity/customers/mod.rs16
-rw-r--r--server/src/game.rs28
-rw-r--r--server/src/state.rs2
-rw-r--r--test-client/main.ts6
-rw-r--r--test-client/protocol.ts6
15 files changed, 179 insertions, 73 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 907c8f29..1e6093d9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -280,7 +280,9 @@ dependencies = [
name = "bot"
version = "0.1.0"
dependencies = [
+ "anyhow",
"hurrycurry-client-lib",
+ "hurrycurry-protocol",
"log",
]
diff --git a/client/game.gd b/client/game.gd
index 07e8aa3b..d3d8ab13 100644
--- a/client/game.gd
+++ b/client/game.gd
@@ -108,9 +108,9 @@ func _ready():
mp.set_tile.connect(set_tile)
mp.remove_tile.connect(func (pos): map.clear_tile(pos))
- mp.position.connect(func(player: int, pos: Vector2, rot: float, boosting: bool):
+ mp.movement.connect(func(player: int, pos: Vector2, rot: float, boost: bool, dir: Vector2):
var player_instance: Player = players[player]
- player_instance.update_position(pos, rot, boosting)
+ player_instance.update_position(pos, rot, boost)
if player == player_id:
last_position = pos
)
diff --git a/client/multiplayer.gd b/client/multiplayer.gd
index 7a63059e..095d3d7b 100644
--- a/client/multiplayer.gd
+++ b/client/multiplayer.gd
@@ -34,7 +34,7 @@ signal item_message(player: int, item: int, persist: bool)
signal effect_message(player: int, effect: String, persist: bool)
signal add_player(player: int, name: String, pos: Vector2, character: int)
signal remove_player(player: int)
-signal position(player: int, position: Vector2, rotation: float, boosting: bool)
+signal movement(player: int, position: Vector2, rotation: float, boost: bool, direction: Vector2)
signal movement_sync()
signal take_item(tile: Vector2i, player: int)
signal put_item(player: int, tile: Vector2i, )
@@ -57,7 +57,7 @@ signal connection_closed(reason: String)
signal show_rating(stars: int, points: int)
signal show_book() # TODO: Connect this
-const VERSION_MAJOR: int = 3
+const VERSION_MAJOR: int = 4
const VERSION_MINOR: int = 0
var connected := false
@@ -86,7 +86,7 @@ func _process(_delta):
var code = socket.get_close_code()
var reason = socket.get_close_reason()
connection_closed.emit(
- tr("WebSocket closed with code: %d, reason %s. Clean: %s") % [code, reason, code != - 1]
+ tr("WebSocket closed with code: %d, reason %s. Clean: %s") % [code, reason, code != -1]
)
self.queue_free()
@@ -124,12 +124,13 @@ func handle_packet(bytes: PackedByteArray):
"remove_player":
var id = decoded["id"]
remove_player.emit(id)
- "position":
+ "movement":
var player = decoded["player"]
var pos = decoded["pos"]
var rot = decoded["rot"]
- var boosting = decoded["boosting"]
- position.emit(player, pos_to_vec2(pos), rot, boosting)
+ var boost = decoded["boost"]
+ var dir = decoded["dir"]
+ movement.emit(player, pos_to_vec2(pos), rot, boost, pos_to_vec2(dir))
"movement_sync":
movement_sync.emit()
"take_item":
@@ -279,16 +280,16 @@ func send_join(player_name: String, character: int):
"character": character
})
-func send_movement(player: int, pos: Vector2, direction: Vector2, boosting: bool):
+func send_movement(player: int, pos: Vector2, direction: Vector2, boost: bool):
send_packet({
"type": "movement",
"player": player,
"pos": [pos.x, pos.y],
- "direction": [direction.x, direction.y],
- "boosting": boosting
+ "dir": [direction.x, direction.y],
+ "boost": boost
})
-func send_tile_interact(player:int, pos: Vector2i, edge: bool):
+func send_tile_interact(player: int, pos: Vector2i, edge: bool):
send_packet({
"type": "interact",
"player": player,
diff --git a/pixel-client/src/game.rs b/pixel-client/src/game.rs
index d4f1e4e4..5a06ad30 100644
--- a/pixel-client/src/game.rs
+++ b/pixel-client/src/game.rs
@@ -113,7 +113,7 @@ impl Game {
keyboard: &KeyboardState,
layout: &AtlasLayout,
) -> Option<Box<State>> {
- self.network.poll();
+ self.network.poll().unwrap();
// TODO perf
for packet in self.network.queue_in.drain(..).collect::<Vec<_>>() {
@@ -160,9 +160,8 @@ impl Game {
}
if let Some(player) = self.players.get_mut(&self.my_id) {
- player
- .movement
- .update(&self.collision_map, direction, boost, dt);
+ player.movement.input(direction, boost);
+ player.movement.update(&self.collision_map, dt);
if send_movement {
self.network
@@ -271,7 +270,8 @@ impl Game {
item: None,
movement: MovementBase {
position,
- direction: Vec2::ZERO,
+ input_direction: Vec2::ZERO,
+ input_boost: false,
facing: Vec2::X,
rotation: 0.,
velocity: Vec2::ZERO,
@@ -287,17 +287,18 @@ impl Game {
self.player_ids.remove(&id);
self.players.remove(&id);
}
- PacketC::Position {
+ PacketC::Movement {
player,
pos,
rot,
- boosting,
+ boost,
+ dir,
} => {
if player != self.my_id {
if let Some(p) = self.players.get_mut(&player) {
p.movement.position = pos;
p.movement.rotation = rot;
- p.movement.boosting = boosting;
+ p.movement.input(dir, boost);
}
}
}
diff --git a/server/bot/Cargo.toml b/server/bot/Cargo.toml
index 157472ae..cd0ac383 100644
--- a/server/bot/Cargo.toml
+++ b/server/bot/Cargo.toml
@@ -5,4 +5,6 @@ edition = "2021"
[dependencies]
hurrycurry-client-lib = { path = "../client-lib", features = ["tokio-network"] }
+hurrycurry-protocol = { path = "../protocol" }
log = "0.4.22"
+anyhow = "1.0.86"
diff --git a/server/bot/src/main.rs b/server/bot/src/main.rs
index 0b625703..61cef1f9 100644
--- a/server/bot/src/main.rs
+++ b/server/bot/src/main.rs
@@ -16,6 +16,59 @@
*/
-fn main() {
-
-} \ No newline at end of file
+use std::{thread::sleep, time::Duration};
+
+use anyhow::Result;
+use hurrycurry_client_lib::{network::sync::Network, Game};
+use hurrycurry_protocol::{glam::Vec2, PacketC, PacketS, PlayerID};
+
+fn main() -> Result<()> {
+ let mut network = Network::connect("ws://127.0.0.1")?;
+
+ let mut game = Game::default();
+
+ network.queue_out.push_back(PacketS::Join {
+ name: "bot".to_string(),
+ character: 1,
+ });
+
+ let mut bots = Vec::new();
+
+ loop {
+ let dt = 1. / 50.;
+
+ network.poll()?;
+
+ while let Some(packet) = network.queue_in.pop_front() {
+ match &packet {
+ PacketC::Joined { id } => bots.push(Bot::new(*id)),
+ _ => (),
+ }
+ game.apply_packet(packet);
+ }
+
+ for b in &bots {
+ network.queue_out.push_back(PacketS::Movement {
+ player: b.id,
+ dir: Vec2::ONE,
+ boost: true,
+ pos: None,
+ });
+ }
+
+ sleep(Duration::from_secs_f32(dt));
+ }
+}
+
+pub struct Bot {
+ id: PlayerID,
+}
+
+impl Bot {
+ pub fn new(id: PlayerID) -> Self {
+ Self { id }
+ }
+ pub fn tick(&self, game: &Game) {
+ if let Some(player) = game.players.get(&self.id) {}
+ }
+}
diff --git a/server/client-lib/src/lib.rs b/server/client-lib/src/lib.rs
index 649e71b2..d949ac6e 100644
--- a/server/client-lib/src/lib.rs
+++ b/server/client-lib/src/lib.rs
@@ -15,6 +15,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+#![feature(map_many_mut)]
pub mod network;
pub mod spatial_index;
@@ -63,6 +64,22 @@ pub struct Game {
pub score: Score,
}
+impl Default for Game {
+ fn default() -> Self {
+ Self {
+ data: Default::default(),
+ tiles: HashMap::new(),
+ walkable: HashSet::new(),
+ players: HashMap::new(),
+ players_spatial_index: SpatialIndex::default(),
+ end: None,
+ lobby: false,
+ environment_effects: HashSet::new(),
+ score: Score::default(),
+ }
+ }
+}
+
impl Game {
pub fn apply_packet(&mut self, packet: PacketC) {
match packet {
@@ -90,16 +107,17 @@ impl Game {
PacketC::RemovePlayer { id } => {
self.players.remove(&id);
}
- PacketC::Position {
+ PacketC::Movement {
player,
pos,
rot,
- boosting,
+ boost,
+ dir,
} => {
if let Some(p) = self.players.get_mut(&player) {
+ p.movement.input(dir, boost);
p.movement.position = pos;
p.movement.rotation = rot;
- p.movement.boosting = boosting;
}
}
@@ -159,6 +177,26 @@ impl Game {
_ => (),
}
}
+
+ pub fn tick(&mut self, dt: f32) {
+ self.score.time_remaining -= dt as f64;
+ self.score.time_remaining -= self.score.time_remaining.max(0.);
+
+ for (&pid, player) in &mut self.players {
+ player.movement.update(&self.walkable, 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)
+ }
+ })
+ });
+ }
+
pub fn get_item(&mut self, location: ItemLocation) -> &mut Option<Item> {
match location {
ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item,
diff --git a/server/client-lib/src/network/sync.rs b/server/client-lib/src/network/sync.rs
index e8aa08de..3a475e01 100644
--- a/server/client-lib/src/network/sync.rs
+++ b/server/client-lib/src/network/sync.rs
@@ -80,7 +80,7 @@ impl Network {
})
}
- pub fn poll(&mut self) {
+ pub fn poll(&mut self) -> anyhow::Result<()> {
loop {
self.queue_in.extend(match self.sock.read() {
Ok(Message::Text(packet)) => match serde_json::from_str(&packet) {
@@ -131,18 +131,16 @@ impl Network {
for packet in self.queue_out.drain(..) {
debug!("-> {packet:?}");
if self.use_bincode {
- self.sock
- .write(Message::Binary(
- bincode::encode_to_vec(&packet, BINCODE_CONFIG).unwrap(),
- ))
- .unwrap();
+ self.sock.write(Message::Binary(
+ bincode::encode_to_vec(&packet, BINCODE_CONFIG).unwrap(),
+ ))?;
} else {
self.sock
- .write(Message::Text(serde_json::to_string(&packet).unwrap()))
- .unwrap();
+ .write(Message::Text(serde_json::to_string(&packet).unwrap()))?;
}
}
self.sock.flush().unwrap();
+ Ok(())
}
}
diff --git a/server/protocol/src/lib.rs b/server/protocol/src/lib.rs
index 1a4e41a9..8f8e9784 100644
--- a/server/protocol/src/lib.rs
+++ b/server/protocol/src/lib.rs
@@ -30,7 +30,7 @@ pub use glam;
pub mod movement;
-pub const VERSION: (u32, u32) = (3, 0);
+pub const VERSION: (u32, u32) = (4, 0);
pub const BINCODE_CONFIG: Configuration<LittleEndian, Varint, Limit<4096>> =
standard().with_limit();
@@ -94,8 +94,8 @@ pub enum PacketS {
Movement {
player: PlayerID,
#[bincode(with_serde)]
- direction: Vec2,
- boosting: bool,
+ dir: Vec2,
+ boost: bool,
#[bincode(with_serde)]
pos: Option<Vec2>,
},
@@ -155,12 +155,14 @@ pub enum PacketC {
RemovePlayer {
id: PlayerID,
},
- Position {
+ Movement {
player: PlayerID,
#[bincode(with_serde)]
pos: Vec2,
rot: f32,
- boosting: bool,
+ #[bincode(with_serde)]
+ dir: Vec2,
+ boost: bool,
},
MoveItem {
from: ItemLocation,
diff --git a/server/protocol/src/movement.rs b/server/protocol/src/movement.rs
index dad9d300..ebcc627d 100644
--- a/server/protocol/src/movement.rs
+++ b/server/protocol/src/movement.rs
@@ -29,7 +29,8 @@ const BOOST_DURATION: f32 = 0.3;
const BOOST_RESTORE: f32 = 0.5;
pub struct MovementBase {
- pub direction: Vec2,
+ pub input_direction: Vec2,
+ pub input_boost: bool,
pub position: Vec2,
pub facing: Vec2,
pub rotation: f32,
@@ -41,8 +42,9 @@ pub struct MovementBase {
impl MovementBase {
pub fn new(position: Vec2) -> Self {
Self {
+ input_direction: Vec2::ZERO,
+ input_boost: false,
position,
- direction: Vec2::ZERO,
facing: Vec2::X,
velocity: Vec2::ZERO,
boosting: false,
@@ -50,8 +52,13 @@ impl MovementBase {
rotation: 0.,
}
}
- pub fn update(&mut self, map: &HashSet<IVec2>, direction: Vec2, mut boost: bool, dt: f32) {
- self.direction = direction.clamp_length_max(1.);
+ pub fn input(&mut self, direction: Vec2, boost: bool) {
+ self.input_boost = boost;
+ self.input_direction = direction;
+ }
+ pub fn update(&mut self, map: &HashSet<IVec2>, dt: f32) {
+ let mut boost = self.input_boost;
+ let direction = self.input_direction.clamp_length_max(1.);
if direction.length() > 0.1 {
self.facing = direction + (self.facing - direction) * (-dt * 10.).exp();
}
@@ -74,8 +81,8 @@ impl MovementBase {
pub fn movement_packet(&self, player: PlayerID) -> PacketS {
PacketS::Movement {
pos: Some(self.position),
- boosting: self.boosting,
- direction: self.direction,
+ boost: self.input_boost,
+ dir: self.input_direction,
player,
}
}
diff --git a/server/src/entity/customers/mod.rs b/server/src/entity/customers/mod.rs
index 0bb3f918..97b94942 100644
--- a/server/src/entity/customers/mod.rs
+++ b/server/src/entity/customers/mod.rs
@@ -102,7 +102,9 @@ impl EntityT for Customers {
match state {
CustomerState::Entering { path, chair } => {
- playerdata.direction = path.next_direction(playerdata.position());
+ playerdata
+ .movement
+ .input(path.next_direction(playerdata.position()), false);
if path.is_done() {
let demand = DemandIndex(random::<usize>() % self.demands.len());
self.cpackets.push_back(PacketS::Communicate {
@@ -123,7 +125,9 @@ impl EntityT for Customers {
demand,
timeout,
} => {
- playerdata.direction = (chair.as_vec2() + 0.5) - playerdata.position();
+ playerdata
+ .movement
+ .input((chair.as_vec2() + 0.5) - playerdata.position(), false);
*timeout -= dt;
if *timeout <= 0. {
self.cpackets.push_back(PacketS::Communicate {
@@ -203,7 +207,9 @@ impl EntityT for Customers {
progress,
chair,
} => {
- playerdata.direction = (chair.as_vec2() + 0.5) - playerdata.position();
+ playerdata
+ .movement
+ .input((chair.as_vec2() + 0.5) - playerdata.position(), false);
let demand = &self.demands[demand.0];
*progress += dt / demand.duration;
if *progress >= 1. {
@@ -234,7 +240,9 @@ impl EntityT for Customers {
}
}
CustomerState::Exiting { path } => {
- playerdata.direction = path.next_direction(playerdata.position());
+ playerdata
+ .movement
+ .input(path.next_direction(playerdata.position()), false);
if path.is_done() {
info!("{player:?} -> leave");
self.cpackets.push_back(PacketS::Leave { player });
diff --git a/server/src/game.rs b/server/src/game.rs
index c010c8b8..5af9658e 100644
--- a/server/src/game.rs
+++ b/server/src/game.rs
@@ -61,8 +61,6 @@ pub struct Player {
pub communicate_persist: Option<Message>,
pub movement: MovementBase,
- pub direction: Vec2,
- pub boost: bool,
pub last_position_update: Instant,
}
@@ -178,7 +176,8 @@ impl Game {
} else {
self.data.chef_spawn
},
- direction: Vec2::ZERO,
+ input_direction: Vec2::ZERO,
+ input_boost: false,
facing: Vec2::X,
rotation: 0.,
velocity: Vec2::ZERO,
@@ -186,8 +185,6 @@ impl Game {
stamina: 0.,
},
last_position_update: Instant::now(),
- boost: false,
- direction: Vec2::ZERO,
communicate_persist: None,
interacting: None,
name: name.clone(),
@@ -298,7 +295,8 @@ impl Game {
} else {
self.data.chef_spawn
},
- direction: Vec2::ZERO,
+ input_direction: Vec2::ZERO,
+ input_boost: false,
facing: Vec2::X,
rotation: 0.,
velocity: Vec2::ZERO,
@@ -306,8 +304,6 @@ impl Game {
stamina: 0.,
},
last_position_update: Instant::now(),
- boost: false,
- direction: Vec2::ZERO,
communicate_persist: None,
interacting: None,
name: name.clone(),
@@ -358,8 +354,8 @@ impl Game {
}
PacketS::Movement {
pos,
- boosting,
- direction,
+ boost,
+ dir: direction,
player,
} => {
let pd = self
@@ -367,8 +363,7 @@ impl Game {
.get_mut(&player)
.ok_or(anyhow!("player does not exist"))?;
- pd.direction = direction;
- pd.boost = boosting;
+ pd.movement.input(direction, boost);
if let Some(pos) = pos {
let dt = pd.last_position_update.elapsed();
@@ -541,9 +536,7 @@ impl Game {
}
for (&pid, player) in &mut self.players {
- player
- .movement
- .update(&self.walkable, player.direction, player.boost, dt);
+ player.movement.update(&self.walkable, dt);
self.players_spatial_index
.update_entry(pid, player.movement.position);
@@ -558,10 +551,11 @@ impl Game {
});
for (&pid, player) in &mut self.players {
- packet_out.push_back(PacketC::Position {
+ packet_out.push_back(PacketC::Movement {
player: pid,
pos: player.movement.position,
- boosting: player.movement.boosting,
+ dir: player.movement.input_direction,
+ boost: player.movement.boosting,
rot: player.movement.rotation,
});
diff --git a/server/src/state.rs b/server/src/state.rs
index 43ca29bd..526f70aa 100644
--- a/server/src/state.rs
+++ b/server/src/state.rs
@@ -106,7 +106,7 @@ impl State {
);
}
while let Some(p) = self.packet_out.pop_front() {
- if matches!(p, PacketC::UpdateMap { .. } | PacketC::Position { .. }) {
+ if matches!(p, PacketC::UpdateMap { .. } | PacketC::Movement { .. }) {
trace!("-> {p:?}");
} else {
debug!("-> {p:?}");
diff --git a/test-client/main.ts b/test-client/main.ts
index f0f481b4..00699b60 100644
--- a/test-client/main.ts
+++ b/test-client/main.ts
@@ -156,7 +156,7 @@ function packet(p: PacketC) {
case "remove_player":
players.delete(p.id)
break;
- case "position": {
+ case "movement": {
const pl = players.get(p.player)!
const pos = { x: p.pos[0], y: p.pos[1] }
// const dist = length(sub_v2(pl.position, pos));a
@@ -165,7 +165,7 @@ function packet(p: PacketC) {
if (p.player == my_id) return last_server_sent_position = pos
pl.position.x = pos.x
pl.position.y = pos.y
- pl.boosting = p.boosting
+ pl.boosting = p.boost
pl.rot = p.rot
break;
}
@@ -307,7 +307,7 @@ function set_interact(edge: boolean) {
function tick_update() {
const p = players.get(my_id)
if (!p) return
- send({ player: my_id, type: "movement", pos: [p.position.x, p.position.y], direction: [p.direction.x, p.direction.y], boosting: p.boosting })
+ send({ player: my_id, type: "movement", pos: [p.position.x, p.position.y], dir: [p.direction.x, p.direction.y], boost: p.boosting })
}
function frame_update(dt: number) {
diff --git a/test-client/protocol.ts b/test-client/protocol.ts
index 353c9100..e16f80d3 100644
--- a/test-client/protocol.ts
+++ b/test-client/protocol.ts
@@ -38,7 +38,7 @@ export interface Gamedata {
export type PacketS =
{ type: "join", name: string, character: number } // Spawns a new character. The server replies with "joined". Dont send it to spectate.
| { type: "leave", player: PlayerID } // Despawns a character
- | { type: "movement", player: PlayerID, pos: Vec2, direction: Vec2, boosting: boolean }
+ | { type: "movement", player: PlayerID, pos: Vec2, dir: Vec2, boost: boolean }
| { type: "interact", player: PlayerID, pos?: Vec2 } // Interact with some tile. pos is a position when pressing and null when releasing interact button
| { type: "communicate", player: PlayerID, message?: Message, persist: boolean } // Send a message
| { type: "replay_tick", dt: number } // Steps forward in replay.
@@ -49,7 +49,8 @@ export type PacketC =
| { type: "data", data: Gamedata } // Game data was changed
| { type: "add_player", id: PlayerID, name: string, position: Vec2, character: number } // Somebody else joined (or was already in the game)
| { type: "remove_player", id: PlayerID } // Somebody left
- | { type: "position", player: PlayerID, pos: Vec2, rot: number, boosting: boolean } // Update the position of a players (your own position is included here)
+ | { type: "movement", player: PlayerID, pos: Vec2, rot: number, boost: boolean, dir: Vec2 } // Update the movement of a players (your own position is included here)
+ | { type: "movement_sync" } // Your movement is difference on the server, you should update your position from a `position` packet
| { type: "move_item", from: ItemLocation, to: ItemLocation } // Item moved
| { type: "set_item", location: ItemLocation, item?: ItemIndex } // the item contained in a tile or player changed
| { type: "set_progress", item: ItemLocation, progress?: number, warn: boolean } // A tile is doing something. progress goes from 0 to 1, then null when finished
@@ -58,7 +59,6 @@ export type PacketC =
| { type: "server_message", text: string } // Text message from the server
| { type: "score" } & Score // Supplies information for score OSD
| { type: "menu" } & Menu // Open a menu on the client-side
- | { type: "movement_sync" } // Your movement is difference on the server, you should update your position from a `position` packet
| { type: "environment", effects: string[] }
| { type: "set_ingame", state: boolean, lobby: boolean } // Set to false when entering the game or switching maps
| { type: "error", message: string } // Your client did something wrong.