player position sync, refactor some stuff

This commit is contained in:
griffi-gh 2023-05-20 02:32:32 +02:00
parent 73940d61ff
commit 75c6d127b7
22 changed files with 151 additions and 84 deletions

1
Cargo.lock generated
View file

@ -954,6 +954,7 @@ dependencies = [
"rayon",
"serde_json",
"shipyard",
"static_assertions",
"strum",
"uflow",
"winapi",

View file

@ -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

View file

@ -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"]

View file

@ -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<ServerEvents>,
addr_map: UniqueView<ClientAddressMap>,
clients: View<Client>,
mut transforms: ViewMut<Transform>
mut transforms: ViewMut<Transform>,
addrs: View<ClientAddress>,
) {
for event in &events.0 {
let Some(message) = check_message_auth::<C_POSITION_CHANGED>(&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
);
}
}
}

View file

@ -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()
}

View file

@ -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()

View file

@ -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"]

View file

@ -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;
}

View file

@ -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));
}

View file

@ -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.);
}

View file

@ -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.);

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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::<S_PLAYER_POSITION_CHANGED>() {
if !event.is_message_of_type::<S_PLAYER_CONNECTED>() {
return None
};
let Ok(parsed_message) = postcard::from_bytes(data) else {

View file

@ -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);
}
}

View file

@ -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",

View file

@ -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)

View file

@ -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<UniqueViewMut<RenderTarget>>,
buffers: NonSendSync<UniqueView<CubePrimitive>>,
program: NonSendSync<UniqueView<ColoredShaderPrefab>>,
camera: View<Camera>,
settings: UniqueView<GameSettings>,
entities: View<Entity>,
transform: View<Transform>,
) {
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();
}
}

View file

@ -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<LookingAtBlock>,
camera: View<Camera>,
mut target: NonSendSync<UniqueViewMut<RenderTarget>>,
program: NonSendSync<UniqueView<SelBoxShaderPrefab>>,
program: NonSendSync<UniqueView<ColoredShaderPrefab>>,
buffers: NonSendSync<UniqueView<CubePrimitive>>,
) {
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()

View file

@ -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;

View file

@ -4,16 +4,12 @@ use shipyard::{UniqueViewMut, Unique};
use super::ChunkStorage;
#[derive(Unique, Default, Clone)]
pub struct BlockUpdateQueue {
queue: Vec<QueuedBlock>
}
#[repr(transparent)]
pub struct BlockUpdateQueue(pub Vec<QueuedBlock>);
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<ChunkStorage>
) {
//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