This commit is contained in:
Mitchell Marino 2025-01-14 20:41:09 -06:00
parent 075ccc5a55
commit 7b727d797c
13 changed files with 2305 additions and 567 deletions

1356
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ edition = "2021"
[dependencies] [dependencies]
avian3d = "0.1.2" avian3d = "0.1.2"
bevy = { version = "0.14.2", features = ["jpeg"] } bevy = { version = "0.14", features = ["jpeg"] }
bevy_dolly = "0.0.4" bevy_dolly = "0.0.4"
bevy_editor_pls = "0.10.0" bevy_editor_pls = "0.10.0"
blenvy = "0.1.0-alpha.1" blenvy = "0.1.0-alpha.1"
@ -14,6 +14,8 @@ clap = { version = "4.5.20", features = ["derive"] }
lazy_static = "1.5.0" lazy_static = "1.5.0"
lightyear = { version = "0.17.1", features = ["avian3d"] } lightyear = { version = "0.17.1", features = ["avian3d"] }
serde = { version = "1.0.213", features = ["derive"] } serde = { version = "1.0.213", features = ["derive"] }
bevy-steamworks = { version = "0.12", features = ["serde"] }
bevy_framepace = "0.17.0"
[profile.dev.package."*"] [profile.dev.package."*"]
opt-level = 3 opt-level = 3

Binary file not shown.

Binary file not shown.

View File

@ -1,10 +1,6 @@
( (
assets: assets:
[ [
("Player", File ( path: "blueprints/Player.glb" )),
("glasses.001", File ( path: "materials/glasses.001.glb" )),
("head.001", File ( path: "materials/head.001.glb" )),
("body.001", File ( path: "materials/body.001.glb" )),
("Hotel", File ( path: "blueprints/Hotel.glb" )), ("Hotel", File ( path: "blueprints/Hotel.glb" )),
("floor", File ( path: "materials/floor.glb" )), ("floor", File ( path: "materials/floor.glb" )),
] ]

File diff suppressed because it is too large Load Diff

1
launch.sh Executable file
View File

@ -0,0 +1 @@
cargo r || ~/rust/crooks/target/debug/crooks

View File

@ -2,11 +2,30 @@
use avian3d::{math::*, prelude::*}; use avian3d::{math::*, prelude::*};
use bevy::{ecs::query::Has, prelude::*, window::PrimaryWindow}; use bevy::{ecs::query::Has, prelude::*, window::PrimaryWindow};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash, Resource)]
pub enum GameCursorMode {
#[default]
Locked,
Unlocked,
}
impl GameCursorMode {
#[must_use]
pub fn switch(self) -> Self {
match self {
GameCursorMode::Locked => GameCursorMode::Unlocked,
GameCursorMode::Unlocked => GameCursorMode::Locked,
}
}
}
pub struct CharacterControllerPlugin; pub struct CharacterControllerPlugin;
impl Plugin for CharacterControllerPlugin { impl Plugin for CharacterControllerPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_event::<MovementAction>() app.insert_resource(GameCursorMode::default())
.add_event::<MovementAction>()
.add_systems(Update, set_cursor_vis)
.add_systems( .add_systems(
Update, Update,
( (
@ -105,6 +124,24 @@ impl MovementBundle {
} }
} }
fn set_cursor_vis(
buttons: Res<ButtonInput<MouseButton>>,
mut cursor_mode: ResMut<GameCursorMode>,
mut q_windows: Query<&mut Window, With<PrimaryWindow>>,
) {
let right_clicked = buttons.just_pressed(MouseButton::Right);
if right_clicked {
*cursor_mode = cursor_mode.switch();
}
if right_clicked || cursor_mode.is_changed() {
if let Ok(mut window) = q_windows.get_single_mut() {
window.cursor.visible = *cursor_mode == GameCursorMode::Unlocked;
}
}
}
impl Default for MovementBundle { impl Default for MovementBundle {
fn default() -> Self { fn default() -> Self {
Self::new(30.0, 0.9, 7.0, PI * 0.45) Self::new(30.0, 0.9, 7.0, PI * 0.45)
@ -146,9 +183,13 @@ impl CharacterControllerBundle {
} }
fn cursor_input( fn cursor_input(
cursor_mode: Res<GameCursorMode>,
mut q_windows: Query<&mut Window, With<PrimaryWindow>>, mut q_windows: Query<&mut Window, With<PrimaryWindow>>,
mut ew_movement: EventWriter<MovementAction>, mut ew_movement: EventWriter<MovementAction>,
) { ) {
if *cursor_mode == GameCursorMode::Unlocked {
return;
}
let mut window = q_windows.single_mut(); let mut window = q_windows.single_mut();
let window_center = window.size() / 2.; let window_center = window.size() / 2.;

View File

@ -1,14 +1,19 @@
use bevy::prelude::*; use bevy::prelude::*;
use lightyear::prelude::ClientConnectEvent; use lightyear::prelude::{client::ClientCommands, ClientConnectEvent};
pub struct MyClientPlugin; pub struct MyClientPlugin;
impl Plugin for MyClientPlugin { impl Plugin for MyClientPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Update, handle_events); app.add_systems(Startup, connect_to_server)
.add_systems(Update, handle_events);
} }
} }
pub fn connect_to_server(mut commands: Commands) {
commands.connect_client();
}
pub fn handle_events( pub fn handle_events(
mut er_client_events: EventReader<ClientConnectEvent>, mut er_client_events: EventReader<ClientConnectEvent>,
// mut er_server_events: EventReader<ServerConnectEvent>, // mut er_server_events: EventReader<ServerConnectEvent>,

View File

@ -1,5 +1,6 @@
use avian3d::prelude::PhysicsDebugPlugin; use avian3d::prelude::PhysicsDebugPlugin;
use avian3d::PhysicsPlugins; use avian3d::PhysicsPlugins;
use bevy::log::LogPlugin;
use bevy::prelude::*; use bevy::prelude::*;
use bevy_editor_pls::prelude::*; use bevy_editor_pls::prelude::*;
use blenvy::BlenvyPlugin; use blenvy::BlenvyPlugin;
@ -20,6 +21,9 @@ lazy_static::lazy_static! {
}; };
} }
const LOG_FILTER: &str = "wgpu=error,naga=warn,blenvy=warn";
// const LOG_FILTER: &str = "wgpu=error,naga=warn,blenvy=warn,lightyear=debug";
#[derive(Parser)] #[derive(Parser)]
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
pub struct Cli { pub struct Cli {
@ -40,16 +44,26 @@ fn main() {
#[cfg(feature = "client")] #[cfg(feature = "client")]
let app = app let app = app
.add_plugins(DefaultPlugins) // .add_plugins(SteamworksPlugin::init_app(480).unwrap())
.add_plugins(DefaultPlugins.set(LogPlugin {
filter: LOG_FILTER.to_string(),
level: bevy::log::Level::INFO,
custom_layer: |_| None,
}))
.add_plugins(lightyear::client::plugin::ClientPlugins::new( .add_plugins(lightyear::client::plugin::ClientPlugins::new(
shared_net::client_config(), shared_net::client_config(),
)) ))
.add_plugins(EditorPlugin::default()) .add_plugins(EditorPlugin::default())
.add_plugins(bevy_framepace::FramepacePlugin)
.add_plugins(client::MyClientPlugin); .add_plugins(client::MyClientPlugin);
#[cfg(feature = "server")] #[cfg(feature = "server")]
app.add_plugins( app.add_plugins(
DefaultPlugins.build(), // disable some plugins on the server DefaultPlugins.build().set(LogPlugin {
filter: LOG_FILTER.to_string(),
level: bevy::log::Level::INFO,
custom_layer: |_| None,
}), // disable some plugins on the server
// .disable::<RenderPlugin>() // .disable::<RenderPlugin>()
// .disable::<PbrPlugin>() // .disable::<PbrPlugin>()
// .disable::<CorePipelinePlugin>() // .disable::<CorePipelinePlugin>()
@ -73,7 +87,20 @@ fn main() {
CharacterControllerPlugin, CharacterControllerPlugin,
)) ))
.add_plugins(protocol::ProtocolPlugin) .add_plugins(protocol::ProtocolPlugin)
.add_plugins(BlenvyPlugin::default()); .add_plugins(BlenvyPlugin::default())
// .add_systems(Startup, steam_system);
;
app.run(); app.run();
} }
// fn steam_system(steam_client: Res<Client>) {
// for friend in steam_client.friends().get_friends(FriendFlags::IMMEDIATE) {
// println!(
// "Friend: {:?} - {}({:?})",
// friend.id(),
// friend.name(),
// friend.state()
// );
// }
// }

View File

@ -1,14 +1,19 @@
use bevy::prelude::*; use bevy::prelude::*;
use lightyear::prelude::ServerConnectEvent; use lightyear::prelude::{server::ServerCommands, ServerConnectEvent};
pub struct MyServerPlugin; pub struct MyServerPlugin;
impl Plugin for MyServerPlugin { impl Plugin for MyServerPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Update, handle_events); app.add_systems(Startup, start_server)
.add_systems(Update, handle_events);
} }
} }
pub fn start_server(mut commands: Commands) {
commands.start_server();
}
pub fn handle_events(mut er_server_events: EventReader<ServerConnectEvent>) { pub fn handle_events(mut er_server_events: EventReader<ServerConnectEvent>) {
for server_connect in er_server_events.read() { for server_connect in er_server_events.read() {
println!("Server Connect: {:?}", server_connect); println!("Server Connect: {:?}", server_connect);

View File

@ -1,9 +1,9 @@
use avian3d::{ use avian3d::{
math::{Scalar, Vector}, math::{Scalar, Vector},
prelude::Collider, prelude::{Collider, PhysicsSet},
}; };
use bevy::prelude::*; use bevy::prelude::*;
use bevy_dolly::{prelude::Rig, system::Dolly}; use bevy_dolly::prelude::Rig;
use blenvy::{BlueprintInfo, GameWorldTag, HideUntilReady, SpawnBlueprint}; use blenvy::{BlueprintInfo, GameWorldTag, HideUntilReady, SpawnBlueprint};
use crate::char_controller::CharacterControllerBundle; use crate::char_controller::CharacterControllerBundle;
@ -27,16 +27,22 @@ impl Plugin for SetupPlugin {
app.register_type::<Player>() app.register_type::<Player>()
.register_type::<PlayerHead>() .register_type::<PlayerHead>()
.add_systems(Startup, setup) .add_systems(Startup, setup)
// .add_systems(
// Update,
// (update_camera_rig, Dolly::<MainCamera>::update_active).chain(),
// )
.add_systems( .add_systems(
Update, PostUpdate,
(update_camera_rig, Dolly::<MainCamera>::update_active), update_camera_location
) .after(PhysicsSet::Sync)
// TODO: change to component hooks .before(TransformSystem::TransformPropagate),
.add_systems(Update, setup_player); );
} }
} }
fn setup(mut commands: Commands) { fn setup(world: &mut World) {
let mut commands = world.commands();
use bevy_dolly::prelude::*; use bevy_dolly::prelude::*;
commands.spawn(( commands.spawn((
MainCamera, // The rig component tag MainCamera, // The rig component tag
@ -45,9 +51,9 @@ fn setup(mut commands: Commands) {
// Adds a driver for rotation // Adds a driver for rotation
.with(Rotation::new(Quat::IDENTITY)) .with(Rotation::new(Quat::IDENTITY))
// Interpolation when the translation is updated, also known as smoothing // Interpolation when the translation is updated, also known as smoothing
.with(Smooth::new_position(0.3)) // .with(Smooth::new_position(0.03))
// Interpolation when the rotation is updated (updated via the YawPitch driver) // Interpolation when the rotation is updated (updated via the YawPitch driver)
.with(Smooth::new_rotation(0.3)) // .with(Smooth::new_rotation(0.03))
// Moves the camera point out in the Z direction and uses the position as the pivot // Moves the camera point out in the Z direction and uses the position as the pivot
// .with(Arm::new(Vec3::Z * 4.0)) // .with(Arm::new(Vec3::Z * 4.0))
.build(), .build(),
@ -92,23 +98,26 @@ fn setup(mut commands: Commands) {
HideUntilReady, HideUntilReady,
GameWorldTag, GameWorldTag,
)); ));
}
fn setup_player( #[cfg(feature = "client")]
mut commands: Commands, commands.spawn((
q_player: Query<Entity, (With<Player>, Without<Collider>)>, BlueprintInfo::from_path("blueprints/Player.glb"),
) { SpawnBlueprint,
let Ok(player_entity) = q_player.get_single() else { HideUntilReady,
return; GameWorldTag,
}; ));
commands.entity(player_entity).insert( world
.register_component_hooks::<Player>()
.on_add(|mut world, e, _| {
world.commands().entity(e).insert(
CharacterControllerBundle::new( CharacterControllerBundle::new(
Collider::capsule_endpoints(1., Vec3::Y, Vec3::Y * 3.), Collider::capsule_endpoints(1., Vec3::Y, Vec3::Y * 3.),
Vector::NEG_Y * 9.81 * 2.0, Vector::NEG_Y * 9.81 * 2.0,
) )
.with_movement(30.0, 0.92, 7.0, (30.0 as Scalar).to_radians()), .with_movement(30.0, 0.92, 7.0, (30.0 as Scalar).to_radians()),
); );
});
} }
fn update_camera_rig( fn update_camera_rig(
@ -129,3 +138,21 @@ fn update_camera_rig(
d_pos.position = pos; d_pos.position = pos;
} }
} }
fn update_camera_location(
q_player_head: Query<&GlobalTransform, (With<PlayerHead>, Without<Camera>)>,
mut q_camera: Query<&mut Transform, (With<Camera>, With<MainCamera>)>,
) {
let Ok(player_head_transform) = q_player_head.get_single() else {
warn!("No player head!");
return;
};
let Ok(mut camera_transform) = q_camera.get_single_mut() else {
warn!("No main camera!");
return;
};
let (_, rot, pos) = player_head_transform.to_scale_rotation_translation();
camera_transform.translation = pos;
camera_transform.rotation = rot;
}

View File

@ -6,17 +6,19 @@ use std::time::Duration;
pub const PRIVIATE_KEY: Key = [12; 32]; pub const PRIVIATE_KEY: Key = [12; 32];
pub const FIXED_TIMESTEP_HZ: f64 = 64.0; pub const FIXED_TIMESTEP_HZ: f64 = 64.0;
pub const REPLICATION_INTERVAL: Duration = Duration::from_millis(100); pub const REPLICATION_INTERVAL: Duration = Duration::from_millis(100);
pub const LOCAL_IP: &str = "127.0.0.1:7000";
pub const ANY_IP: &str = "0.0.0.0:0";
pub fn client_config() -> ClientConfig { pub fn client_config() -> ClientConfig {
let auth = Authentication::Manual { let auth = Authentication::Manual {
server_addr: "127.0.0.1:7000".parse().unwrap(), server_addr: "127.0.0.1:7001".parse().unwrap(),
client_id: 2, client_id: 2,
private_key: PRIVIATE_KEY, private_key: PRIVIATE_KEY,
protocol_id: 0, protocol_id: 0,
}; };
let netcode_config = client::NetcodeConfig::default(); let netcode_config = client::NetcodeConfig::default();
let io_config = client::IoConfig { let io_config = client::IoConfig {
transport: client::ClientTransport::UdpSocket("127.0.0.1:7000".parse().unwrap()), transport: client::ClientTransport::UdpSocket(ANY_IP.parse().unwrap()),
conditioner: None, conditioner: None,
compression: CompressionConfig::default(), compression: CompressionConfig::default(),
}; };
@ -33,9 +35,13 @@ pub fn client_config() -> ClientConfig {
} }
pub fn server_config() -> ServerConfig { pub fn server_config() -> ServerConfig {
let netcode_config = server::NetcodeConfig::default(); let netcode_config = server::NetcodeConfig {
protocol_id: 0,
private_key: PRIVIATE_KEY,
..Default::default()
};
let io_config = server::IoConfig { let io_config = server::IoConfig {
transport: server::ServerTransport::UdpSocket("127.0.0.1:7000".parse().unwrap()), transport: server::ServerTransport::UdpSocket(LOCAL_IP.parse().unwrap()),
conditioner: None, conditioner: None,
compression: CompressionConfig::default(), compression: CompressionConfig::default(),
}; };