mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-12-25 21:28:20 -06:00
server
This commit is contained in:
parent
ac5f39a49e
commit
5754dc7485
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -928,6 +928,7 @@ name = "kubi-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"flume",
|
||||||
"glam",
|
"glam",
|
||||||
"hashbrown 0.13.2",
|
"hashbrown 0.13.2",
|
||||||
"kubi-logging",
|
"kubi-logging",
|
||||||
|
@ -935,6 +936,7 @@ dependencies = [
|
||||||
"kubi-udp",
|
"kubi-udp",
|
||||||
"log",
|
"log",
|
||||||
"nohash-hasher",
|
"nohash-hasher",
|
||||||
|
"rayon",
|
||||||
"serde",
|
"serde",
|
||||||
"shipyard",
|
"shipyard",
|
||||||
"toml",
|
"toml",
|
||||||
|
|
|
@ -2,3 +2,6 @@
|
||||||
address = "0.0.0.0:12345"
|
address = "0.0.0.0:12345"
|
||||||
max_clients = 254
|
max_clients = 254
|
||||||
timeout_ms = 10000
|
timeout_ms = 10000
|
||||||
|
|
||||||
|
[world]
|
||||||
|
seed = 0xbeef_face_dead_cafe
|
||||||
|
|
|
@ -16,6 +16,8 @@ glam = { version = "0.23", features = ["debug-glam-assert", "fast-math"] }
|
||||||
hashbrown = "0.13"
|
hashbrown = "0.13"
|
||||||
nohash-hasher = "0.2.0"
|
nohash-hasher = "0.2.0"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
|
rayon = "1.6"
|
||||||
|
flume = "0.10"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
use glam::IVec3;
|
|
||||||
use hashbrown::HashMap;
|
|
||||||
use kubi_shared::chunk::BlockData;
|
|
||||||
use shipyard::Unique;
|
|
||||||
|
|
||||||
pub struct Chunk {
|
|
||||||
pub blocks: BlockData
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Unique)]
|
|
||||||
pub struct ChunkManager {
|
|
||||||
pub chunks: HashMap<IVec3, Chunk>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn server_chunk_response(
|
|
||||||
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
|
|
@ -10,9 +10,15 @@ pub struct ConfigTableServer {
|
||||||
pub password: Option<String>,
|
pub password: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct ConfigTableWorld {
|
||||||
|
pub seed: u64,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Unique, Serialize, Deserialize)]
|
#[derive(Unique, Serialize, Deserialize)]
|
||||||
pub struct ConfigTable {
|
pub struct ConfigTable {
|
||||||
pub server: ConfigTableServer
|
pub server: ConfigTableServer,
|
||||||
|
pub world: ConfigTableWorld,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_config(
|
pub fn read_config(
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
use shipyard::{World, Workload, IntoWorkload};
|
use shipyard::{World, Workload, IntoWorkload};
|
||||||
use std::{thread, time::Duration};
|
use std::{thread, time::Duration};
|
||||||
|
|
||||||
|
@ -6,17 +5,19 @@ pub(crate) mod util;
|
||||||
pub(crate) mod config;
|
pub(crate) mod config;
|
||||||
pub(crate) mod server;
|
pub(crate) mod server;
|
||||||
pub(crate) mod client;
|
pub(crate) mod client;
|
||||||
pub(crate) mod chunk;
|
pub(crate) mod world;
|
||||||
pub(crate) mod auth;
|
pub(crate) mod auth;
|
||||||
|
|
||||||
use config::read_config;
|
use config::read_config;
|
||||||
use server::{bind_server, update_server, update_server_events};
|
use server::{bind_server, update_server, update_server_events};
|
||||||
use auth::authenticate_players;
|
use auth::authenticate_players;
|
||||||
|
use world::{update_world, init_world};
|
||||||
|
|
||||||
fn initialize() -> Workload {
|
fn initialize() -> Workload {
|
||||||
(
|
(
|
||||||
read_config,
|
read_config,
|
||||||
bind_server,
|
bind_server,
|
||||||
|
init_world,
|
||||||
).into_workload()
|
).into_workload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ fn update() -> Workload {
|
||||||
update_server,
|
update_server,
|
||||||
update_server_events,
|
update_server_events,
|
||||||
authenticate_players,
|
authenticate_players,
|
||||||
|
update_world,
|
||||||
).into_workload()
|
).into_workload()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
111
kubi-server/src/world.rs
Normal file
111
kubi-server/src/world.rs
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
use shipyard::{Unique, UniqueView, UniqueViewMut, Workload, IntoWorkload, AllStoragesView};
|
||||||
|
use glam::IVec3;
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use kubi_shared::networking::messages::{ClientToServerMessage, ServerToClientMessage};
|
||||||
|
use kubi_udp::server::ServerEvent;
|
||||||
|
use crate::{
|
||||||
|
server::{UdpServer, ServerEvents},
|
||||||
|
config::ConfigTable,
|
||||||
|
util::log_error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod chunk;
|
||||||
|
pub mod tasks;
|
||||||
|
|
||||||
|
use chunk::Chunk;
|
||||||
|
|
||||||
|
use self::{tasks::{ChunkTaskManager, ChunkTask, ChunkTaskResponse}, chunk::ChunkState};
|
||||||
|
|
||||||
|
#[derive(Unique, Default)]
|
||||||
|
pub struct ChunkManager {
|
||||||
|
pub chunks: HashMap<IVec3, Chunk>
|
||||||
|
}
|
||||||
|
impl ChunkManager {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_chunk_requests(
|
||||||
|
mut server: UniqueViewMut<UdpServer>,
|
||||||
|
events: UniqueView<ServerEvents>,
|
||||||
|
mut chunk_manager: UniqueViewMut<ChunkManager>,
|
||||||
|
task_manager: UniqueView<ChunkTaskManager>,
|
||||||
|
config: UniqueView<ConfigTable>
|
||||||
|
) {
|
||||||
|
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(),
|
||||||
|
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_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>,
|
||||||
|
task_manager: UniqueView<ChunkTaskManager>,
|
||||||
|
mut chunk_manager: UniqueViewMut<ChunkManager>,
|
||||||
|
) {
|
||||||
|
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");
|
||||||
|
continue
|
||||||
|
};
|
||||||
|
if chunk.state != ChunkState::Loading {
|
||||||
|
log::warn!("Chunk discarded: Not Loading");
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
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.iter().map(|item| (item.position.to_array(), item.block_type)).collect()
|
||||||
|
}).map_err(log_error).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_chunk_manager(
|
||||||
|
storages: AllStoragesView
|
||||||
|
) {
|
||||||
|
storages.add_unique(ChunkManager::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_world() -> Workload {
|
||||||
|
(
|
||||||
|
init_chunk_manager
|
||||||
|
).into_workload()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_world() -> Workload {
|
||||||
|
(
|
||||||
|
process_chunk_requests,
|
||||||
|
process_finished_tasks,
|
||||||
|
).into_workload()
|
||||||
|
}
|
29
kubi-server/src/world/chunk.rs
Normal file
29
kubi-server/src/world/chunk.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
use glam::IVec3;
|
||||||
|
use hashbrown::HashSet;
|
||||||
|
use nohash_hasher::BuildNoHashHasher;
|
||||||
|
use kubi_shared::chunk::BlockData;
|
||||||
|
use kubi_udp::{ClientId, ClientIdRepr};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum ChunkState {
|
||||||
|
Nothing,
|
||||||
|
Loading,
|
||||||
|
Loaded,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Chunk {
|
||||||
|
pub position: IVec3,
|
||||||
|
pub state: ChunkState,
|
||||||
|
pub blocks: Option<BlockData>,
|
||||||
|
pub subscriptions: HashSet<ClientId, BuildNoHashHasher<ClientIdRepr>>,
|
||||||
|
}
|
||||||
|
impl Chunk {
|
||||||
|
pub fn new(position: IVec3) -> Self {
|
||||||
|
Self {
|
||||||
|
position,
|
||||||
|
state: ChunkState::Nothing,
|
||||||
|
blocks: None,
|
||||||
|
subscriptions: HashSet::with_hasher(BuildNoHashHasher::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
kubi-server/src/world/tasks.rs
Normal file
58
kubi-server/src/world/tasks.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
use shipyard::{Unique, AllStoragesView};
|
||||||
|
use flume::{unbounded, Sender, Receiver};
|
||||||
|
use glam::IVec3;
|
||||||
|
use rayon::{ThreadPool, ThreadPoolBuilder};
|
||||||
|
use anyhow::Result;
|
||||||
|
use kubi_shared::{
|
||||||
|
chunk::BlockData,
|
||||||
|
worldgen::{QueuedBlock, generate_world}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum ChunkTask {
|
||||||
|
LoadChunk {
|
||||||
|
position: IVec3,
|
||||||
|
seed: u64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ChunkTaskResponse {
|
||||||
|
ChunkLoaded {
|
||||||
|
chunk_position: IVec3,
|
||||||
|
blocks: BlockData,
|
||||||
|
queue: Vec<QueuedBlock>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Unique)]
|
||||||
|
pub struct ChunkTaskManager {
|
||||||
|
channel: (Sender<ChunkTaskResponse>, Receiver<ChunkTaskResponse>),
|
||||||
|
pool: ThreadPool,
|
||||||
|
}
|
||||||
|
impl ChunkTaskManager {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
channel: unbounded(),
|
||||||
|
pool: ThreadPoolBuilder::new().build()?
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn spawn_task(&self, task: ChunkTask) {
|
||||||
|
let sender = self.channel.0.clone();
|
||||||
|
self.pool.spawn(move || {
|
||||||
|
sender.send(match task {
|
||||||
|
ChunkTask::LoadChunk { position: chunk_position, seed } => {
|
||||||
|
let (blocks, queue) = generate_world(chunk_position, seed);
|
||||||
|
ChunkTaskResponse::ChunkLoaded { chunk_position, blocks, queue }
|
||||||
|
}
|
||||||
|
}).unwrap()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn receive(&self) -> Option<ChunkTaskResponse> {
|
||||||
|
self.channel.1.try_recv().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_chunk_task_manager(
|
||||||
|
storages: AllStoragesView
|
||||||
|
) {
|
||||||
|
storages.add_unique(ChunkTaskManager::new().expect("ChunkTaskManager Init failed"));
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
use bincode::{Encode, Decode};
|
use bincode::{Encode, Decode};
|
||||||
use crate::chunk::BlockData;
|
use crate::{chunk::BlockData, block::Block};
|
||||||
|
|
||||||
type IVec3Arr = [i32; 3];
|
type IVec3Arr = [i32; 3];
|
||||||
type Vec3Arr = [f32; 3];
|
type Vec3Arr = [f32; 3];
|
||||||
|
@ -20,7 +19,7 @@ pub enum ClientToServerMessage {
|
||||||
velocity: Vec3Arr,
|
velocity: Vec3Arr,
|
||||||
direction: QuatArr,
|
direction: QuatArr,
|
||||||
},
|
},
|
||||||
ChunkRequest {
|
ChunkSubRequest {
|
||||||
chunk: IVec3Arr,
|
chunk: IVec3Arr,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -54,6 +53,7 @@ pub enum ServerToClientMessage {
|
||||||
},
|
},
|
||||||
ChunkResponse {
|
ChunkResponse {
|
||||||
chunk: IVec3Arr,
|
chunk: IVec3Arr,
|
||||||
data: BlockData
|
data: BlockData,
|
||||||
|
queued: Vec<(IVec3Arr, Block)>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@ fn start_required_tasks(
|
||||||
DesiredChunkState::Loaded | DesiredChunkState::Rendered if chunk.current_state == CurrentChunkState::Nothing => {
|
DesiredChunkState::Loaded | DesiredChunkState::Rendered if chunk.current_state == CurrentChunkState::Nothing => {
|
||||||
//start load task
|
//start load task
|
||||||
if let Some(client) = &udp_client {
|
if let Some(client) = &udp_client {
|
||||||
client.0.send_message(ClientToServerMessage::ChunkRequest {
|
client.0.send_message(ClientToServerMessage::ChunkSubRequest {
|
||||||
chunk: position.to_array()
|
chunk: position.to_array()
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -83,12 +83,15 @@ pub fn inject_network_responses_into_manager_queue(
|
||||||
events: View<NetworkEvent>
|
events: View<NetworkEvent>
|
||||||
) {
|
) {
|
||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
if let ClientEvent::MessageReceived(ServerToClientMessage::ChunkResponse { chunk, data }) = &event.0 {
|
if let ClientEvent::MessageReceived(ServerToClientMessage::ChunkResponse { chunk, data, queued }) = &event.0 {
|
||||||
let position = IVec3::from_array(*chunk);
|
let position = IVec3::from_array(*chunk);
|
||||||
manager.add_sussy_response(ChunkTaskResponse::LoadedChunk {
|
manager.add_sussy_response(ChunkTaskResponse::LoadedChunk {
|
||||||
position,
|
position,
|
||||||
chunk_data: data.clone(),
|
chunk_data: data.clone(),
|
||||||
queued: Vec::with_capacity(0)
|
queued: queued.iter().map(|&(position, block_type)| QueuedBlock {
|
||||||
|
position: IVec3::from_array(position),
|
||||||
|
block_type
|
||||||
|
}).collect()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue