This commit is contained in:
griffi-gh 2023-05-19 13:22:32 +02:00
parent 03fbb774b5
commit 4bf2b350c6
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();
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{
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 std::net::SocketAddr;
use uflow::server::Event as ServerEvent;
use kubi_shared::networking::{
use kubi_shared::{
networking::{
client::{ClientIdMap, Client},
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)]
pub struct ClientAddress(pub SocketAddr);
@ -29,34 +35,18 @@ pub fn sync_client_positions(
events: UniqueView<ServerEvents>,
addr_map: UniqueView<ClientAddressMap>,
clients: View<Client>,
mut transforms: ViewMut<Transform>
) {
for event in &events.0 {
let ServerEvent::Receive(client_addr, data) = event else{
continue
let Some(message) = check_message_auth::<C_POSITION_CHANGED>(&server, event, &clients, &addr_map) else {
continue;
};
if !event.is_message_of_type::<C_POSITION_CHANGED>() {
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 {
let ClientToServerMessage::PositionChanged { position, velocity: _, direction } = message.message else {
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 std::{thread, time::Duration};
mod util;
mod config;
mod server;
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},
config::ConfigTable,
client::{ClientAddress, ClientAddressMap},
util::check_message_auth,
};
pub mod chunk;
@ -61,38 +62,19 @@ fn process_chunk_requests(
clients: View<Client>
) {
for event in &events.0 {
let ServerEvent::Receive(client_addr, data) = event else{
continue
let Some(message) = check_message_auth::<C_CHUNK_SUB_REQUEST>(&server, event, &clients, &addr_map) else {
continue;
};
if !event.is_message_of_type::<C_CHUNK_SUB_REQUEST>() {
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 {
let ClientToServerMessage::ChunkSubRequest { chunk: chunk_position } = message.message else {
unreachable!()
};
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"
if let Some(blocks) = &chunk.blocks {
send_chunk_compressed(
&client,
&message.client,
&ServerToClientMessage::ChunkResponse {
chunk: chunk_position,
data: blocks.clone(),
@ -103,7 +85,7 @@ fn process_chunk_requests(
} else {
let mut chunk = Chunk::new(chunk_position);
chunk.state = ChunkState::Loading;
chunk.subscriptions.insert(client_id);
chunk.subscriptions.insert(message.client_id);
chunk_manager.chunks.insert(chunk_position, chunk);
task_manager.spawn_task(ChunkTask::LoadChunk {
position: chunk_position,
@ -172,32 +154,15 @@ fn process_block_queue_messages(
addrs: View<ClientAddress>,
) {
for event in &events.0 {
let ServerEvent::Receive(client_addr, data) = event else{
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 Some(message) = check_message_auth::<C_QUEUE_BLOCK>(&server, event, &clients, &addr_map) else {
continue;
};
let ClientToServerMessage::QueueBlock { item } = message.message else { unreachable!() };
//TODO place in our own queue, for now just send to other clients
log::info!("Placed block {:?} at {}", item.block_type, item.position);
for (other_client, other_client_address) in (&clients, &addrs).iter() {
//No need to send the event back
if client_id == other_client.0 {
if message.client_id == other_client.0 {
continue
}
//Get client

View file

@ -9,9 +9,26 @@ flat in uint v_tex_index;
out vec4 color;
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() {
// 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;

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 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 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 gui::{render_gui, init_gui, update_gui};
use loading_screen::update_loading_screen;
@ -114,7 +114,9 @@ fn update() -> Workload {
process_inputs,
(
init_game_world.run_if_missing_unique::<ChunkTaskManager>(),
(
spawn_player.run_if_storage_empty::<MainPlayer>(),
).into_sequential_workload().run_if(is_singleplayer),
).into_sequential_workload().run_if(is_ingame_or_loading),
update_networking.run_if(is_multiplayer),
(

View file

@ -5,6 +5,7 @@ use uflow::{client::{Client, Config as ClientConfig, Event as ClientEvent}, Endp
use kubi_shared::networking::{
messages::ServerToClientMessage,
state::ClientJoinState,
client::ClientIdMap,
};
use crate::{
events::EventComponent,
@ -27,7 +28,10 @@ use world::{
send_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};
@ -111,6 +115,7 @@ fn handle_disconnect(
pub fn update_networking() -> Workload {
(
init_client_map.run_if_missing_unique::<ClientIdMap>(),
connect_client.run_if_missing_unique::<UdpClient>(),
poll_client,
(

View file

@ -1,17 +1,15 @@
use glam::Mat4;
use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesViewMut, ViewMut, IntoWithId};
use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesViewMut};
use uflow::{client::Event as ClientEvent, SendMode};
use kubi_shared::{
networking::{
messages::{ClientToServerMessage, ServerToClientMessage, S_SERVER_HELLO},
state::ClientJoinState,
channels::CHANNEL_AUTH,
client::{Username, Client},
client::Username,
},
transform::Transform, entity::Health
};
use crate::player::MainPlayer;
use super::{UdpClient, NetworkEvent, player::add_net_player};
use crate::player::{MainPlayer, spawn_local_player_multiplayer, spawn_remote_player_multiplayer};
use super::{UdpClient, NetworkEvent};
pub fn set_client_join_state_to_connected(
mut join_state: UniqueViewMut<ClientJoinState>
@ -25,7 +23,7 @@ pub fn say_hello(
main_player: View<MainPlayer>,
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;
log::info!("Authenticating");
client.0.send(
@ -68,28 +66,11 @@ pub fn check_server_hello_response(
// }
//Add components to main player
{
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;
}
}
spawn_local_player_multiplayer(&mut storages, init.user);
//Init players
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

View file

@ -2,16 +2,17 @@ use glam::{Vec3, Mat4};
use shipyard::{UniqueViewMut, View, IntoIter, AllStoragesView, AllStoragesViewMut, UniqueView, ViewMut, Get};
use uflow::{SendMode, client::Event as ClientEvent};
use kubi_shared::{
player::{Player, PlayerHolding},
entity::Entity,
transform::Transform,
networking::{
messages::{ClientToServerMessage, ServerToClientMessage, S_PLAYER_POSITION_CHANGED, ClientInitData},
messages::{ClientToServerMessage, ServerToClientMessage, S_PLAYER_POSITION_CHANGED},
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};
pub fn init_client_map(
@ -20,35 +21,6 @@ pub fn init_client_map(
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(
actions: View<PlayerActionEvent>,
mut client: UniqueViewMut<UdpClient>,
@ -124,6 +96,6 @@ pub fn receive_player_connect_events(
for message in messages {
let ServerToClientMessage::PlayerConnected { init } = message else { unreachable!() };
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::{
entity::{Entity, Health},
player::{PLAYER_HEALTH, PlayerHolding},
networking::client::Username
networking::{
client::{Username, Client, ClientIdMap},
messages::ClientInitData
}
};
use crate::{
transform::Transform,
@ -16,7 +20,7 @@ pub use kubi_shared::player::Player;
pub struct MainPlayer;
pub fn spawn_player (
mut storages: AllStoragesViewMut
mut storages: AllStoragesViewMut,
) {
log::info!("spawning player");
storages.add_entity((
@ -29,6 +33,55 @@ pub fn spawn_player (
FlyController,
LookingAtBlock::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);
}