diff --git a/Cargo.lock b/Cargo.lock index 1056986..117b02f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -954,6 +954,7 @@ dependencies = [ "rayon", "serde_json", "shipyard", + "static_assertions", "strum", "uflow", "winapi", diff --git a/Cargo.toml b/Cargo.toml index cfbd2e8..54fa66e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,9 @@ opt-level = 1 [profile.dev.package."*"] opt-level = 1 +[profile.dev.package.uflow] +opt-level = 3 + [profile.dev.package.glium] opt-level = 3 diff --git a/kubi-server/Cargo.toml b/kubi-server/Cargo.toml index 3cc19b3..44bec47 100644 --- a/kubi-server/Cargo.toml +++ b/kubi-server/Cargo.toml @@ -8,7 +8,7 @@ publish = false kubi-shared = { path = "../kubi-shared" } kubi-logging = { path = "../kubi-logging" } log = "*" -shipyard = { git = "https://github.com/leudz/shipyard", rev = "a4f4d27edcf", features = ["thread_local"] } +shipyard = { git = "https://github.com/leudz/shipyard", rev = "a4f4d27edcf", default-features = false, features = ["std", "proc", "thread_local"] } serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] } toml = "0.7" glam = { version = "0.23", features = ["debug-glam-assert", "fast-math"] } @@ -23,6 +23,7 @@ postcard = { version = "1.0", features = ["alloc"] } lz4_flex = { version = "0.10", default-features = false, features = ["std", "checked-decode"] } [features] -default = [] +default = ["parallel"] +parallel = ["shipyard/parallel"] safe_lz4 = ["lz4_flex/safe-encode", "lz4_flex/safe-decode"] nightly = ["hashbrown/nightly", "rand/nightly", "rand/simd_support", "glam/core-simd", "kubi-shared/nightly"] diff --git a/kubi-server/src/client.rs b/kubi-server/src/client.rs index 3c1d849..d94bcdc 100644 --- a/kubi-server/src/client.rs +++ b/kubi-server/src/client.rs @@ -1,11 +1,13 @@ use glam::Mat4; -use shipyard::{Component, EntityId, Unique, AllStoragesView, UniqueView, NonSendSync, View, ViewMut, Get}; +use shipyard::{Component, EntityId, Unique, AllStoragesView, UniqueView, NonSendSync, View, ViewMut, Get, IntoIter}; use hashbrown::HashMap; +use uflow::SendMode; use std::net::SocketAddr; use kubi_shared::{ networking::{ client::{ClientIdMap, Client}, - messages::{ClientToServerMessage, C_POSITION_CHANGED} + messages::{ClientToServerMessage, ServerToClientMessage, C_POSITION_CHANGED}, + channels::CHANNEL_MOVE }, transform::Transform }; @@ -35,7 +37,8 @@ pub fn sync_client_positions( events: UniqueView, addr_map: UniqueView, clients: View, - mut transforms: ViewMut + mut transforms: ViewMut, + addrs: View, ) { for event in &events.0 { let Some(message) = check_message_auth::(&server, event, &clients, &addr_map) else { @@ -44,9 +47,34 @@ pub fn sync_client_positions( let ClientToServerMessage::PositionChanged { position, velocity: _, direction } = message.message else { unreachable!() }; - //Apply position to client + + //log movement (annoying duh) + log::debug!("dbg: player moved id: {} coords: {} quat: {}", message.client_id, position, direction); + + //Apply position to server-side client let mut trans = (&mut transforms).get(message.entity_id).unwrap(); trans.0 = Mat4::from_rotation_translation(direction, position); + //Transmit the change to other players + for (other_client, other_client_address) in (&clients, &addrs).iter() { + if other_client.0 == message.client_id { + continue + } + let Some(client) = server.0.client(&other_client_address.0) else { + log::error!("Client with address not found"); + continue + }; + client.borrow_mut().send( + postcard::to_allocvec( + &ServerToClientMessage::PlayerPositionChanged { + client_id: message.client_id, + position, + direction + } + ).unwrap().into_boxed_slice(), + CHANNEL_MOVE, + SendMode::Reliable + ); + } } } diff --git a/kubi-server/src/main.rs b/kubi-server/src/main.rs index 3878e8e..476b81d 100644 --- a/kubi-server/src/main.rs +++ b/kubi-server/src/main.rs @@ -10,7 +10,7 @@ mod auth; use config::read_config; use server::{bind_server, update_server, log_server_errors}; -use client::init_client_maps; +use client::{init_client_maps, sync_client_positions}; use auth::authenticate_players; use world::{update_world, init_world}; @@ -30,6 +30,7 @@ fn update() -> Workload { log_server_errors, authenticate_players, update_world, + sync_client_positions, ).into_workload() ).into_sequential_workload() } diff --git a/kubi-server/src/server.rs b/kubi-server/src/server.rs index 6d5339f..3195677 100644 --- a/kubi-server/src/server.rs +++ b/kubi-server/src/server.rs @@ -35,7 +35,7 @@ pub fn bind_server( endpoint_config: EndpointConfig { active_timeout_ms: config.server.timeout_ms, keepalive: true, - keepalive_interval_ms: 1000, + keepalive_interval_ms: 300, ..Default::default() }, ..Default::default() diff --git a/kubi/Cargo.toml b/kubi/Cargo.toml index 5dba603..1786767 100644 --- a/kubi/Cargo.toml +++ b/kubi/Cargo.toml @@ -23,6 +23,7 @@ uflow = "0.7" postcard = { version = "1.0", features = ["alloc"] } serde_json = { version = "1.0", optional = true } lz4_flex = { version = "0.10", default-features = false, features = ["std", "checked-decode"] } +static_assertions = "1.1" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3" } @@ -32,4 +33,4 @@ default = [] generate_visualizer_data = ["serde_json", "shipyard/serde1"] safe_lz4 = ["lz4_flex/safe-encode", "lz4_flex/safe-decode"] parallel = ["shipyard/parallel"] -nightly = ["hashbrown/nightly", "glam/core-simd", "kubi-shared/nightly"] +nightly = ["hashbrown/nightly", "glam/core-simd", "static_assertions/nightly", "kubi-shared/nightly"] diff --git a/kubi/shaders/colored.frag b/kubi/shaders/colored.frag index b7a0615..7ab85c6 100644 --- a/kubi/shaders/colored.frag +++ b/kubi/shaders/colored.frag @@ -6,5 +6,7 @@ out vec4 out_color; uniform vec4 color; void main() { + // discard fully transparent pixels + if (color.w <= 0.) discard; out_color = color; } diff --git a/kubi/shaders/selection_box.frag b/kubi/shaders/selection_box.frag deleted file mode 100644 index 5311d6f..0000000 --- a/kubi/shaders/selection_box.frag +++ /dev/null @@ -1,11 +0,0 @@ -#version 300 es - -precision highp float; - -out vec4 color; -uniform vec4 u_color; - -void main() { - color = u_color; - // color -= vec4(0, 0, 0, 0.1 * sin(gl_FragCoord.x) * cos(gl_FragCoord.y)); -} diff --git a/kubi/shaders/selection_box.vert b/kubi/shaders/selection_box.vert deleted file mode 100644 index 049488d..0000000 --- a/kubi/shaders/selection_box.vert +++ /dev/null @@ -1,12 +0,0 @@ -#version 300 es - -precision highp float; - -in vec3 position; -uniform ivec3 u_position; -uniform mat4 perspective; -uniform mat4 view; - -void main() { - gl_Position = perspective * view * vec4(position + vec3(u_position), 1.); -} diff --git a/kubi/shaders/world.frag b/kubi/shaders/world.frag index ba9f184..59ae56b 100644 --- a/kubi/shaders/world.frag +++ b/kubi/shaders/world.frag @@ -19,20 +19,8 @@ uniform sampler2DArray tex; void main() { // base color from texture color = texture(tex, vec3(v_uv, v_tex_index)); - // HACKY texture "antialiasing" - // color += ( - // alpha_drop(color, texture(tex, vec3(v_uv + vec2(.000, .001), v_tex_index))) + - // alpha_drop(color, texture(tex, vec3(v_uv + vec2(.001, .000), v_tex_index))) + - // alpha_drop(color, texture(tex, vec3(v_uv + vec2(.001, .001), v_tex_index))) + - // alpha_drop(color, texture(tex, vec3(v_uv - vec2(.000, .001), v_tex_index))) + - // alpha_drop(color, texture(tex, vec3(v_uv - vec2(.001, .000), v_tex_index))) + - // alpha_drop(color, texture(tex, vec3(v_uv - vec2(.001, .001), v_tex_index))) - // ) / 6.; - // color /= 2.; // discard fully transparent pixels - if (color.w <= 0.0) { - discard; - } + if (color.w <= 0.0) discard; //basic "lighting" float light = abs(v_normal.x) + .8 * abs(v_normal.y) + .6 * abs(v_normal.z); color *= vec4(vec3(light), 1.); diff --git a/kubi/src/block_placement.rs b/kubi/src/block_placement.rs index 110d7a8..434b55c 100644 --- a/kubi/src/block_placement.rs +++ b/kubi/src/block_placement.rs @@ -63,7 +63,7 @@ fn block_placement_system( (ray.block_position, Block::Air) }; //queue place - block_event_queue.push(QueuedBlock { + block_event_queue.0.push(QueuedBlock { position: place_position, block_type: place_block, soft: place_block != Block::Air, diff --git a/kubi/src/main.rs b/kubi/src/main.rs index b15f056..2196dc1 100644 --- a/kubi/src/main.rs +++ b/kubi/src/main.rs @@ -69,9 +69,9 @@ use rendering::{ init_window_size, update_window_size, primitives::init_primitives, + world::{draw_world, draw_current_chunk_border}, selection_box::render_selection_box, - world::draw_world, - world::draw_current_chunk_border, + entities::render_entities, }; use block_placement::update_block_placement; use delta_time::{DeltaTime, init_delta_time}; @@ -107,6 +107,7 @@ fn startup() -> Workload { init_delta_time, ).into_sequential_workload() } + fn update() -> Workload { ( update_window_size, @@ -143,6 +144,7 @@ fn update() -> Workload { disconnect_on_exit.run_if(is_multiplayer), ).into_sequential_workload() } + fn render() -> Workload { ( clear_background, @@ -150,10 +152,12 @@ fn render() -> Workload { draw_world, draw_current_chunk_border, render_selection_box, + render_entities, ).into_sequential_workload().run_if(is_ingame), render_gui, ).into_sequential_workload() } + fn after_frame_end() -> Workload { ( clear_events, diff --git a/kubi/src/networking.rs b/kubi/src/networking.rs index f2626fc..fffa723 100644 --- a/kubi/src/networking.rs +++ b/kubi/src/networking.rs @@ -1,7 +1,10 @@ use shipyard::{Unique, AllStoragesView, UniqueView, UniqueViewMut, Workload, IntoWorkload, EntitiesViewMut, Component, ViewMut, SystemModificator, View, IntoIter, WorkloadModificator}; use glium::glutin::event_loop::ControlFlow; use std::net::SocketAddr; -use uflow::{client::{Client, Config as ClientConfig, Event as ClientEvent}, EndpointConfig}; +use uflow::{ + client::{Client, Config as ClientConfig, Event as ClientEvent}, + EndpointConfig +}; use kubi_shared::networking::{ messages::ServerToClientMessage, state::ClientJoinState, @@ -71,7 +74,7 @@ fn connect_client( endpoint_config: EndpointConfig { active_timeout_ms: 10000, keepalive: true, - keepalive_interval_ms: 1000, + keepalive_interval_ms: 300, ..Default::default() }, }).expect("Client connection failed"); @@ -128,8 +131,8 @@ pub fn update_networking() -> Workload { ).into_sequential_workload().run_if(is_join_state::<{ClientJoinState::Connected as u8}>), ( ( - receive_player_connect_events - ), + receive_player_connect_events, + ).into_workload(), ( recv_block_place_events, receive_player_movement_events, diff --git a/kubi/src/networking/player.rs b/kubi/src/networking/player.rs index e2b3abf..6d6e43d 100644 --- a/kubi/src/networking/player.rs +++ b/kubi/src/networking/player.rs @@ -4,7 +4,7 @@ use uflow::{SendMode, client::Event as ClientEvent}; use kubi_shared::{ transform::Transform, networking::{ - messages::{ClientToServerMessage, ServerToClientMessage, S_PLAYER_POSITION_CHANGED}, + messages::{ClientToServerMessage, ServerToClientMessage, S_PLAYER_POSITION_CHANGED, S_PLAYER_CONNECTED}, channels::CHANNEL_MOVE, client::ClientIdMap, }, @@ -83,7 +83,7 @@ pub fn receive_player_connect_events( let ClientEvent::Receive(data) = &event.0 else { return None }; - if !event.is_message_of_type::() { + if !event.is_message_of_type::() { return None }; let Ok(parsed_message) = postcard::from_bytes(data) else { diff --git a/kubi/src/networking/world.rs b/kubi/src/networking/world.rs index 56cab52..c04755c 100644 --- a/kubi/src/networking/world.rs +++ b/kubi/src/networking/world.rs @@ -86,6 +86,6 @@ pub fn recv_block_place_events( let ServerToClientMessage::QueueBlock { item } = parsed_message else { unreachable!() }; - queue.push(item); + queue.0.push(item); } } diff --git a/kubi/src/prefabs.rs b/kubi/src/prefabs.rs index ba16e81..a469cab 100644 --- a/kubi/src/prefabs.rs +++ b/kubi/src/prefabs.rs @@ -37,18 +37,19 @@ impl AssetPaths for BlockTexture { } #[derive(Unique)] +#[repr(transparent)] pub struct BlockTexturesPrefab(pub SrgbTexture2dArray); #[derive(Unique)] +#[repr(transparent)] pub struct ChunkShaderPrefab(pub Program); #[derive(Unique)] -pub struct SelBoxShaderPrefab(pub Program); - -#[derive(Unique)] +#[repr(transparent)] pub struct ColoredShaderPrefab(pub Program); #[derive(Unique)] +#[repr(transparent)] pub struct ProgressbarShaderPrefab(pub Program); pub fn load_prefabs( @@ -73,14 +74,6 @@ pub fn load_prefabs( &renderer.display ) )); - storages.add_unique_non_send_sync(SelBoxShaderPrefab( - include_shader_prefab!( - "selection_box", - "../shaders/selection_box.vert", - "../shaders/selection_box.frag", - &renderer.display - ) - )); storages.add_unique_non_send_sync(ColoredShaderPrefab( include_shader_prefab!( "colored", diff --git a/kubi/src/rendering.rs b/kubi/src/rendering.rs index 9a30bba..efb215e 100644 --- a/kubi/src/rendering.rs +++ b/kubi/src/rendering.rs @@ -14,14 +14,18 @@ use crate::{events::WindowResizedEvent, settings::{GameSettings, FullscreenMode} pub mod primitives; pub mod world; pub mod selection_box; +pub mod entities; #[derive(Unique)] +#[repr(transparent)] pub struct RenderTarget(pub glium::Frame); #[derive(Unique)] +#[repr(transparent)] pub struct BackgroundColor(pub Vec3); #[derive(Unique, Clone, Copy)] +#[repr(transparent)] pub struct WindowSize(pub UVec2); #[derive(Unique)] @@ -32,7 +36,6 @@ impl Renderer { pub fn init(event_loop: &EventLoop<()>, settings: &GameSettings) -> Self { log::info!("initializing display"); - let wb = WindowBuilder::new() .with_title("kubi") .with_maximized(true) diff --git a/kubi/src/rendering/entities.rs b/kubi/src/rendering/entities.rs new file mode 100644 index 0000000..94075a6 --- /dev/null +++ b/kubi/src/rendering/entities.rs @@ -0,0 +1,59 @@ +use shipyard::{NonSendSync, UniqueViewMut, UniqueView, View, IntoIter, IntoWithId}; +use glium::{DepthTest, Depth, PolygonMode, BackfaceCullingMode, DrawParameters, Surface, uniform}; +use kubi_shared::{entity::Entity, transform::Transform}; +use crate::{ + prefabs::ColoredShaderPrefab, + camera::Camera, + settings::GameSettings, + player::MainPlayer +}; +use super::{ + RenderTarget, + primitives::cube::CubePrimitive +}; + +// TODO: entity models +pub fn render_entities( + mut target: NonSendSync>, + buffers: NonSendSync>, + program: NonSendSync>, + camera: View, + settings: UniqueView, + entities: View, + transform: View, +) { + let (camera_id, camera) = camera.iter().with_id().next().expect("No cameras in the scene"); + + let draw_parameters = DrawParameters { + depth: Depth { + test: DepthTest::IfLess, + write: true, + ..Default::default() + }, + multisampling: settings.msaa.is_some(), + polygon_mode: PolygonMode::Fill, + backface_culling: BackfaceCullingMode::CullClockwise, + ..Default::default() + }; + let view = camera.view_matrix.to_cols_array_2d(); + let perspective = camera.perspective_matrix.to_cols_array_2d(); + + for (entity_id, (_, trans)) in (&entities, &transform).iter().with_id() { + //skip rendering camera holder (as the entity would block the view) + if entity_id == camera_id { continue } + + //render entity + target.0.draw( + &buffers.0, + &buffers.1, + &program.0, + &uniform! { + color: [1.0, 1.0, 1.0, 1.0_f32], + model: trans.0.to_cols_array_2d(), + view: view, + perspective: perspective, + }, + &draw_parameters + ).unwrap(); + } +} diff --git a/kubi/src/rendering/selection_box.rs b/kubi/src/rendering/selection_box.rs index 1c8cc5a..c6e8586 100644 --- a/kubi/src/rendering/selection_box.rs +++ b/kubi/src/rendering/selection_box.rs @@ -1,3 +1,4 @@ +use glam::{Mat4, Vec3, Quat}; use shipyard::{View, IntoIter, NonSendSync, UniqueViewMut, UniqueView}; use glium::{ Surface, @@ -8,18 +9,21 @@ use glium::{ }; use crate::{ world::raycast::LookingAtBlock, - camera::Camera, prefabs::SelBoxShaderPrefab + camera::Camera, + prefabs::ColoredShaderPrefab }; use super::{ RenderTarget, primitives::cube::CubePrimitive, }; +const SMOL: f32 = 0.0001; + pub fn render_selection_box( lookat: View, camera: View, mut target: NonSendSync>, - program: NonSendSync>, + program: NonSendSync>, buffers: NonSendSync>, ) { let camera = camera.iter().next().unwrap(); @@ -32,8 +36,12 @@ pub fn render_selection_box( &buffers.1, &program.0, &uniform! { - u_color: [0., 0., 0., 0.5_f32], - u_position: lookat.block_position.to_array(), + color: [0., 0., 0., 0.5_f32], + model: Mat4::from_scale_rotation_translation( + Vec3::splat(1. + SMOL * 2.), + Quat::default(), + lookat.block_position.as_vec3() - Vec3::splat(SMOL) + ).to_cols_array_2d(), perspective: camera.perspective_matrix.to_cols_array_2d(), view: camera.view_matrix.to_cols_array_2d(), }, @@ -41,7 +49,8 @@ pub fn render_selection_box( backface_culling: BackfaceCullingMode::CullClockwise, blend: Blend::alpha_blending(), depth: Depth { - test: DepthTest::IfLessOrEqual, //this may be unreliable! + //this may be unreliable... unless scale is applied! hacky... + test: DepthTest::IfLessOrEqual, ..Default::default() }, ..Default::default() diff --git a/kubi/src/world/loading.rs b/kubi/src/world/loading.rs index 88e9ce1..f8ab0a0 100644 --- a/kubi/src/world/loading.rs +++ b/kubi/src/world/loading.rs @@ -191,7 +191,7 @@ fn process_completed_tasks( let mut ops: usize = 0; while let Some(res) = task_manager.receive() { match res { - ChunkTaskResponse::LoadedChunk { position, chunk_data, queued } => { + ChunkTaskResponse::LoadedChunk { position, chunk_data, mut queued } => { //check if chunk exists let Some(chunk) = world.chunks.get_mut(&position) else { log::warn!("blocks data discarded: chunk doesn't exist"); @@ -213,10 +213,8 @@ fn process_completed_tasks( chunk.current_state = CurrentChunkState::Loaded; //push queued blocks - //TODO use extend - for item in queued { - queue.push(item); - } + queue.0.append(&mut queued); + drop(queued); //`queued` is empty after `append` //increase ops counter ops += 1; diff --git a/kubi/src/world/queue.rs b/kubi/src/world/queue.rs index eb9f346..76d6b02 100644 --- a/kubi/src/world/queue.rs +++ b/kubi/src/world/queue.rs @@ -4,16 +4,12 @@ use shipyard::{UniqueViewMut, Unique}; use super::ChunkStorage; #[derive(Unique, Default, Clone)] -pub struct BlockUpdateQueue { - queue: Vec -} +#[repr(transparent)] +pub struct BlockUpdateQueue(pub Vec); impl BlockUpdateQueue { pub fn new() -> Self { Self::default() } - pub fn push(&mut self, event: QueuedBlock) { - self.queue.push(event) - } } pub fn apply_queued_blocks( @@ -21,7 +17,7 @@ pub fn apply_queued_blocks( mut world: UniqueViewMut ) { //maybe i need to check for desired/current state here before marking as dirty? - queue.queue.retain(|&event| { + queue.0.retain(|&event| { if let Some(block) = world.get_block_mut(event.position) { if event.soft && *block != Block::Air { return false