mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-12-21 19:38:20 -06:00
Finish upgrading server to uflow
This commit is contained in:
parent
c4767e8be8
commit
5a75e44beb
|
@ -1,7 +1,10 @@
|
|||
[server]
|
||||
address = "0.0.0.0:12345"
|
||||
max_clients = 254
|
||||
max_clients = 32
|
||||
timeout_ms = 10000
|
||||
|
||||
[world]
|
||||
seed = 0xfeb_face_dead_cafe
|
||||
|
||||
[query]
|
||||
name = "Kubi Server"
|
||||
|
|
|
@ -1,40 +1,47 @@
|
|||
use shipyard::{UniqueView, NonSendSync};
|
||||
use shipyard::{UniqueView, NonSendSync, EntitiesViewMut, ViewMut, UniqueViewMut};
|
||||
use uflow::{server::Event as ServerEvent, SendMode};
|
||||
use kubi_shared::networking::messages::{
|
||||
ClientToServerMessage,
|
||||
ServerToClientMessage,
|
||||
InitData,
|
||||
C_CLIENT_HELLO
|
||||
use kubi_shared::{
|
||||
networking::{
|
||||
messages::{
|
||||
ClientToServerMessage,
|
||||
ServerToClientMessage,
|
||||
InitData,
|
||||
C_CLIENT_HELLO
|
||||
},
|
||||
client::{Client, ClientId}, channels::CHANNEL_AUTH
|
||||
},
|
||||
player::Player,
|
||||
transform::Transform
|
||||
};
|
||||
use crate::{
|
||||
server::{ServerEvents, UdpServer, IsMessageOfType},
|
||||
config::ConfigTable
|
||||
config::ConfigTable,
|
||||
client::{ClientAddress, ClientIdMap, ClientAddressMap}
|
||||
};
|
||||
|
||||
pub fn authenticate_players(
|
||||
mut entities: EntitiesViewMut,
|
||||
mut players: ViewMut<Player>,
|
||||
mut clients: ViewMut<Client>,
|
||||
mut client_addrs: ViewMut<ClientAddress>,
|
||||
mut transforms: ViewMut<Transform>,
|
||||
mut client_entity_map: UniqueViewMut<ClientIdMap>,
|
||||
mut client_addr_map: UniqueViewMut<ClientAddressMap>,
|
||||
server: NonSendSync<UniqueView<UdpServer>>,
|
||||
events: UniqueView<ServerEvents>,
|
||||
config: UniqueView<ConfigTable>
|
||||
) {
|
||||
for event in &events.0 {
|
||||
// if let ServerEvent::MessageReceived {
|
||||
// from,
|
||||
// message: ClientToServerMessage::ClientHello {
|
||||
// username,
|
||||
// password
|
||||
// }
|
||||
// } = event {
|
||||
|
||||
let ServerEvent::Receive(client_addr, data) = event else{
|
||||
continue
|
||||
};
|
||||
let Some(client) = server.0.client(client_addr) else {
|
||||
log::error!("Client doesn't exist");
|
||||
continue
|
||||
};
|
||||
if !event.is_message_of_type::<C_CLIENT_HELLO>() {
|
||||
continue
|
||||
}
|
||||
let Some(client) = server.0.client(client_addr) else {
|
||||
log::error!("Client doesn't exist");
|
||||
continue
|
||||
};
|
||||
let Ok(parsed_message) = postcard::from_bytes(data) else {
|
||||
log::error!("Malformed message");
|
||||
continue
|
||||
|
@ -49,38 +56,70 @@ pub fn authenticate_players(
|
|||
if let Some(server_password) = &config.server.password {
|
||||
if let Some(user_password) = &password {
|
||||
if server_password != user_password {
|
||||
let res = postcard::to_allocvec(&ServerToClientMessage::ServerFuckOff {
|
||||
reason: "Passwords don't match".into()
|
||||
}).unwrap().into_boxed_slice();
|
||||
client.borrow_mut().send(
|
||||
res, 0, SendMode::Reliable
|
||||
postcard::to_allocvec(&ServerToClientMessage::ServerFuckOff {
|
||||
reason: "Incorrect password".into()
|
||||
}).unwrap().into_boxed_slice(),
|
||||
CHANNEL_AUTH,
|
||||
SendMode::Reliable
|
||||
);
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
let res = postcard::to_allocvec(&ServerToClientMessage::ServerFuckOff {
|
||||
reason: "This server is password protected".into()
|
||||
}).unwrap().into_boxed_slice();
|
||||
client.borrow_mut().send(
|
||||
res, 0, SendMode::Reliable
|
||||
postcard::to_allocvec(&ServerToClientMessage::ServerFuckOff {
|
||||
reason: "This server is password protected".into()
|
||||
}).unwrap().into_boxed_slice(),
|
||||
CHANNEL_AUTH,
|
||||
SendMode::Reliable
|
||||
);
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
//Find the player ID
|
||||
let max_clients = config.server.max_clients as ClientId;
|
||||
let Some(client_id) = (0..max_clients).into_iter().find(|id| {
|
||||
!client_entity_map.0.contains_key(id)
|
||||
}) else {
|
||||
client.borrow_mut().send(
|
||||
postcard::to_allocvec(&ServerToClientMessage::ServerFuckOff {
|
||||
reason: "Can't find a free spot for you!".into()
|
||||
}).unwrap().into_boxed_slice(),
|
||||
CHANNEL_AUTH,
|
||||
SendMode::Reliable
|
||||
);
|
||||
continue
|
||||
};
|
||||
|
||||
//Spawn the user
|
||||
//TODO Spawn the user on server side
|
||||
let entity_id = entities.add_entity((
|
||||
&mut players,
|
||||
&mut clients,
|
||||
&mut client_addrs,
|
||||
&mut transforms,
|
||||
), (
|
||||
Player,
|
||||
Client(client_id),
|
||||
ClientAddress(*client_addr),
|
||||
Transform::default(),
|
||||
));
|
||||
|
||||
//Add the user to the ClientIdMap and ClientAddressMap
|
||||
client_entity_map.0.insert(client_id, entity_id);
|
||||
client_addr_map.0.insert(*client_addr, entity_id);
|
||||
|
||||
//Approve the user
|
||||
let res = postcard::to_allocvec(&ServerToClientMessage::ServerHello {
|
||||
init: InitData {
|
||||
users: vec![] //TODO create init data
|
||||
}
|
||||
}).unwrap().into_boxed_slice();
|
||||
client.borrow_mut().send(
|
||||
res, 0, SendMode::Reliable
|
||||
postcard::to_allocvec(&ServerToClientMessage::ServerHello {
|
||||
init: InitData {
|
||||
users: vec![] //TODO create init data
|
||||
}
|
||||
}).unwrap().into_boxed_slice(),
|
||||
CHANNEL_AUTH,
|
||||
SendMode::Reliable
|
||||
);
|
||||
|
||||
log::info!("{username} joined the game!")
|
||||
log::info!("{username}({client_id}) joined the game!")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,34 @@
|
|||
use shipyard::{Component, EntityId};
|
||||
use shipyard::{Component, EntityId, Unique, Workload, AllStoragesView};
|
||||
use hashbrown::HashMap;
|
||||
use nohash_hasher::BuildNoHashHasher;
|
||||
use std::net::SocketAddr;
|
||||
use kubi_shared::networking::client::ClientId;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Client(ClientId);
|
||||
#[derive(Component, Clone, Copy)]
|
||||
pub struct ClientAddress(pub SocketAddr);
|
||||
|
||||
pub struct ClientMap(HashMap<ClientId, EntityId, BuildNoHashHasher<ClientId>>);
|
||||
impl ClientMap {
|
||||
#[derive(Unique)]
|
||||
pub struct ClientIdMap(pub HashMap<ClientId, EntityId, BuildNoHashHasher<ClientId>>);
|
||||
impl ClientIdMap {
|
||||
pub fn new() -> Self {
|
||||
Self(HashMap::with_hasher(BuildNoHashHasher::default()))
|
||||
}
|
||||
}
|
||||
impl Default for ClientMap {
|
||||
impl Default for ClientIdMap {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Unique, Default)]
|
||||
pub struct ClientAddressMap(pub HashMap<SocketAddr, EntityId>);
|
||||
impl ClientAddressMap {
|
||||
pub fn new() -> Self { Self::default() }
|
||||
}
|
||||
|
||||
pub fn init_client_maps(
|
||||
storages: AllStoragesView
|
||||
) {
|
||||
storages.add_unique(ClientIdMap::new());
|
||||
storages.add_unique(ClientAddressMap::new());
|
||||
}
|
||||
|
|
|
@ -15,10 +15,16 @@ pub struct ConfigTableWorld {
|
|||
pub seed: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ConfigTableQuery {
|
||||
pub name: Option<String>
|
||||
}
|
||||
|
||||
#[derive(Unique, Serialize, Deserialize)]
|
||||
pub struct ConfigTable {
|
||||
pub server: ConfigTableServer,
|
||||
pub world: ConfigTableWorld,
|
||||
pub query: ConfigTableQuery,
|
||||
}
|
||||
|
||||
pub fn read_config(
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
use shipyard::{World, Workload, IntoWorkload};
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
pub(crate) mod util;
|
||||
pub(crate) mod config;
|
||||
pub(crate) mod server;
|
||||
pub(crate) mod client;
|
||||
//pub(crate) mod world;
|
||||
pub(crate) mod auth;
|
||||
mod util;
|
||||
mod config;
|
||||
mod server;
|
||||
mod client;
|
||||
mod world;
|
||||
mod auth;
|
||||
|
||||
use config::read_config;
|
||||
use server::{bind_server, update_server, log_server_errors};
|
||||
use client::init_client_maps;
|
||||
use auth::authenticate_players;
|
||||
//use world::{update_world, init_world};
|
||||
use world::{update_world, init_world};
|
||||
|
||||
fn initialize() -> Workload {
|
||||
(
|
||||
read_config,
|
||||
bind_server,
|
||||
//init_world,
|
||||
init_client_maps,
|
||||
init_world,
|
||||
).into_workload()
|
||||
}
|
||||
|
||||
|
@ -27,7 +29,7 @@ fn update() -> Workload {
|
|||
(
|
||||
log_server_errors,
|
||||
authenticate_players,
|
||||
//update_world,
|
||||
update_world,
|
||||
).into_workload()
|
||||
).into_sequential_workload()
|
||||
}
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
use shipyard::{Unique, UniqueView, UniqueViewMut, Workload, IntoWorkload, AllStoragesView};
|
||||
use shipyard::{Unique, UniqueView, UniqueViewMut, Workload, IntoWorkload, AllStoragesView, View, Get, NonSendSync};
|
||||
use glam::IVec3;
|
||||
use hashbrown::HashMap;
|
||||
use kubi_shared::networking::messages::{ClientToServerMessage, ServerToClientMessage};
|
||||
use kubi_shared::networking::{
|
||||
messages::{ClientToServerMessage, ServerToClientMessage, C_CHUNK_SUB_REQUEST},
|
||||
channels::CHANNEL_WORLD,
|
||||
client::Client,
|
||||
};
|
||||
use uflow::{
|
||||
server::Event as ServerEvent,
|
||||
SendMode
|
||||
};
|
||||
use crate::{
|
||||
server::{UdpServer, ServerEvents},
|
||||
server::{UdpServer, ServerEvents, IsMessageOfType},
|
||||
config::ConfigTable,
|
||||
util::log_error,
|
||||
client::{ClientAddress, ClientIdMap, ClientAddressMap},
|
||||
};
|
||||
|
||||
pub mod chunk;
|
||||
|
@ -26,51 +34,77 @@ impl ChunkManager {
|
|||
}
|
||||
|
||||
fn process_chunk_requests(
|
||||
mut server: UniqueViewMut<UdpServer>,
|
||||
mut server: NonSendSync<UniqueViewMut<UdpServer>>,
|
||||
events: UniqueView<ServerEvents>,
|
||||
mut chunk_manager: UniqueViewMut<ChunkManager>,
|
||||
task_manager: UniqueView<ChunkTaskManager>,
|
||||
config: UniqueView<ConfigTable>
|
||||
config: UniqueView<ConfigTable>,
|
||||
addr_map: UniqueView<ClientAddressMap>,
|
||||
clients: View<Client>
|
||||
) {
|
||||
for event in &events.0 {
|
||||
if let ServerEvent::MessageReceived {
|
||||
from: client_id,
|
||||
message: ClientToServerMessage::ChunkSubRequest {
|
||||
chunk: chunk_position
|
||||
}
|
||||
} = event {
|
||||
let chunk_position = IVec3::from_array(*chunk_position);
|
||||
if let Some(chunk) = chunk_manager.chunks.get_mut(&chunk_position) {
|
||||
chunk.subscriptions.insert(*client_id);
|
||||
//TODO Start task here if status is "Nothing"
|
||||
if let Some(blocks) = &chunk.blocks {
|
||||
server.0.send_message(*client_id, kubi_shared::networking::messages::ServerToClientMessage::ChunkResponse {
|
||||
chunk: chunk_position.to_array(),
|
||||
let ServerEvent::Receive(client_addr, data) = event 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 {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
if let Some(chunk) = chunk_manager.chunks.get_mut(&chunk_position) {
|
||||
chunk.subscriptions.insert(client_id);
|
||||
//TODO Start task here if status is "Nothing"
|
||||
if let Some(blocks) = &chunk.blocks {
|
||||
client.borrow_mut().send(
|
||||
postcard::to_allocvec(&ServerToClientMessage::ChunkResponse {
|
||||
chunk: chunk_position,
|
||||
data: blocks.clone(),
|
||||
queued: Vec::with_capacity(0)
|
||||
}).map_err(log_error).ok();
|
||||
}
|
||||
} else {
|
||||
let mut chunk = Chunk::new(chunk_position);
|
||||
chunk.state = ChunkState::Loading;
|
||||
chunk.subscriptions.insert(*client_id);
|
||||
chunk_manager.chunks.insert(chunk_position, chunk);
|
||||
task_manager.spawn_task(ChunkTask::LoadChunk {
|
||||
position: chunk_position,
|
||||
seed: config.world.seed,
|
||||
});
|
||||
}).unwrap().into_boxed_slice(),
|
||||
CHANNEL_WORLD,
|
||||
SendMode::Reliable,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let mut chunk = Chunk::new(chunk_position);
|
||||
chunk.state = ChunkState::Loading;
|
||||
chunk.subscriptions.insert(client_id);
|
||||
chunk_manager.chunks.insert(chunk_position, chunk);
|
||||
task_manager.spawn_task(ChunkTask::LoadChunk {
|
||||
position: chunk_position,
|
||||
seed: config.world.seed,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_finished_tasks(
|
||||
mut server: UniqueViewMut<UdpServer>,
|
||||
mut server: NonSendSync<UniqueViewMut<UdpServer>>,
|
||||
task_manager: UniqueView<ChunkTaskManager>,
|
||||
mut chunk_manager: UniqueViewMut<ChunkManager>,
|
||||
id_map: UniqueView<ClientIdMap>,
|
||||
client_addr: View<ClientAddress>,
|
||||
) {
|
||||
let mut limit: usize = 8;
|
||||
while let Some(res) = task_manager.receive() {
|
||||
'outer: while let Some(res) = task_manager.receive() {
|
||||
let ChunkTaskResponse::ChunkLoaded { chunk_position, blocks, queue } = res;
|
||||
let Some(chunk) = chunk_manager.chunks.get_mut(&chunk_position) else {
|
||||
log::warn!("Chunk discarded: Doesn't exist");
|
||||
|
@ -82,18 +116,29 @@ fn process_finished_tasks(
|
|||
}
|
||||
chunk.state = ChunkState::Loaded;
|
||||
chunk.blocks = Some(blocks.clone());
|
||||
for &subscriber in &chunk.subscriptions {
|
||||
server.0.send_message(subscriber, ServerToClientMessage::ChunkResponse {
|
||||
chunk: chunk_position.to_array(),
|
||||
data: blocks.clone(),
|
||||
queued: queue
|
||||
}).map_err(log_error).ok();
|
||||
}
|
||||
log::debug!("Chunk {chunk_position} loaded, {} subs", chunk.subscriptions.len());
|
||||
//HACK: Implement proper flow control/reliable transport in kubi-udp
|
||||
limit -= 1;
|
||||
if limit == 0 {
|
||||
break;
|
||||
for &subscriber in &chunk.subscriptions {
|
||||
let Some(&entity_id) = id_map.0.get(&subscriber) else {
|
||||
log::error!("Invalid subscriber client id");
|
||||
continue 'outer;
|
||||
};
|
||||
let Ok(&ClientAddress(client_addr)) = (&client_addr).get(entity_id) else {
|
||||
log::error!("Invalid subscriber entity id");
|
||||
continue 'outer;
|
||||
};
|
||||
let Some(client) = server.0.client(&client_addr) else {
|
||||
log::error!("Client not connected");
|
||||
continue 'outer;
|
||||
};
|
||||
client.borrow_mut().send(
|
||||
postcard::to_allocvec(&ServerToClientMessage::ChunkResponse {
|
||||
chunk: chunk_position,
|
||||
data: blocks.clone(),
|
||||
queued: queue.clone()
|
||||
}).unwrap().into_boxed_slice(),
|
||||
CHANNEL_WORLD,
|
||||
SendMode::Reliable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod messages;
|
||||
pub mod state;
|
||||
pub mod client;
|
||||
pub mod channels;
|
||||
|
|
3
kubi-shared/src/networking/channels.rs
Normal file
3
kubi-shared/src/networking/channels.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub const CHANNEL_GENERIC: usize = 0;
|
||||
pub const CHANNEL_AUTH: usize = 1;
|
||||
pub const CHANNEL_WORLD: usize = 2;
|
|
@ -1,3 +1,7 @@
|
|||
use shipyard::Component;
|
||||
|
||||
pub type ClientId = u16;
|
||||
pub type ClientKey = u16;
|
||||
|
||||
#[derive(Component, Clone, Copy, Debug)]
|
||||
pub struct Client(pub ClientId);
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
use std::num::NonZeroUsize;
|
||||
use glam::{Vec3, IVec3, Quat};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::{chunk::BlockData, queue::QueuedBlock};
|
||||
|
||||
pub type IVec3Arr = [i32; 3];
|
||||
pub type Vec3Arr = [f32; 3];
|
||||
pub type QuatArr = [f32; 3];
|
||||
use super::client::ClientId;
|
||||
|
||||
pub const PROTOCOL_ID: u16 = 2;
|
||||
|
||||
|
@ -20,12 +17,12 @@ pub enum ClientToServerMessage {
|
|||
password: Option<String>,
|
||||
} = C_CLIENT_HELLO,
|
||||
PositionChanged {
|
||||
position: Vec3Arr,
|
||||
velocity: Vec3Arr,
|
||||
direction: QuatArr,
|
||||
position: Vec3,
|
||||
velocity: Vec3,
|
||||
direction: Quat,
|
||||
} = C_POSITION_CHANGED,
|
||||
ChunkSubRequest {
|
||||
chunk: IVec3Arr,
|
||||
chunk: IVec3,
|
||||
} = C_CHUNK_SUB_REQUEST,
|
||||
}
|
||||
|
||||
|
@ -45,11 +42,11 @@ pub enum ServerToClientMessage {
|
|||
} = S_SERVER_FUCK_OFF,
|
||||
PlayerPositionChanged {
|
||||
client_id: u8,
|
||||
position: Vec3Arr,
|
||||
direction: QuatArr,
|
||||
position: Vec3,
|
||||
direction: Quat,
|
||||
} = S_PLAYER_POSITION_CHANGED,
|
||||
ChunkResponse {
|
||||
chunk: IVec3Arr,
|
||||
chunk: IVec3,
|
||||
data: BlockData,
|
||||
queued: Vec<QueuedBlock>,
|
||||
} = S_CHUNK_RESPONSE,
|
||||
|
@ -59,11 +56,11 @@ pub enum ServerToClientMessage {
|
|||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct UserInitData {
|
||||
pub client_id: NonZeroUsize, //maybe use the proper type instead
|
||||
pub client_id: ClientId,
|
||||
pub username: String,
|
||||
pub position: Vec3Arr,
|
||||
pub velocity: Vec3Arr,
|
||||
pub direction: QuatArr,
|
||||
pub position: Vec3,
|
||||
pub velocity: Vec3,
|
||||
pub direction: Quat,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
|
|
|
@ -4,7 +4,8 @@ use std::net::SocketAddr;
|
|||
use uflow::client::{Client, Config as ClientConfig, Event as ClientEvent};
|
||||
use kubi_shared::networking::{
|
||||
messages::{ClientToServerMessage, ServerToClientMessage, S_SERVER_HELLO},
|
||||
state::ClientJoinState
|
||||
state::ClientJoinState,
|
||||
channels::CHANNEL_AUTH,
|
||||
};
|
||||
use crate::{events::EventComponent, control_flow::SetControlFlow};
|
||||
|
||||
|
@ -77,7 +78,7 @@ fn say_hello(
|
|||
password: None
|
||||
}
|
||||
).unwrap().into_boxed_slice(),
|
||||
0,
|
||||
CHANNEL_AUTH,
|
||||
uflow::SendMode::Reliable
|
||||
);
|
||||
}
|
||||
|
|
|
@ -138,7 +138,7 @@ fn start_required_tasks(
|
|||
if let Some(client) = &mut udp_client {
|
||||
client.0.send(
|
||||
postcard::to_allocvec(&ClientToServerMessage::ChunkSubRequest {
|
||||
chunk: position.to_array()
|
||||
chunk: position,
|
||||
}).unwrap().into_boxed_slice(),
|
||||
0,
|
||||
SendMode::Reliable
|
||||
|
|
|
@ -89,7 +89,7 @@ pub fn inject_network_responses_into_manager_queue(
|
|||
chunk, data, queued
|
||||
} = postcard::from_bytes(data).expect("Chunk decode failed") else { unreachable!() };
|
||||
manager.add_sussy_response(ChunkTaskResponse::LoadedChunk {
|
||||
position: IVec3::from_array(chunk),
|
||||
position: chunk,
|
||||
chunk_data: data,
|
||||
queued
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue