This commit is contained in:
griffi-gh 2023-05-19 13:22:32 +02:00
parent 8d7b51707f
commit b18756bb7e
11 changed files with 203 additions and 144 deletions

View file

@ -33,6 +33,7 @@ pub fn authenticate_players(
let config = storages.borrow::<UniqueView<ConfigTable>>().unwrap(); let config = storages.borrow::<UniqueView<ConfigTable>>().unwrap();
for event in &events.0 { for event in &events.0 {
// NOT using `check_message_auth` here because the user is not authed yet!
let ServerEvent::Receive(client_addr, data) = event else{ let ServerEvent::Receive(client_addr, data) = event else{
continue continue
}; };

View file

@ -1,12 +1,18 @@
use shipyard::{Component, EntityId, Unique, AllStoragesView, UniqueView, NonSendSync, View, Get}; use glam::Mat4;
use shipyard::{Component, EntityId, Unique, AllStoragesView, UniqueView, NonSendSync, View, ViewMut, Get};
use hashbrown::HashMap; use hashbrown::HashMap;
use std::net::SocketAddr; use std::net::SocketAddr;
use uflow::server::Event as ServerEvent; use kubi_shared::{
use kubi_shared::networking::{ networking::{
client::{ClientIdMap, Client}, client::{ClientIdMap, Client},
messages::{ClientToServerMessage, C_POSITION_CHANGED} messages::{ClientToServerMessage, C_POSITION_CHANGED}
},
transform::Transform
};
use crate::{
server::{ServerEvents, UdpServer},
util::check_message_auth
}; };
use crate::server::{ServerEvents, IsMessageOfType, UdpServer};
#[derive(Component, Clone, Copy)] #[derive(Component, Clone, Copy)]
pub struct ClientAddress(pub SocketAddr); pub struct ClientAddress(pub SocketAddr);
@ -29,34 +35,18 @@ pub fn sync_client_positions(
events: UniqueView<ServerEvents>, events: UniqueView<ServerEvents>,
addr_map: UniqueView<ClientAddressMap>, addr_map: UniqueView<ClientAddressMap>,
clients: View<Client>, clients: View<Client>,
mut transforms: ViewMut<Transform>
) { ) {
for event in &events.0 { for event in &events.0 {
let ServerEvent::Receive(client_addr, data) = event else{ let Some(message) = check_message_auth::<C_POSITION_CHANGED>(&server, event, &clients, &addr_map) else {
continue continue;
}; };
if !event.is_message_of_type::<C_POSITION_CHANGED>() { let ClientToServerMessage::PositionChanged { position, velocity: _, direction } = message.message else {
continue
}
let Some(client) = server.0.client(client_addr) else {
log::error!("Client doesn't exist");
continue
};
let Some(&entity_id) = addr_map.0.get(client_addr) else {
log::error!("Client not authenticated");
continue
};
let Ok(&Client(client_id)) = (&clients).get(entity_id) else {
log::error!("Entity ID is invalid");
continue
};
let Ok(parsed_message) = postcard::from_bytes(data) else {
log::error!("Malformed message");
continue
};
let ClientToServerMessage::PositionChanged { position, velocity, direction } = parsed_message else {
unreachable!() unreachable!()
}; };
//Apply position to client
let mut trans = (&mut transforms).get(message.entity_id).unwrap();
trans.0 = Mat4::from_rotation_translation(direction, position);
//TODO: sync positions on server
} }
} }

View file

@ -1,6 +1,7 @@
use shipyard::{World, Workload, IntoWorkload}; use shipyard::{World, Workload, IntoWorkload};
use std::{thread, time::Duration}; use std::{thread, time::Duration};
mod util;
mod config; mod config;
mod server; mod server;
mod client; mod client;

72
kubi-server/src/util.rs Normal file
View file

@ -0,0 +1,72 @@
use std::{net::SocketAddr, rc::Rc, cell::RefCell};
use shipyard::{View, Get, EntityId};
use uflow::server::{Event as ServerEvent, RemoteClient};
use kubi_shared::networking::{
messages::ClientToServerMessage,
client::{Client, ClientId}
};
use crate::{
server::{IsMessageOfType, UdpServer},
client::ClientAddressMap
};
#[derive(Clone)]
pub struct CtsMessageMetadata<'a> {
pub message: ClientToServerMessage,
pub client_id: ClientId,
pub entity_id: EntityId,
pub client_addr: SocketAddr,
pub client: &'a Rc<RefCell<RemoteClient>>,
}
impl From<CtsMessageMetadata<'_>> for ClientToServerMessage {
fn from(value: CtsMessageMetadata) -> Self { value.message }
}
impl From<CtsMessageMetadata<'_>> for ClientId {
fn from(value: CtsMessageMetadata) -> Self { value.client_id }
}
impl From<CtsMessageMetadata<'_>> for EntityId {
fn from(value: CtsMessageMetadata) -> Self { value.entity_id }
}
impl From<CtsMessageMetadata<'_>> for SocketAddr {
fn from(value: CtsMessageMetadata) -> Self { value.client_addr }
}
impl<'a> From<CtsMessageMetadata<'a>> for &'a Rc<RefCell<RemoteClient>> {
fn from(value: CtsMessageMetadata<'a>) -> Self { value.client }
}
pub fn check_message_auth<'a, const C_MSG: u8>(
server: &'a UdpServer,
event: &ServerEvent,
clients: &View<Client>,
addr_map: &ClientAddressMap
) -> Option<CtsMessageMetadata<'a>> {
let ServerEvent::Receive(client_addr, data) = event else{
return None
};
if !event.is_message_of_type::<C_MSG>() {
return None
}
let Some(client) = server.0.client(client_addr) else {
log::error!("Client doesn't exist");
return None
};
let Some(&entity_id) = addr_map.0.get(client_addr) else {
log::error!("Client not authenticated");
return None
};
let Ok(&Client(client_id)) = (&clients).get(entity_id) else {
log::error!("Entity ID is invalid");
return None
};
let Ok(message) = postcard::from_bytes(data) else {
log::error!("Malformed message");
return None
};
Some(CtsMessageMetadata {
message,
client_id,
entity_id,
client_addr: *client_addr,
client
})
}

View file

@ -18,6 +18,7 @@ use crate::{
server::{UdpServer, ServerEvents, IsMessageOfType}, server::{UdpServer, ServerEvents, IsMessageOfType},
config::ConfigTable, config::ConfigTable,
client::{ClientAddress, ClientAddressMap}, client::{ClientAddress, ClientAddressMap},
util::check_message_auth,
}; };
pub mod chunk; pub mod chunk;
@ -61,38 +62,19 @@ fn process_chunk_requests(
clients: View<Client> clients: View<Client>
) { ) {
for event in &events.0 { for event in &events.0 {
let ServerEvent::Receive(client_addr, data) = event else{ let Some(message) = check_message_auth::<C_CHUNK_SUB_REQUEST>(&server, event, &clients, &addr_map) else {
continue continue;
}; };
if !event.is_message_of_type::<C_CHUNK_SUB_REQUEST>() { let ClientToServerMessage::ChunkSubRequest { chunk: chunk_position } = message.message else {
continue
}
let Some(client) = server.0.client(client_addr) else {
log::error!("Client doesn't exist");
continue
};
let Some(&entity_id) = addr_map.0.get(client_addr) else {
log::error!("Client not authenticated");
continue
};
let Ok(&Client(client_id)) = (&clients).get(entity_id) else {
log::error!("Entity ID is invalid");
continue
};
let Ok(parsed_message) = postcard::from_bytes(data) else {
log::error!("Malformed message");
continue
};
let ClientToServerMessage::ChunkSubRequest { chunk: chunk_position } = parsed_message else {
unreachable!() unreachable!()
}; };
if let Some(chunk) = chunk_manager.chunks.get_mut(&chunk_position) { if let Some(chunk) = chunk_manager.chunks.get_mut(&chunk_position) {
chunk.subscriptions.insert(client_id); chunk.subscriptions.insert(message.client_id);
//TODO Start task here if status is "Nothing" //TODO Start task here if status is "Nothing"
if let Some(blocks) = &chunk.blocks { if let Some(blocks) = &chunk.blocks {
send_chunk_compressed( send_chunk_compressed(
&client, &message.client,
&ServerToClientMessage::ChunkResponse { &ServerToClientMessage::ChunkResponse {
chunk: chunk_position, chunk: chunk_position,
data: blocks.clone(), data: blocks.clone(),
@ -103,7 +85,7 @@ fn process_chunk_requests(
} else { } else {
let mut chunk = Chunk::new(chunk_position); let mut chunk = Chunk::new(chunk_position);
chunk.state = ChunkState::Loading; chunk.state = ChunkState::Loading;
chunk.subscriptions.insert(client_id); chunk.subscriptions.insert(message.client_id);
chunk_manager.chunks.insert(chunk_position, chunk); chunk_manager.chunks.insert(chunk_position, chunk);
task_manager.spawn_task(ChunkTask::LoadChunk { task_manager.spawn_task(ChunkTask::LoadChunk {
position: chunk_position, position: chunk_position,
@ -172,32 +154,15 @@ fn process_block_queue_messages(
addrs: View<ClientAddress>, addrs: View<ClientAddress>,
) { ) {
for event in &events.0 { for event in &events.0 {
let ServerEvent::Receive(client_addr, data) = event else{ let Some(message) = check_message_auth::<C_QUEUE_BLOCK>(&server, event, &clients, &addr_map) else {
continue continue;
};
if !event.is_message_of_type::<C_QUEUE_BLOCK>() {
continue
}
let Some(&entity_id) = addr_map.0.get(client_addr) else {
log::error!("Client not authenticated");
continue
};
let Ok(&Client(client_id)) = (&clients).get(entity_id) else {
log::error!("Entity ID is invalid");
continue
};
let Ok(parsed_message) = postcard::from_bytes(data) else {
log::error!("Malformed message");
continue
};
let ClientToServerMessage::QueueBlock { item } = parsed_message else {
unreachable!()
}; };
let ClientToServerMessage::QueueBlock { item } = message.message else { unreachable!() };
//TODO place in our own queue, for now just send to other clients //TODO place in our own queue, for now just send to other clients
log::info!("Placed block {:?} at {}", item.block_type, item.position); log::info!("Placed block {:?} at {}", item.block_type, item.position);
for (other_client, other_client_address) in (&clients, &addrs).iter() { for (other_client, other_client_address) in (&clients, &addrs).iter() {
//No need to send the event back //No need to send the event back
if client_id == other_client.0 { if message.client_id == other_client.0 {
continue continue
} }
//Get client //Get client

View file

@ -9,9 +9,26 @@ flat in uint v_tex_index;
out vec4 color; out vec4 color;
uniform sampler2DArray tex; uniform sampler2DArray tex;
vec4 alpha_drop(vec4 b, vec4 a) {
if ((a.w < 1.) || (b.w < 1.)) {
return vec4(b.xyz, 0.);
}
return a;
}
void main() { void main() {
// base color from texture // base color from texture
color = texture(tex, vec3(v_uv, v_tex_index)); 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 // discard fully transparent pixels
if (color.w <= 0.0) { if (color.w <= 0.0) {
discard; discard;

View file

@ -78,7 +78,7 @@ use delta_time::{DeltaTime, init_delta_time};
use cursor_lock::{insert_lock_state, update_cursor_lock_state, lock_cursor_now}; use cursor_lock::{insert_lock_state, update_cursor_lock_state, lock_cursor_now};
use control_flow::{exit_on_esc, insert_control_flow_unique, SetControlFlow}; use control_flow::{exit_on_esc, insert_control_flow_unique, SetControlFlow};
use state::{is_ingame, is_ingame_or_loading, is_loading, init_state, update_state, is_connecting}; use state::{is_ingame, is_ingame_or_loading, is_loading, init_state, update_state, is_connecting};
use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit}; use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer};
use init::initialize_from_args; use init::initialize_from_args;
use gui::{render_gui, init_gui, update_gui}; use gui::{render_gui, init_gui, update_gui};
use loading_screen::update_loading_screen; use loading_screen::update_loading_screen;
@ -114,7 +114,9 @@ fn update() -> Workload {
process_inputs, process_inputs,
( (
init_game_world.run_if_missing_unique::<ChunkTaskManager>(), init_game_world.run_if_missing_unique::<ChunkTaskManager>(),
spawn_player.run_if_storage_empty::<MainPlayer>(), (
spawn_player.run_if_storage_empty::<MainPlayer>(),
).into_sequential_workload().run_if(is_singleplayer),
).into_sequential_workload().run_if(is_ingame_or_loading), ).into_sequential_workload().run_if(is_ingame_or_loading),
update_networking.run_if(is_multiplayer), update_networking.run_if(is_multiplayer),
( (

View file

@ -4,7 +4,8 @@ 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::{ use kubi_shared::networking::{
messages::ServerToClientMessage, messages::ServerToClientMessage,
state::ClientJoinState, state::ClientJoinState,
client::ClientIdMap,
}; };
use crate::{ use crate::{
events::EventComponent, events::EventComponent,
@ -27,7 +28,10 @@ use world::{
send_block_place_events, send_block_place_events,
recv_block_place_events, recv_block_place_events,
}; };
use player::send_player_movement_events; use player::{
init_client_map,
send_player_movement_events,
};
use self::player::{receive_player_movement_events, receive_player_connect_events}; use self::player::{receive_player_movement_events, receive_player_connect_events};
@ -111,6 +115,7 @@ fn handle_disconnect(
pub fn update_networking() -> Workload { pub fn update_networking() -> Workload {
( (
init_client_map.run_if_missing_unique::<ClientIdMap>(),
connect_client.run_if_missing_unique::<UdpClient>(), connect_client.run_if_missing_unique::<UdpClient>(),
poll_client, poll_client,
( (

View file

@ -1,17 +1,15 @@
use glam::Mat4; use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesViewMut};
use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesViewMut, ViewMut, IntoWithId};
use uflow::{client::Event as ClientEvent, SendMode}; use uflow::{client::Event as ClientEvent, SendMode};
use kubi_shared::{ use kubi_shared::{
networking::{ networking::{
messages::{ClientToServerMessage, ServerToClientMessage, S_SERVER_HELLO}, messages::{ClientToServerMessage, ServerToClientMessage, S_SERVER_HELLO},
state::ClientJoinState, state::ClientJoinState,
channels::CHANNEL_AUTH, channels::CHANNEL_AUTH,
client::{Username, Client}, client::Username,
}, },
transform::Transform, entity::Health
}; };
use crate::player::MainPlayer; use crate::player::{MainPlayer, spawn_local_player_multiplayer, spawn_remote_player_multiplayer};
use super::{UdpClient, NetworkEvent, player::add_net_player}; use super::{UdpClient, NetworkEvent};
pub fn set_client_join_state_to_connected( pub fn set_client_join_state_to_connected(
mut join_state: UniqueViewMut<ClientJoinState> mut join_state: UniqueViewMut<ClientJoinState>
@ -25,7 +23,7 @@ pub fn say_hello(
main_player: View<MainPlayer>, main_player: View<MainPlayer>,
username: View<Username> username: View<Username>
) { ) {
let username = (&main_player, &username).iter().next().unwrap().1.0.clone(); let username = "XxX-FishFucker-69420-XxX".into(); //(&main_player, &username).iter().next().unwrap().1.0.clone();
let password = None; let password = None;
log::info!("Authenticating"); log::info!("Authenticating");
client.0.send( client.0.send(
@ -68,28 +66,11 @@ pub fn check_server_hello_response(
// } // }
//Add components to main player //Add components to main player
{ spawn_local_player_multiplayer(&mut storages, init.user);
let entity = (&storages.borrow::<View<MainPlayer>>().unwrap()).iter().with_id().next().unwrap().0;
storages.add_component(entity, Client(init.user.client_id));
}
//Modify main player
{
for (entity, (_, mut username, mut transform, mut health)) in (
&storages.borrow::<View<MainPlayer>>().unwrap(),
&mut storages.borrow::<ViewMut<Username>>().unwrap(),
&mut storages.borrow::<ViewMut<Transform>>().unwrap(),
&mut storages.borrow::<ViewMut<Health>>().unwrap(),
).iter().with_id() {
username.0 = init.user.username.clone();
transform.0 = Mat4::from_rotation_translation(init.user.direction, init.user.position);
*health = init.user.health;
}
}
//Init players //Init players
for init_data in init.users { for init_data in init.users {
add_net_player(&mut storages, init_data); spawn_remote_player_multiplayer(&mut storages, init_data);
} }
// Set state to connected // Set state to connected

View file

@ -2,16 +2,17 @@ use glam::{Vec3, Mat4};
use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesView, AllStoragesViewMut, UniqueView, ViewMut, Get}; use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesView, AllStoragesViewMut, UniqueView, ViewMut, Get};
use uflow::{SendMode, client::Event as ClientEvent}; use uflow::{SendMode, client::Event as ClientEvent};
use kubi_shared::{ use kubi_shared::{
player::{Player, PlayerHolding},
entity::Entity,
transform::Transform, transform::Transform,
networking::{ networking::{
messages::{ClientToServerMessage, ServerToClientMessage, S_PLAYER_POSITION_CHANGED, ClientInitData}, messages::{ClientToServerMessage, ServerToClientMessage, S_PLAYER_POSITION_CHANGED},
channels::CHANNEL_MOVE, channels::CHANNEL_MOVE,
client::{ClientIdMap, Username, Client}, client::ClientIdMap,
}, },
}; };
use crate::events::player_actions::PlayerActionEvent; use crate::{
events::player_actions::PlayerActionEvent,
player::spawn_remote_player_multiplayer,
};
use super::{UdpClient, NetworkEvent}; use super::{UdpClient, NetworkEvent};
pub fn init_client_map( pub fn init_client_map(
@ -20,35 +21,6 @@ pub fn init_client_map(
storages.add_unique(ClientIdMap::new()); storages.add_unique(ClientIdMap::new());
} }
pub fn add_net_player(
storages: &mut AllStoragesViewMut,
init: ClientInitData
) {
// struct ClientInitData {
// client_id: ClientId,
// username: String,
// position: Vec3,
// velocity: Vec3,
// direction: Quat,
// health: Health,
// }
//Spawn player locally
let entity_id = storages.add_entity((
Username(init.username),
Client(init.client_id),
Player,
Entity,
init.health,
Transform(Mat4::from_rotation_translation(init.direction, init.position)),
PlayerHolding::default(),
));
//Add it to the client id map
let mut client_id_map = storages.borrow::<UniqueViewMut<ClientIdMap>>().unwrap();
client_id_map.0.insert(init.client_id, entity_id);
}
pub fn send_player_movement_events( pub fn send_player_movement_events(
actions: View<PlayerActionEvent>, actions: View<PlayerActionEvent>,
mut client: UniqueViewMut<UdpClient>, mut client: UniqueViewMut<UdpClient>,
@ -124,6 +96,6 @@ pub fn receive_player_connect_events(
for message in messages { for message in messages {
let ServerToClientMessage::PlayerConnected { init } = message else { unreachable!() }; let ServerToClientMessage::PlayerConnected { init } = message else { unreachable!() };
log::info!("player connected: {} (id {})", init.username, init.client_id); log::info!("player connected: {} (id {})", init.username, init.client_id);
add_net_player(&mut storages, init); spawn_remote_player_multiplayer(&mut storages, init);
} }
} }

View file

@ -1,8 +1,12 @@
use shipyard::{Component, AllStoragesViewMut}; use glam::Mat4;
use shipyard::{Component, AllStoragesViewMut, UniqueViewMut};
use kubi_shared::{ use kubi_shared::{
entity::{Entity, Health}, entity::{Entity, Health},
player::{PLAYER_HEALTH, PlayerHolding}, player::{PLAYER_HEALTH, PlayerHolding},
networking::client::Username networking::{
client::{Username, Client, ClientIdMap},
messages::ClientInitData
}
}; };
use crate::{ use crate::{
transform::Transform, transform::Transform,
@ -16,7 +20,7 @@ pub use kubi_shared::player::Player;
pub struct MainPlayer; pub struct MainPlayer;
pub fn spawn_player ( pub fn spawn_player (
mut storages: AllStoragesViewMut mut storages: AllStoragesViewMut,
) { ) {
log::info!("spawning player"); log::info!("spawning player");
storages.add_entity(( storages.add_entity((
@ -29,6 +33,55 @@ pub fn spawn_player (
FlyController, FlyController,
LookingAtBlock::default(), LookingAtBlock::default(),
PlayerHolding::default(), PlayerHolding::default(),
Username("Sbeve".into()) Username("LocalPlayer".into())
)); ));
} }
pub fn spawn_local_player_multiplayer (
storages: &mut AllStoragesViewMut,
init: ClientInitData
) {
log::info!("spawning local multiplayer player");
let entity_id = storages.add_entity((
(
Player,
Client(init.client_id),
MainPlayer,
Entity,
init.health,
Transform(Mat4::from_rotation_translation(init.direction, init.position)),
Camera::default(),
FlyController,
LookingAtBlock::default(),
PlayerHolding::default(),
),(
Username(init.username)
)
));
//Add ourself to the client id map
let mut client_id_map = storages.borrow::<UniqueViewMut<ClientIdMap>>().unwrap();
client_id_map.0.insert(init.client_id, entity_id);
}
pub fn spawn_remote_player_multiplayer(
storages: &mut AllStoragesViewMut,
init: ClientInitData
) {
log::info!("spawning remote multiplayer player");
//Spawn player locally
let entity_id = storages.add_entity((
Username(init.username),
Client(init.client_id),
Player,
Entity,
init.health,
Transform(Mat4::from_rotation_translation(init.direction, init.position)),
PlayerHolding::default(),
));
//Add it to the client id map
let mut client_id_map = storages.borrow::<UniqueViewMut<ClientIdMap>>().unwrap();
client_id_map.0.insert(init.client_id, entity_id);
}