Initial changes for multiplayer mode

This commit is contained in:
Paul-Christian Volkmer 2022-05-04 13:50:34 +02:00
parent ccf90b4da7
commit b59b6d62a7
7 changed files with 620 additions and 30 deletions

275
Cargo.lock generated
View File

@ -14,6 +14,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bytes"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -41,7 +47,16 @@ checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi", "wasi 0.10.2+wasi-snapshot-preview1",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
] ]
[[package]] [[package]]
@ -56,6 +71,63 @@ version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94" checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94"
[[package]]
name = "lock_api"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "mio"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9"
dependencies = [
"libc",
"log",
"miow",
"ntapi",
"wasi 0.11.0+wasi-snapshot-preview1",
"winapi",
]
[[package]]
name = "miow"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
dependencies = [
"winapi",
]
[[package]]
name = "ntapi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.44" version = "0.1.44"
@ -75,12 +147,75 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "num_cpus"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "parking_lot"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.16" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro2"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.8.5" version = "0.8.5"
@ -111,6 +246,21 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "redox_syscall"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [
"bitflags",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]] [[package]]
name = "sdl2" name = "sdl2"
version = "0.35.2" version = "0.35.2"
@ -134,6 +284,42 @@ dependencies = [
"version-compare", "version-compare",
] ]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "socket2"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "syn"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.1.43" version = "0.1.43"
@ -144,6 +330,43 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "tokio"
version = "1.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce653fb475565de9f6fb0614b28bca8df2c430c0cf84bcd9c843f15de5414cc"
dependencies = [
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"once_cell",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"winapi",
]
[[package]]
name = "tokio-macros"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-xid"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]] [[package]]
name = "version-compare" name = "version-compare"
version = "0.1.0" version = "0.1.0"
@ -156,6 +379,12 @@ version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"
@ -178,6 +407,49 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]] [[package]]
name = "winelounge" name = "winelounge"
version = "0.1.0" version = "0.1.0"
@ -185,4 +457,5 @@ dependencies = [
"chrono", "chrono",
"rand", "rand",
"sdl2", "sdl2",
"tokio",
] ]

View File

@ -3,7 +3,7 @@ name = "winelounge"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html autobins = true
[dependencies] [dependencies]
chrono = "0.4" chrono = "0.4"
@ -13,6 +13,11 @@ rand = "0.8"
version = "0.35" version = "0.35"
features = ["image", "ttf"] features = ["image", "ttf"]
[dependencies.tokio]
version = "1.18"
features = ["full"]
default-features = false
[profile.release] [profile.release]
opt-level = "s" opt-level = "s"
codegen-units = 1 codegen-units = 1

View File

@ -0,0 +1,12 @@
#[tokio::main]
async fn main() {
let listener = tokio::net::TcpListener::bind(":7888").await.expect("Cannot open socket");
'listener: loop {
let (socket, _) = listener.accept().await.expect("Cannot accept connection");
}
}

View File

@ -11,6 +11,7 @@ use crate::world::World;
mod player; mod player;
mod sprite; mod sprite;
mod world; mod world;
mod net;
const GLASS_SPACE: u8 = 5; const GLASS_SPACE: u8 = 5;
@ -40,6 +41,8 @@ fn main() {
let mut world = World::init(); let mut world = World::init();
world.spawn_player("Test".to_string(), 100, 100);
'running: loop { 'running: loop {
for event in event_pump.poll_iter() { for event in event_pump.poll_iter() {
match event { match event {

183
src/net.rs Normal file
View File

@ -0,0 +1,183 @@
use crate::world::{BoxAreaContent, BoxAreaPosition, Command, Direction};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
impl Display for Direction {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Direction::Up => write!(f, "Up"),
Direction::Down => write!(f, "Down"),
Direction::Left => write!(f, "Left"),
Direction::Right => write!(f, "Right"),
}
}
}
impl FromStr for Direction {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Up" => Ok(Direction::Up),
"Down" => Ok(Direction::Down),
"Left" => Ok(Direction::Left),
"Right" => Ok(Direction::Right),
_ => Err(()),
}
}
}
impl Display for BoxAreaPosition {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
BoxAreaPosition::RightTop => write!(f, "RightTop"),
BoxAreaPosition::RightBottom => write!(f, "RightBottom"),
BoxAreaPosition::LeftBottom => write!(f, "LeftBottom"),
BoxAreaPosition::LeftTop => write!(f, "LeftTop"),
}
}
}
impl FromStr for BoxAreaPosition {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"RightTop" => Ok(BoxAreaPosition::RightTop),
"RightBottom" => Ok(BoxAreaPosition::RightBottom),
"LeftBottom" => Ok(BoxAreaPosition::LeftBottom),
"LeftTop" => Ok(BoxAreaPosition::LeftTop),
_ => Err(()),
}
}
}
impl Display for BoxAreaContent {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
BoxAreaContent::Nothing => write!(f, "Nothing"),
BoxAreaContent::HiddenBox => write!(f, "HiddenBox"),
BoxAreaContent::EmptyGlass => write!(f, "EmptyGlass"),
BoxAreaContent::FilledBottle => write!(f, "FilledBottle"),
BoxAreaContent::EmptyBottle => write!(f, "EmptyBottle"),
}
}
}
impl FromStr for BoxAreaContent {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Nothing" => Ok(BoxAreaContent::Nothing),
"HiddenBox" => Ok(BoxAreaContent::HiddenBox),
"EmptyGlass" => Ok(BoxAreaContent::EmptyGlass),
"FilledBottle" => Ok(BoxAreaContent::FilledBottle),
"EmptyBottle" => Ok(BoxAreaContent::EmptyBottle),
_ => Err(()),
}
}
}
impl Display for Command {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Command::SpawnPlayer(player_id, x, y) => write!(f, "Spawn {}", player_id),
Command::RemovePlayer(player_id) => write!(f, "Face {}", player_id),
Command::FacePlayer(player_id, direction) => write!(f, "Face {} {}", player_id, direction),
Command::MovePlayer(player_id, direction) => write!(f, "Move {} {}", player_id, direction),
Command::StopPlayer(player_id) => write!(f, "Stop {}", player_id),
Command::UpdateBoxArea(pos, content) => write!(f, "UpdateBoxArea {} {}", pos, content),
}
}
}
impl FromStr for Command {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(' ');
match parts.next() {
Some("Spawn") => match parts.next() {
Some(player_id) => match parts.next() {
Some(x) => match parts.next() {
Some(y) => Ok(Command::SpawnPlayer(player_id.to_string(), x.parse().unwrap(), y.parse().unwrap())),
_ => Err(())
},
_ => Err(())
}
_ => Err(())
},
Some("Remove") => match parts.next() {
Some(player_id) => Ok(Command::RemovePlayer(player_id.to_string())),
_ => Err(())
},
Some("Face") => match parts.next() {
Some(player_id) => match parts.next() {
Some(direction) => Ok(Command::FacePlayer(player_id.to_string(), direction.parse::<Direction>().unwrap())),
_ => Err(()),
},
_ => Err(())
},
Some("Move") => match parts.next() {
Some(player_id) => match parts.next() {
Some(direction) => Ok(Command::MovePlayer(player_id.to_string(), direction.parse::<Direction>().unwrap())),
_ => Err(()),
},
_ => Err(())
},
Some("Stop") => match parts.next() {
Some(player_id) => Ok(Command::StopPlayer(player_id.to_string())),
_ => Err(())
},
Some("UpdateBoxArea") => match parts.next() {
Some(position) => {
let position = position.parse::<BoxAreaPosition>().unwrap();
match parts.next() {
Some(content) => {
let content = content.parse::<BoxAreaContent>().unwrap();
Ok(Command::UpdateBoxArea(position, content))
}
_ => Err(()),
}
}
_ => Err(()),
},
Some(_) | None => Err(()),
}
}
}
#[cfg(test)]
mod test {
use crate::world::Direction::{Left, Up};
use crate::world::{BoxAreaContent, BoxAreaPosition, Command};
#[test]
fn should_deserialize_command_line() {
assert_eq!(
Command::SpawnPlayer("1234".to_string(), 100, 200),
"Spawn 1234 100 200".parse::<Command>().unwrap()
);
assert_eq!(
Command::RemovePlayer("1234".to_string()),
"Remove 1234".parse::<Command>().unwrap()
);
assert_eq!(
Command::FacePlayer("1234".to_string(), Left),
"Face 1234 Left".parse::<Command>().unwrap()
);
assert_eq!(
Command::MovePlayer("1234".to_string(), Up),
"Move 1234 Up".parse::<Command>().unwrap()
);
assert_eq!(Command::StopPlayer("1234".to_string()), "Stop 1234".parse::<Command>().unwrap());
assert_eq!(
Command::UpdateBoxArea(BoxAreaPosition::RightBottom, BoxAreaContent::HiddenBox),
"UpdateBoxArea RightBottom HiddenBox"
.parse::<Command>()
.unwrap()
);
}
}

View File

@ -1,3 +1,4 @@
use rand::random;
use crate::sprite::Sprite; use crate::sprite::Sprite;
use crate::{sprite, GLASS_SPACE}; use crate::{sprite, GLASS_SPACE};
use sdl2::rect::{Point, Rect}; use sdl2::rect::{Point, Rect};
@ -5,6 +6,7 @@ use sdl2::render::{Texture, WindowCanvas};
/// The player with his position, direction ... /// The player with his position, direction ...
pub struct Player { pub struct Player {
pub id: String,
position: Point, position: Point,
direction: PlayerDirection, direction: PlayerDirection,
footstep: u8, footstep: u8,
@ -24,8 +26,13 @@ enum PlayerDirection {
impl Player { impl Player {
/// Initializes Player with fixed position and direction. /// Initializes Player with fixed position and direction.
pub fn init() -> Player { pub fn init() -> Player {
Self::spawn(random::<u32>().to_string().as_str(), 380, 250)
}
pub fn spawn(player_id: &str, x: u32, y: u32) -> Player {
Player { Player {
position: Point::new(380, 250), id: player_id.to_string(),
position: Point::new(x as i32, y as i32),
direction: PlayerDirection::Down, direction: PlayerDirection::Down,
footstep: 0, footstep: 0,
empty_glasses: 0, empty_glasses: 0,

View File

@ -10,6 +10,7 @@ use crate::{Player, GLASS_SPACE};
pub struct World { pub struct World {
player: Player, player: Player,
remote_player: Option<Player>,
right_top_box_area: BoxArea, right_top_box_area: BoxArea,
right_bottom_box_area: BoxArea, right_bottom_box_area: BoxArea,
left_bottom_box_area: BoxArea, left_bottom_box_area: BoxArea,
@ -23,6 +24,7 @@ impl World {
pub fn init() -> World { pub fn init() -> World {
World { World {
player: Player::init(), player: Player::init(),
remote_player: None,
right_top_box_area: BoxArea::new(BoxAreaPosition::RightTop, BoxAreaContent::EmptyGlass), right_top_box_area: BoxArea::new(BoxAreaPosition::RightTop, BoxAreaContent::EmptyGlass),
right_bottom_box_area: BoxArea::new( right_bottom_box_area: BoxArea::new(
BoxAreaPosition::RightBottom, BoxAreaPosition::RightBottom,
@ -43,6 +45,14 @@ impl World {
} }
} }
pub fn spawn_player(&mut self, player_id: String, x: u32, y: u32) {
self.remote_player = Some(Player::spawn(player_id.as_str(), x, y));
}
pub fn get_player(&mut self, _: String) -> &mut Player {
&mut self.player
}
pub fn playable_rect() -> Rect { pub fn playable_rect() -> Rect {
Rect::new(0, 50, 800, 550) Rect::new(0, 50, 800, 550)
} }
@ -52,58 +62,120 @@ impl World {
/// This checks if player collides with any stop item or will move out of world. /// This checks if player collides with any stop item or will move out of world.
/// If player can move, move him and turn him to the correct side. /// If player can move, move him and turn him to the correct side.
pub fn handle_event(&mut self, event: Event) { pub fn handle_event(&mut self, event: Event) {
let player_id = self.player.id.clone();
match event { match event {
Event::KeyDown { Event::KeyDown {
keycode: Some(Keycode::Up) | Some(Keycode::W), keycode: Some(Keycode::Up) | Some(Keycode::W),
.. ..
} => { } => {
self.player.move_up(); self.execute_command(Command::MovePlayer(player_id.clone(), Direction::Up));
if self.collides_with_stop() || !self.player.within_rect(&Self::playable_rect()) { if self.collides_with_stop() || !self.player.within_rect(&Self::playable_rect()) {
self.player.move_down(); self.execute_command(Command::MovePlayer(player_id.clone(), Direction::Down));
self.player.face_up(); self.execute_command(Command::FacePlayer(player_id.clone(), Direction::Up));
} }
} }
Event::KeyDown { Event::KeyDown {
keycode: Some(Keycode::Down) | Some(Keycode::S), keycode: Some(Keycode::Down) | Some(Keycode::S),
.. ..
} => { } => {
self.player.move_down(); self.execute_command(Command::MovePlayer(player_id.clone(), Direction::Down));
if self.collides_with_stop() || !self.player.within_rect(&Self::playable_rect()) { if self.collides_with_stop() || !self.player.within_rect(&Self::playable_rect()) {
self.player.move_up(); self.execute_command(Command::MovePlayer(player_id.clone(), Direction::Up));
self.player.face_down(); self.execute_command(Command::FacePlayer(player_id.clone(), Direction::Down));
} }
} }
Event::KeyDown { Event::KeyDown {
keycode: Some(Keycode::Left) | Some(Keycode::A), keycode: Some(Keycode::Left) | Some(Keycode::A),
.. ..
} => { } => {
self.player.move_left(); self.execute_command(Command::MovePlayer(player_id.clone(), Direction::Left));
if self.collides_with_stop() || !self.player.within_rect(&Self::playable_rect()) { if self.collides_with_stop() || !self.player.within_rect(&Self::playable_rect()) {
self.player.move_right(); self.execute_command(Command::MovePlayer(player_id.clone(), Direction::Right));
self.player.face_left(); self.execute_command(Command::FacePlayer(player_id.clone(), Direction::Left));
} }
} }
Event::KeyDown { Event::KeyDown {
keycode: Some(Keycode::Right) | Some(Keycode::D), keycode: Some(Keycode::Right) | Some(Keycode::D),
.. ..
} => { } => {
self.player.move_right(); self.execute_command(Command::MovePlayer(player_id.clone(), Direction::Right));
if self.collides_with_stop() || !self.player.within_rect(&Self::playable_rect()) { if self.collides_with_stop() || !self.player.within_rect(&Self::playable_rect()) {
self.player.move_left(); self.execute_command(Command::MovePlayer(player_id.clone(), Direction::Left));
self.player.face_right(); self.execute_command(Command::FacePlayer(player_id.clone(), Direction::Right));
} }
} }
Event::KeyUp { .. } => self.player.stop(), Event::KeyUp { .. } => self.execute_command(Command::StopPlayer(player_id.clone())),
_ => {} _ => {}
} }
} }
/// Executes a command for world update.
pub fn execute_command(&mut self, command: Command) {
println!("{}", command);
match command {
Command::SpawnPlayer(player_id, x, y) => &mut {
// TBD
},
Command::RemovePlayer(player_id) => &mut {
// TBD
},
Command::FacePlayer(player_id, Direction::Down) => {
&mut self.get_player(player_id).face_down()
},
Command::FacePlayer(player_id, Direction::Up) => {
&mut self.get_player(player_id).face_up()
},
Command::FacePlayer(player_id, Direction::Left) => {
&mut self.get_player(player_id).face_left()
},
Command::FacePlayer(player_id, Direction::Right) => {
&mut self.get_player(player_id).face_right()
},
Command::MovePlayer(player_id, Direction::Down) => {
&mut self.get_player(player_id).move_down()
},
Command::MovePlayer(player_id, Direction::Up) => {
&mut self.get_player(player_id).move_up()
},
Command::MovePlayer(player_id, Direction::Left) => {
&mut self.get_player(player_id).move_left()
},
Command::MovePlayer(player_id, Direction::Right) => {
&mut self.get_player(player_id).move_right()
},
Command::StopPlayer(player_id) => {
&mut self.get_player(player_id).stop()
},
Command::UpdateBoxArea(position, content) => &mut {
match position {
BoxAreaPosition::RightTop => {
self.right_top_box_area.update_content(content);
self.right_top_box_area.last_update = chrono::Utc::now().timestamp();
}
BoxAreaPosition::RightBottom => {
self.right_bottom_box_area.update_content(content);
self.right_top_box_area.last_update = chrono::Utc::now().timestamp();
}
BoxAreaPosition::LeftBottom => {
self.left_bottom_box_area.update_content(content);
self.right_top_box_area.last_update = chrono::Utc::now().timestamp();
}
BoxAreaPosition::LeftTop => {
self.left_top_box_area.update_content(content);
self.right_top_box_area.last_update = chrono::Utc::now().timestamp();
}
};
}
};
}
/// Updates box areas to provide new boxes and remove items after some time /// Updates box areas to provide new boxes and remove items after some time
pub fn update_box_areas(&mut self) { pub fn update_box_areas(&mut self) {
World::update_box_area(&mut self.right_top_box_area); self.update_box_area(BoxAreaPosition::RightTop);
World::update_box_area(&mut self.right_bottom_box_area); self.update_box_area(BoxAreaPosition::RightBottom);
World::update_box_area(&mut self.left_bottom_box_area); self.update_box_area(BoxAreaPosition::LeftBottom);
World::update_box_area(&mut self.left_top_box_area); self.update_box_area(BoxAreaPosition::LeftTop);
} }
/// Handles both, collisions with lounge and any box area /// Handles both, collisions with lounge and any box area
@ -159,6 +231,11 @@ impl World {
// Player // Player
self.player.render(canvas, texture); self.player.render(canvas, texture);
// Remote/other player
if let Some(remote_player) = &self.remote_player {
remote_player.render(canvas, texture);
}
// Points // Points
let x = font let x = font
.render(format!("Score: {:#04}", self.player.points).as_str()) .render(format!("Score: {:#04}", self.player.points).as_str())
@ -177,17 +254,28 @@ impl World {
canvas.present(); canvas.present();
} }
fn update_box_area(box_area: &mut BoxArea) { fn update_box_area(&mut self, box_area_position: BoxAreaPosition) {
let box_area = match box_area_position {
BoxAreaPosition::RightTop => &self.right_top_box_area,
BoxAreaPosition::RightBottom => &self.right_bottom_box_area,
BoxAreaPosition::LeftBottom => &self.left_bottom_box_area,
BoxAreaPosition::LeftTop => &self.left_top_box_area,
};
let now = chrono::Utc::now().timestamp(); let now = chrono::Utc::now().timestamp();
let r: i64 = (rand::random::<i64>() % 10) + 3; let r: i64 = (rand::random::<i64>() % 10) + 3;
if box_area.content == BoxAreaContent::Nothing && box_area.last_update + 10 < now { if box_area.content == BoxAreaContent::Nothing && box_area.last_update + 10 < now {
box_area.content = BoxAreaContent::HiddenBox; self.execute_command(Command::UpdateBoxArea(
box_area.last_update = now; box_area_position,
BoxAreaContent::HiddenBox,
));
} else if box_area.content != BoxAreaContent::Nothing && box_area.last_update + 30 < now - r } else if box_area.content != BoxAreaContent::Nothing && box_area.last_update + 30 < now - r
{ {
box_area.content = BoxAreaContent::Nothing; self.execute_command(Command::UpdateBoxArea(
box_area.last_update = now; box_area_position,
BoxAreaContent::Nothing,
));
} }
} }
@ -239,6 +327,7 @@ impl World {
} }
} }
// TODO Commands
fn handle_boxarea_collisions(&mut self) { fn handle_boxarea_collisions(&mut self) {
if let Collision::BoxArea(bap) = self.has_player_collision() { if let Collision::BoxArea(bap) = self.has_player_collision() {
let ba = match bap { let ba = match bap {
@ -256,20 +345,38 @@ impl World {
}; };
if content == BoxAreaContent::EmptyGlass && self.player.can_pick_glass() { if content == BoxAreaContent::EmptyGlass && self.player.can_pick_glass() {
ba.update_content(BoxAreaContent::Nothing); self.execute_command(Command::UpdateBoxArea(bap, BoxAreaContent::Nothing));
self.player.pick_glass(); self.player.pick_glass();
} else if content == BoxAreaContent::EmptyGlass && !self.player.can_pick_glass() { } else if content == BoxAreaContent::EmptyGlass && !self.player.can_pick_glass() {
ba.update_content(BoxAreaContent::EmptyGlass); self.execute_command(Command::UpdateBoxArea(bap, BoxAreaContent::EmptyGlass));
} else if content == BoxAreaContent::FilledBottle && self.player.can_fill_glass() { } else if content == BoxAreaContent::FilledBottle && self.player.can_fill_glass() {
ba.update_content(BoxAreaContent::EmptyBottle); self.execute_command(Command::UpdateBoxArea(bap, BoxAreaContent::EmptyBottle));
self.player.fill_glass(); self.player.fill_glass();
} else if content == BoxAreaContent::FilledBottle && !self.player.can_fill_glass() { } else if content == BoxAreaContent::FilledBottle && !self.player.can_fill_glass() {
ba.update_content(BoxAreaContent::FilledBottle); self.execute_command(Command::UpdateBoxArea(bap, BoxAreaContent::FilledBottle));
} }
} }
} }
} }
#[derive(Debug, PartialEq)]
pub enum Direction {
Up,
Down,
Left,
Right,
}
#[derive(Debug, PartialEq)]
pub enum Command {
SpawnPlayer(String, u32, u32),
RemovePlayer(String),
FacePlayer(String, Direction),
MovePlayer(String, Direction),
StopPlayer(String),
UpdateBoxArea(BoxAreaPosition, BoxAreaContent),
}
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
enum Collision { enum Collision {
BoxArea(BoxAreaPosition), BoxArea(BoxAreaPosition),
@ -317,7 +424,7 @@ impl BoxArea {
Rect::new(x_offset, y_offset, 110, 110) Rect::new(x_offset, y_offset, 110, 110)
} }
/// Checks if player collides with this BoxSrea /// Checks if player collides with this BoxArea
fn collides_with(&self, player: &Player) -> bool { fn collides_with(&self, player: &Player) -> bool {
self.bounding_rect().contains_point(player.center()) self.bounding_rect().contains_point(player.center())
} }