├── client ├── assets │ ├── client_network.ron │ ├── display_config.ron │ ├── audio │ │ ├── ouch.ogg │ │ ├── shot.ogg │ │ └── handgun_ready.ogg │ ├── spritesheet.png │ ├── fonts │ │ ├── square.ttf │ │ └── carnevalee_freakshow.ttf │ ├── input.ron │ └── spritesheet.ron ├── src │ ├── states │ │ ├── mod.rs │ │ ├── connection.rs │ │ └── play.rs │ ├── components │ │ ├── mod.rs │ │ └── weapon_info.rs │ ├── resources │ │ ├── network_stream_id.rs │ │ ├── audio.rs │ │ ├── mod.rs │ │ └── sprite_resource.rs │ ├── systems │ │ ├── audio_player.rs │ │ ├── camera.rs │ │ ├── mod.rs │ │ ├── sprite.rs │ │ ├── shooter.rs │ │ ├── network_entity_delete.rs │ │ ├── player_update.rs │ │ ├── notification_bar.rs │ │ ├── network_entity_update.rs │ │ ├── cursor_pos_update.rs │ │ ├── input_state.rs │ │ ├── hud.rs │ │ ├── network_messenger.rs │ │ └── client_connect.rs │ ├── entities │ │ ├── mod.rs │ │ ├── tilemap.rs │ │ └── player.rs │ ├── test_helpers.rs │ └── main.rs └── Cargo.toml ├── resources ├── protocol.ron ├── server_network.ron ├── assets │ └── weapons │ │ ├── revolver.ron │ │ ├── rifle.ron │ │ └── shotgun.ron └── map │ └── rust2.wmap ├── .gitignore ├── media └── westiny_logo.png ├── .travis.yml ├── common ├── src │ ├── components │ │ ├── player.rs │ │ ├── projectile.rs │ │ ├── damage.rs │ │ ├── eliminate.rs │ │ ├── bounding_circle.rs │ │ ├── respawn.rs │ │ ├── sprite_id.rs │ │ ├── velocity.rs │ │ ├── health.rs │ │ ├── network_id.rs │ │ ├── time_limit.rs │ │ ├── mod.rs │ │ ├── input.rs │ │ └── weapon.rs │ ├── events │ │ ├── mod.rs │ │ ├── entity_delete.rs │ │ └── damage.rs │ ├── resources │ │ ├── audio.rs │ │ ├── collision.rs │ │ ├── weapon.rs │ │ ├── mod.rs │ │ └── map.rs │ ├── systems │ │ ├── lifespan.rs │ │ ├── mod.rs │ │ ├── physics.rs │ │ └── collision.rs │ ├── entities │ │ ├── mod.rs │ │ ├── barrel.rs │ │ └── bullet.rs │ ├── lib.rs │ ├── serialization.rs │ ├── network.rs │ ├── utilities.rs │ ├── collision.rs │ └── metric_dimension.rs ├── proptest-regressions │ └── serialization.txt └── Cargo.toml ├── server ├── src │ ├── components │ │ ├── mod.rs │ │ └── client.rs │ ├── resources │ │ ├── network_stream_id.rs │ │ ├── mod.rs │ │ ├── event.rs │ │ ├── network_id_supplier.rs │ │ └── client_registry.rs │ ├── systems │ │ ├── mod.rs │ │ ├── command_transformer.rs │ │ ├── entity_state_broadcaster.rs │ │ ├── death.rs │ │ ├── entity_delete_broadcaster.rs │ │ ├── health.rs │ │ ├── player_movement.rs │ │ ├── client_introduction.rs │ │ ├── spawn.rs │ │ ├── network_messenger.rs │ │ └── shooter.rs │ ├── diagnostics.rs │ └── main.rs └── Cargo.toml ├── src └── lib.rs ├── test ├── Cargo.toml └── src │ └── lib.rs ├── Cargo.toml ├── NETWORK-TESTING.md └── README.md /client/assets/client_network.ron: -------------------------------------------------------------------------------- 1 | ClientPort( 2 | 0 3 | ) -------------------------------------------------------------------------------- /resources/protocol.ron: -------------------------------------------------------------------------------- 1 | ( 2 | hartbeat_interval: 3, 3 | ) -------------------------------------------------------------------------------- /resources/server_network.ron: -------------------------------------------------------------------------------- 1 | ServerAddress( 2 | address: "127.0.0.1:5745" 3 | ) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | */target/ 3 | **/*.rs.bk 4 | Cargo.lock 5 | 6 | *.iml 7 | .idea/ 8 | -------------------------------------------------------------------------------- /media/westiny_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/westinygame/westiny/HEAD/media/westiny_logo.png -------------------------------------------------------------------------------- /client/assets/display_config.ron: -------------------------------------------------------------------------------- 1 | ( 2 | title: "Westiny", 3 | dimensions: Some((640, 480)), 4 | ) 5 | -------------------------------------------------------------------------------- /client/assets/audio/ouch.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/westinygame/westiny/HEAD/client/assets/audio/ouch.ogg -------------------------------------------------------------------------------- /client/assets/audio/shot.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/westinygame/westiny/HEAD/client/assets/audio/shot.ogg -------------------------------------------------------------------------------- /client/assets/spritesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/westinygame/westiny/HEAD/client/assets/spritesheet.png -------------------------------------------------------------------------------- /client/assets/fonts/square.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/westinygame/westiny/HEAD/client/assets/fonts/square.ttf -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - stable 5 | 6 | before_install: 7 | - sudo apt-get install libsdl2-dev 8 | 9 | -------------------------------------------------------------------------------- /common/src/components/player.rs: -------------------------------------------------------------------------------- 1 | use bevy::ecs::component::Component; 2 | 3 | #[derive(Component)] 4 | pub struct Player; 5 | -------------------------------------------------------------------------------- /client/assets/audio/handgun_ready.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/westinygame/westiny/HEAD/client/assets/audio/handgun_ready.ogg -------------------------------------------------------------------------------- /common/src/components/projectile.rs: -------------------------------------------------------------------------------- 1 | use bevy::ecs::component::Component; 2 | 3 | #[derive(Component)] 4 | pub struct Projectile; 5 | -------------------------------------------------------------------------------- /server/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) use client::Client; 2 | pub(crate) use westiny_common::components::*; 3 | 4 | mod client; 5 | -------------------------------------------------------------------------------- /client/assets/fonts/carnevalee_freakshow.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/westinygame/westiny/HEAD/client/assets/fonts/carnevalee_freakshow.ttf -------------------------------------------------------------------------------- /common/src/events/mod.rs: -------------------------------------------------------------------------------- 1 | pub use damage::DamageEvent; 2 | pub use entity_delete::EntityDelete; 3 | 4 | mod damage; 5 | mod entity_delete; 6 | -------------------------------------------------------------------------------- /common/src/components/damage.rs: -------------------------------------------------------------------------------- 1 | use bevy::ecs::component::Component; 2 | 3 | #[derive(Copy, Clone, Debug, Component)] 4 | pub struct Damage(pub u16); 5 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // this file is empty due to this root crate serves as a container for the sub-crates beneath 2 | // but a lib/bin target must be defined for cargo 3 | -------------------------------------------------------------------------------- /test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "westiny_test" 3 | version = "0.1.0" 4 | authors = ["westinygame"] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | bevy = "0.9.1" 9 | -------------------------------------------------------------------------------- /common/src/events/entity_delete.rs: -------------------------------------------------------------------------------- 1 | use bevy::ecs::prelude::Entity; 2 | use derive_new::new; 3 | 4 | #[derive(new)] 5 | pub struct EntityDelete { 6 | pub entity_id: Entity, 7 | } 8 | -------------------------------------------------------------------------------- /common/src/components/eliminate.rs: -------------------------------------------------------------------------------- 1 | use bevy::ecs::component::Component; 2 | 3 | #[derive(Copy, Clone, Debug, Component)] 4 | pub struct Eliminated { 5 | pub elimination_time_sec: f64, 6 | } 7 | -------------------------------------------------------------------------------- /common/src/events/damage.rs: -------------------------------------------------------------------------------- 1 | use crate::components::Damage; 2 | use bevy::ecs::prelude::Entity; 3 | 4 | pub struct DamageEvent { 5 | pub damage: Damage, 6 | pub target: Entity, 7 | } 8 | -------------------------------------------------------------------------------- /test/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! assert_delta { 3 | ($x:expr, $y:expr, $delta:expr) => { 4 | assert!(($x - $y).abs() < $delta, "x: {}, y: {}, distance: {}", $x, $y, ($x-$y).abs()); 5 | }; 6 | } 7 | -------------------------------------------------------------------------------- /server/src/components/client.rs: -------------------------------------------------------------------------------- 1 | use crate::resources::ClientID; 2 | use bevy::ecs::component::Component; 3 | use derive_new::new; 4 | 5 | #[derive(Copy, Clone, new, Component)] 6 | pub struct Client { 7 | pub id: ClientID, 8 | } 9 | -------------------------------------------------------------------------------- /client/src/states/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod connection; 2 | pub mod play; 3 | 4 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 5 | #[cfg_attr(test, derive(bevy::prelude::Resource))] 6 | pub enum AppState { 7 | Connect, 8 | PlayInit, 9 | Play, 10 | } 11 | -------------------------------------------------------------------------------- /common/src/components/bounding_circle.rs: -------------------------------------------------------------------------------- 1 | use crate::metric_dimension::length::Meter; 2 | use bevy::prelude::*; 3 | 4 | #[derive(Default, Copy, Clone, Debug, Component, Reflect)] 5 | #[reflect(Component)] 6 | pub struct BoundingCircle { 7 | pub radius: Meter, 8 | } 9 | -------------------------------------------------------------------------------- /client/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | pub use weapon_info::WeaponInfo; 2 | pub use westiny_common::components::*; 3 | 4 | mod weapon_info; 5 | 6 | pub mod hud { 7 | use bevy::prelude::Component; 8 | 9 | #[derive(Component)] 10 | pub struct Health; 11 | } 12 | -------------------------------------------------------------------------------- /common/src/components/respawn.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | /// This component marks if an entity should respawn after being eliminated 4 | #[derive(Copy, Clone, Debug, bevy::prelude::Component)] 5 | pub struct Respawn { 6 | pub respawn_duration: Duration, 7 | } 8 | -------------------------------------------------------------------------------- /client/src/resources/network_stream_id.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone, Debug)] 2 | #[repr(u8)] 3 | pub enum StreamId { 4 | InputState, 5 | } 6 | 7 | impl From for Option { 8 | fn from(id: StreamId) -> Option { 9 | Some(id as u8) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /resources/assets/weapons/revolver.ron: -------------------------------------------------------------------------------- 1 | ( 2 | fire_rate: 7.2, 3 | magazine_size: 6, 4 | reload_time: Second(2.0), 5 | damage: 5, 6 | spread: 10.0, 7 | bullet_distance_limit: Meter(7.5), 8 | bullet_speed: MeterPerSec(12.5), 9 | shot: Single, 10 | pellet_number: 1, 11 | ) -------------------------------------------------------------------------------- /resources/assets/weapons/rifle.ron: -------------------------------------------------------------------------------- 1 | ( 2 | fire_rate: 1.0, 3 | magazine_size: 1, 4 | reload_time: Second(3.0), 5 | damage: 50, 6 | spread: 4.0, 7 | bullet_distance_limit: Meter(12.5), 8 | bullet_speed: MeterPerSec(15.5), 9 | shot: Single, 10 | pellet_number: 1, 11 | ) -------------------------------------------------------------------------------- /resources/assets/weapons/shotgun.ron: -------------------------------------------------------------------------------- 1 | ( 2 | fire_rate: 1.2, 3 | magazine_size: 2, 4 | reload_time: Second(3.0), 5 | damage: 3, 6 | spread: 20.0, 7 | bullet_distance_limit: Meter(5.0), 8 | bullet_speed: MeterPerSec(18.0), 9 | shot: Single, 10 | pellet_number: 9, 11 | ) -------------------------------------------------------------------------------- /common/src/components/sprite_id.rs: -------------------------------------------------------------------------------- 1 | use bevy::ecs::component::Component; 2 | 3 | #[derive(Copy, Clone, Eq, PartialEq, Component)] 4 | #[repr(usize)] 5 | pub enum SpriteId { 6 | Grass = 0, 7 | Barren = 1, 8 | Player = 2, 9 | Barrel = 3, 10 | Corpse = 4, 11 | Bullet = 5, 12 | HandWithPistol = 6, 13 | } 14 | -------------------------------------------------------------------------------- /common/src/components/velocity.rs: -------------------------------------------------------------------------------- 1 | use crate::metric_dimension::MeterPerSecVec2; 2 | use bevy::ecs::component::Component; 3 | 4 | #[derive(Debug, Component)] 5 | pub struct Velocity(pub MeterPerSecVec2); 6 | 7 | impl Default for Velocity { 8 | fn default() -> Self { 9 | Velocity(MeterPerSecVec2::from_raw(0.0, 0.0)) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /server/src/resources/network_stream_id.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone, Debug)] 2 | #[repr(u8)] 3 | pub enum StreamId { 4 | EntityStateUpdate, 5 | HealthUpdate, 6 | AmmoUpdate, 7 | WeaponSwitch, 8 | ShotEvent, 9 | PlayerDeath, 10 | } 11 | 12 | impl From for Option { 13 | fn from(id: StreamId) -> Option { 14 | Some(id as u8) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/src/resources/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) use client_registry::ClientID; 2 | pub(crate) use event::{ClientNetworkEvent, NetworkCommand}; 3 | 4 | pub use client_registry::ClientRegistry; 5 | pub use network_id_supplier::NetworkIdSupplier; 6 | pub use network_stream_id::StreamId; 7 | pub use westiny_common::resources::*; 8 | 9 | mod client_registry; 10 | mod event; 11 | mod network_id_supplier; 12 | mod network_stream_id; 13 | -------------------------------------------------------------------------------- /common/src/resources/audio.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone)] 2 | #[repr(usize)] 3 | pub enum SoundId { 4 | SingleShot = 0, 5 | WeaponReady = 1, 6 | Ouch = 2, 7 | } 8 | 9 | #[derive(Default, bevy::prelude::Resource)] 10 | pub struct AudioQueue { 11 | pub sound: Option, 12 | } 13 | 14 | impl AudioQueue { 15 | pub fn play(&mut self, id: SoundId, _volume: f32) { 16 | self.sound = Some(id); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/states/connection.rs: -------------------------------------------------------------------------------- 1 | use crate::states::AppState; 2 | use crate::systems; 3 | use bevy::prelude::SystemSet; 4 | use bevy::prelude::IntoSystemDescriptor; 5 | 6 | pub fn connect_state_systems() -> SystemSet { 7 | SystemSet::on_update(AppState::Connect) 8 | .with_system(systems::send_connection_request.label("connection_request")) 9 | .with_system(systems::receive_connection_response.label("connection_response")) 10 | } 11 | -------------------------------------------------------------------------------- /common/src/components/health.rs: -------------------------------------------------------------------------------- 1 | use crate::components::Damage; 2 | use bevy::ecs::component::Component; 3 | use serde::{Deserialize, Serialize}; 4 | use std::ops::SubAssign; 5 | 6 | #[derive(Copy, Clone, Debug, Serialize, Deserialize, Component)] 7 | #[cfg_attr(test, derive(PartialEq))] 8 | pub struct Health(pub u16); 9 | 10 | impl SubAssign for Health { 11 | fn sub_assign(&mut self, damage: Damage) { 12 | self.0 -= damage.0; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /common/src/components/network_id.rs: -------------------------------------------------------------------------------- 1 | use bevy::ecs::component::Component; 2 | use derive_new::new; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize, Hash, new, Component)] 6 | pub struct NetworkId { 7 | pub entity_type: EntityType, 8 | pub id: u32, 9 | } 10 | 11 | #[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] 12 | pub enum EntityType { 13 | Player, 14 | } 15 | -------------------------------------------------------------------------------- /common/proptest-regressions/serialization.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc b244fc4e772a06f6bc6bce1c6c91c855537c9451cf950594ed240a2157e9a87f # shrinks to packet = ConnectionRequest { player_name: "" } 8 | -------------------------------------------------------------------------------- /common/src/components/time_limit.rs: -------------------------------------------------------------------------------- 1 | use crate::metric_dimension::Second; 2 | use bevy::ecs::component::Component; 3 | use std::time::Duration; 4 | 5 | #[derive(Debug, Component)] 6 | pub struct Lifespan { 7 | pub living_until: Duration, 8 | } 9 | 10 | impl Lifespan { 11 | pub fn new(secs_to_live: Second, timing_start: Duration) -> Self { 12 | Lifespan { 13 | living_until: timing_start + secs_to_live.into_duration(), 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/src/components/weapon_info.rs: -------------------------------------------------------------------------------- 1 | use bevy::prelude::Component; 2 | 3 | #[derive(Debug, Clone, Component)] 4 | pub struct WeaponInfo { 5 | pub magazine_size: u32, 6 | pub bullets_in_magazine: u32, 7 | pub name: String, 8 | } 9 | 10 | impl Default for WeaponInfo { 11 | fn default() -> Self { 12 | WeaponInfo { 13 | magazine_size: 0, 14 | bullets_in_magazine: 0, 15 | name: "None".to_string(), 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/src/systems/audio_player.rs: -------------------------------------------------------------------------------- 1 | use westiny_common::resources::AudioQueue; 2 | 3 | use bevy::prelude::*; 4 | use crate::resources::Sounds; 5 | 6 | pub fn play_audio( 7 | mut queue: ResMut, 8 | audio: Res