mirror of
https://github.com/griffi-gh/kubi.git
synced 2024-11-22 06:48:43 -06:00
Compare commits
12 commits
043bb873c4
...
772a8ea7db
Author | SHA1 | Date | |
---|---|---|---|
griffi-gh | 772a8ea7db | ||
griffi-gh | 6f84d9014a | ||
griffi-gh | 6ee282e744 | ||
griffi-gh | 5d8906cfb1 | ||
griffi-gh | 8c4ef7f83f | ||
griffi-gh | b8c7dcc196 | ||
griffi-gh | dc1a641887 | ||
griffi-gh | e373aa758c | ||
griffi-gh | 907a5845fa | ||
griffi-gh | 740da98cbd | ||
griffi-gh | af3c938a03 | ||
griffi-gh | 50cc36e3d5 |
489
Cargo.lock
generated
489
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,14 @@
|
||||||
use shipyard::{Unique, UniqueView, UniqueViewMut, Workload, IntoWorkload, AllStoragesView, View, Get, NonSendSync, IntoIter};
|
use shipyard::{Unique, UniqueView, UniqueViewMut, Workload, IntoWorkload, AllStoragesView, View, Get, NonSendSync, IntoIter};
|
||||||
use glam::IVec3;
|
use glam::IVec3;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use kubi_shared::networking::{
|
use kubi_shared::{
|
||||||
channels::Channel, client::{Client, ClientId}, messages::{ClientToServerMessage, ClientToServerMessageType, ServerToClientMessage}
|
chunk::CHUNK_SIZE,
|
||||||
|
queue::QueuedBlock,
|
||||||
|
networking::{
|
||||||
|
channels::Channel,
|
||||||
|
client::{Client, ClientId},
|
||||||
|
messages::{ClientToServerMessage, ClientToServerMessageType, ServerToClientMessage}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use uflow::{server::RemoteClient, SendMode};
|
use uflow::{server::RemoteClient, SendMode};
|
||||||
use lz4_flex::compress_prepend_size as lz4_compress;
|
use lz4_flex::compress_prepend_size as lz4_compress;
|
||||||
|
@ -21,7 +27,15 @@ pub mod tasks;
|
||||||
|
|
||||||
use chunk::Chunk;
|
use chunk::Chunk;
|
||||||
|
|
||||||
use self::{tasks::{ChunkTaskManager, ChunkTask, ChunkTaskResponse, init_chunk_task_manager}, chunk::ChunkState};
|
use self::{
|
||||||
|
tasks::{ChunkTaskManager, ChunkTask, ChunkTaskResponse, init_chunk_task_manager},
|
||||||
|
chunk::ChunkState
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Unique, Default)]
|
||||||
|
pub struct LocalBlockQueue {
|
||||||
|
pub queue: Vec<QueuedBlock>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Unique, Default)]
|
#[derive(Unique, Default)]
|
||||||
pub struct ChunkManager {
|
pub struct ChunkManager {
|
||||||
|
@ -106,6 +120,7 @@ fn process_finished_tasks(
|
||||||
mut chunk_manager: UniqueViewMut<ChunkManager>,
|
mut chunk_manager: UniqueViewMut<ChunkManager>,
|
||||||
id_map: UniqueView<ClientIdMap>,
|
id_map: UniqueView<ClientIdMap>,
|
||||||
client_addr: View<ClientAddress>,
|
client_addr: View<ClientAddress>,
|
||||||
|
mut local_queue: UniqueViewMut<LocalBlockQueue>,
|
||||||
) {
|
) {
|
||||||
'outer: while let Some(res) = task_manager.receive() {
|
'outer: while let Some(res) = task_manager.receive() {
|
||||||
let ChunkTaskResponse::ChunkLoaded { chunk_position, blocks, queue } = res;
|
let ChunkTaskResponse::ChunkLoaded { chunk_position, blocks, queue } = res;
|
||||||
|
@ -120,12 +135,14 @@ fn process_finished_tasks(
|
||||||
chunk.state = ChunkState::Loaded;
|
chunk.state = ChunkState::Loaded;
|
||||||
chunk.blocks = Some(blocks.clone());
|
chunk.blocks = Some(blocks.clone());
|
||||||
|
|
||||||
|
local_queue.queue.extend_from_slice(&queue);
|
||||||
|
|
||||||
log::debug!("Chunk {chunk_position} loaded, {} subs", chunk.subscriptions.len());
|
log::debug!("Chunk {chunk_position} loaded, {} subs", chunk.subscriptions.len());
|
||||||
|
|
||||||
let chunk_packet = &ServerToClientMessage::ChunkResponse {
|
let chunk_packet = &ServerToClientMessage::ChunkResponse {
|
||||||
chunk: chunk_position,
|
chunk: chunk_position,
|
||||||
data: blocks,
|
data: blocks,
|
||||||
queued: queue
|
queued: queue //should this be here?
|
||||||
};
|
};
|
||||||
|
|
||||||
for &subscriber in &chunk.subscriptions {
|
for &subscriber in &chunk.subscriptions {
|
||||||
|
@ -157,6 +174,7 @@ fn process_block_queue_messages(
|
||||||
addr_map: UniqueView<ClientAddressMap>,
|
addr_map: UniqueView<ClientAddressMap>,
|
||||||
clients: View<Client>,
|
clients: View<Client>,
|
||||||
addrs: View<ClientAddress>,
|
addrs: View<ClientAddress>,
|
||||||
|
mut queue: UniqueViewMut<LocalBlockQueue>,
|
||||||
) {
|
) {
|
||||||
for event in &events.0 {
|
for event in &events.0 {
|
||||||
let Some(message) = check_message_auth
|
let Some(message) = check_message_auth
|
||||||
|
@ -164,7 +182,10 @@ fn process_block_queue_messages(
|
||||||
(&server, event, &clients, &addr_map) else { continue };
|
(&server, event, &clients, &addr_map) else { continue };
|
||||||
|
|
||||||
let ClientToServerMessage::QueueBlock { item } = message.message else { unreachable!() };
|
let ClientToServerMessage::QueueBlock { item } = message.message else { unreachable!() };
|
||||||
//TODO place in our own queue, for now just send to other clients
|
|
||||||
|
//place in our local world
|
||||||
|
queue.queue.push(item);
|
||||||
|
|
||||||
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
|
||||||
|
@ -188,23 +209,48 @@ fn process_block_queue_messages(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_chunk_manager(
|
fn process_block_queue(
|
||||||
|
mut chunk_manager: UniqueViewMut<ChunkManager>,
|
||||||
|
mut queue: UniqueViewMut<LocalBlockQueue>,
|
||||||
|
) {
|
||||||
|
let initial_len = queue.queue.len();
|
||||||
|
queue.queue.retain(|item| {
|
||||||
|
let chunk_position = item.position.div_euclid(IVec3::splat(CHUNK_SIZE as i32));
|
||||||
|
let block_position = item.position.rem_euclid(IVec3::splat(CHUNK_SIZE as i32));
|
||||||
|
let Some(chunk) = chunk_manager.chunks.get_mut(&chunk_position) else {
|
||||||
|
return true
|
||||||
|
};
|
||||||
|
let Some(blocks) = &mut chunk.blocks else {
|
||||||
|
return true
|
||||||
|
};
|
||||||
|
blocks[block_position.x as usize][block_position.y as usize][block_position.z as usize] = item.block_type;
|
||||||
|
false
|
||||||
|
});
|
||||||
|
if initial_len != queue.queue.len() {
|
||||||
|
log::debug!("queue processed {}/{} items", initial_len - queue.queue.len(), initial_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// init local block queue and chunk manager
|
||||||
|
fn init_chunk_manager_and_block_queue(
|
||||||
storages: AllStoragesView
|
storages: AllStoragesView
|
||||||
) {
|
) {
|
||||||
storages.add_unique(ChunkManager::new());
|
storages.add_unique(ChunkManager::new());
|
||||||
|
storages.add_unique(LocalBlockQueue::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_world() -> Workload {
|
pub fn init_world() -> Workload {
|
||||||
(
|
(
|
||||||
init_chunk_manager,
|
init_chunk_manager_and_block_queue,
|
||||||
init_chunk_task_manager,
|
init_chunk_task_manager,
|
||||||
).into_workload()
|
).into_workload()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_world() -> Workload {
|
pub fn update_world() -> Workload {
|
||||||
(
|
(
|
||||||
process_chunk_requests,
|
|
||||||
process_finished_tasks,
|
process_finished_tasks,
|
||||||
process_block_queue_messages,
|
process_block_queue_messages,
|
||||||
).into_workload()
|
process_block_queue,
|
||||||
|
process_chunk_requests,
|
||||||
|
).into_sequential_workload()
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ winit = { version = "0.29", features = ["android-native-activity"] }
|
||||||
glutin-winit = "0.4"
|
glutin-winit = "0.4"
|
||||||
raw-window-handle = "=0.5.*"
|
raw-window-handle = "=0.5.*"
|
||||||
glam = { version = "0.27", features = ["debug-glam-assert", "fast-math"] }
|
glam = { version = "0.27", features = ["debug-glam-assert", "fast-math"] }
|
||||||
image = { version = "0.24", default_features = false, features = ["png"] }
|
image = { version = "0.25", default_features = false, features = ["png"] }
|
||||||
strum = { version = "0.26", features = ["derive"] }
|
strum = { version = "0.26", features = ["derive"] }
|
||||||
hashbrown = "0.14"
|
hashbrown = "0.14"
|
||||||
nohash-hasher = "0.2"
|
nohash-hasher = "0.2"
|
||||||
|
@ -35,13 +35,14 @@ lz4_flex = { version = "0.11", default-features = false, features = ["std"] }
|
||||||
static_assertions = "1.1"
|
static_assertions = "1.1"
|
||||||
tinyset = "0.4"
|
tinyset = "0.4"
|
||||||
serde_json = { version = "1.0", optional = true } #only used for `generate_visualizer_data`
|
serde_json = { version = "1.0", optional = true } #only used for `generate_visualizer_data`
|
||||||
|
rand = { version = "0.8", features = ["alloc", "small_rng"]}
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
android-activity = "^0.5.2"
|
android-activity = "^0.5.2"
|
||||||
ndk = "0.8"
|
ndk = "0.8"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
winapi = "0.3"
|
winapi = { version = "0.3", features = ["wincon"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["raw-evt"]
|
default = ["raw-evt"]
|
||||||
|
@ -49,7 +50,7 @@ raw-evt = [] #required for mouse input, but breaks keyboard on android
|
||||||
generate_visualizer_data = ["dep:serde_json", "shipyard/serde1"]
|
generate_visualizer_data = ["dep:serde_json", "shipyard/serde1"]
|
||||||
safe_lz4 = ["lz4_flex/safe-encode", "lz4_flex/safe-decode"]
|
safe_lz4 = ["lz4_flex/safe-encode", "lz4_flex/safe-decode"]
|
||||||
parallel = ["shipyard/parallel"] # causes some serious issues!
|
parallel = ["shipyard/parallel"] # causes some serious issues!
|
||||||
nightly = ["hashbrown/nightly", "glam/core-simd", "static_assertions/nightly", "lz4_flex/nightly", "kubi-shared/nightly"]
|
nightly = ["hashbrown/nightly", "glam/core-simd", "static_assertions/nightly", "lz4_flex/nightly", "kubi-shared/nightly", "rand/nightly"]
|
||||||
|
|
||||||
#part of wip android support
|
#part of wip android support
|
||||||
[package.metadata.android]
|
[package.metadata.android]
|
||||||
|
|
|
@ -1,48 +1,49 @@
|
||||||
use glam::{Vec3, Mat4};
|
use glam::{Vec3, Mat4};
|
||||||
use shipyard::{ViewMut, View, IntoIter, Workload, IntoWorkload, track, UniqueView, SystemModificator};
|
use shipyard::{ViewMut, View, IntoIter, Workload, IntoWorkload, track, UniqueView, SystemModificator};
|
||||||
use crate::{transform::Transform, rendering::WindowSize, events::WindowResizedEvent};
|
use crate::{transform::Transform, rendering::WindowSize, events::WindowResizedEvent};
|
||||||
use super::Camera;
|
use super::Camera;
|
||||||
|
|
||||||
//maybe parallelize these two?
|
//maybe parallelize these two?
|
||||||
|
|
||||||
fn update_view_matrix(
|
fn update_view_matrix(
|
||||||
mut vm_camera: ViewMut<Camera>,
|
mut vm_camera: ViewMut<Camera>,
|
||||||
v_transform: View<Transform, track::All>
|
v_transform: View<Transform, track::All>
|
||||||
) {
|
) {
|
||||||
for (mut camera, transform) in (&mut vm_camera, v_transform.inserted_or_modified()).iter() {
|
for (mut camera, transform) in (&mut vm_camera, v_transform.inserted_or_modified()).iter() {
|
||||||
let (_, rotation, translation) = transform.0.to_scale_rotation_translation();
|
let (_, rotation, translation) = transform.0.to_scale_rotation_translation();
|
||||||
let direction = (rotation.normalize() * Vec3::NEG_Z).normalize();
|
let direction = (rotation.normalize() * Vec3::NEG_Z).normalize();
|
||||||
camera.view_matrix = Mat4::look_to_rh(translation, direction, camera.up);
|
camera.view_matrix = Mat4::look_to_rh(translation, direction, camera.up);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_perspective_matrix(
|
fn update_perspective_matrix(
|
||||||
mut vm_camera: ViewMut<Camera>,
|
mut vm_camera: ViewMut<Camera>,
|
||||||
size: UniqueView<WindowSize>,
|
size: UniqueView<WindowSize>,
|
||||||
) {
|
) {
|
||||||
for mut camera in (&mut vm_camera).iter() {
|
for mut camera in (&mut vm_camera).iter() {
|
||||||
camera.perspective_matrix = Mat4::perspective_rh_gl(
|
camera.perspective_matrix = Mat4::perspective_rh_gl(
|
||||||
camera.fov,
|
camera.fov,
|
||||||
size.0.x as f32 / size.0.y as f32,
|
size.0.x as f32 / size.0.y as f32,
|
||||||
camera.z_near,
|
camera.z_near,
|
||||||
camera.z_far,
|
camera.z_far,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn need_perspective_calc(
|
fn need_perspective_calc(
|
||||||
v_camera: View<Camera>,
|
v_camera: View<Camera>,
|
||||||
resize_event: View<WindowResizedEvent>,
|
resize_event: View<WindowResizedEvent>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
(resize_event.len() > 0) ||
|
(resize_event.len() > 0) ||
|
||||||
(v_camera.iter().any(|camera| {
|
(v_camera.iter().any(|camera| {
|
||||||
camera.perspective_matrix == Mat4::default()
|
camera.perspective_matrix == Mat4::default()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_matrices() -> Workload {
|
pub fn update_matrices() -> Workload {
|
||||||
(
|
(
|
||||||
update_view_matrix,
|
update_view_matrix,
|
||||||
update_perspective_matrix.run_if(need_perspective_calc),
|
//update_perspective_matrix,
|
||||||
).into_sequential_workload()
|
update_perspective_matrix.run_if(need_perspective_calc),
|
||||||
}
|
).into_sequential_workload()
|
||||||
|
}
|
||||||
|
|
64
kubi/src/chat.rs
Normal file
64
kubi/src/chat.rs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
use kubi_shared::networking::client::ClientId;
|
||||||
|
use shipyard::{AllStoragesView, Unique, UniqueViewMut};
|
||||||
|
|
||||||
|
pub enum ChatMessage {
|
||||||
|
PlayerMessage {
|
||||||
|
id: ClientId,
|
||||||
|
username: String,
|
||||||
|
message: String,
|
||||||
|
},
|
||||||
|
PlayerJoin {
|
||||||
|
id: ClientId,
|
||||||
|
username: String,
|
||||||
|
},
|
||||||
|
PlayerLeave {
|
||||||
|
id: ClientId,
|
||||||
|
username: String,
|
||||||
|
},
|
||||||
|
System(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl ChatMessage {
|
||||||
|
// pub fn render() -> String {
|
||||||
|
// todo!() //TODO
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[derive(Unique, Default)]
|
||||||
|
pub struct ChatHistory {
|
||||||
|
pub messages: Vec<ChatMessage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChatHistory {
|
||||||
|
pub fn add_message(&mut self, message: ChatMessage) {
|
||||||
|
self.messages.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_chat_message(&mut self, id: ClientId, username: String, message: String,) {
|
||||||
|
self.messages.push(ChatMessage::PlayerMessage { id, username, message });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_player_join(&mut self, id: ClientId, username: String) {
|
||||||
|
self.messages.push(ChatMessage::PlayerJoin { id, username });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_player_leave(&mut self, id: ClientId, username: String) {
|
||||||
|
self.messages.push(ChatMessage::PlayerLeave { id, username });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_system_message(&mut self, message: String) {
|
||||||
|
self.messages.push(ChatMessage::System(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_messages(&self) -> &[ChatMessage] {
|
||||||
|
&self.messages[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_chat_manager(
|
||||||
|
storages: AllStoragesView,
|
||||||
|
) {
|
||||||
|
let mut chat_manager = ChatHistory::default();
|
||||||
|
chat_manager.add_system_message("Welcome to Kubi! Chat messages will appear here".to_string());
|
||||||
|
storages.add_unique(chat_manager);
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
use shipyard::{AllStoragesView, Unique, NonSendSync, UniqueView, UniqueViewMut};
|
use shipyard::{AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
|
||||||
use crate::rendering::Renderer;
|
use crate::{events::InputDeviceEvent, rendering::Renderer};
|
||||||
use winit::window::CursorGrabMode;
|
use winit::{
|
||||||
|
event::{DeviceEvent, ElementState, RawKeyEvent},
|
||||||
|
keyboard::{KeyCode, PhysicalKey},
|
||||||
|
window::CursorGrabMode
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Unique)]
|
#[derive(Unique)]
|
||||||
pub struct CursorLock(pub bool);
|
pub struct CursorLock(pub bool);
|
||||||
|
@ -34,3 +38,18 @@ pub fn lock_cursor_now(
|
||||||
) {
|
) {
|
||||||
lock.0 = true
|
lock.0 = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// XXX: this is a huge hack
|
||||||
|
pub fn debug_toggle_lock(
|
||||||
|
mut lock: UniqueViewMut<CursorLock>,
|
||||||
|
device_events: View<InputDeviceEvent>,
|
||||||
|
) {
|
||||||
|
for evt in device_events.iter() {
|
||||||
|
if let DeviceEvent::Key(RawKeyEvent {
|
||||||
|
physical_key: PhysicalKey::Code(KeyCode::F3),
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
}) = evt.event {
|
||||||
|
lock.0 = !lock.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -18,8 +18,9 @@ pub(crate) use kubi_shared::transform;
|
||||||
mod ui {
|
mod ui {
|
||||||
pub(crate) mod loading_screen;
|
pub(crate) mod loading_screen;
|
||||||
pub(crate) mod connecting_screen;
|
pub(crate) mod connecting_screen;
|
||||||
|
pub(crate) mod chat_ui;
|
||||||
}
|
}
|
||||||
pub(crate) use ui::{loading_screen, connecting_screen};
|
pub(crate) use ui::{loading_screen, connecting_screen, chat_ui};
|
||||||
|
|
||||||
pub(crate) mod rendering;
|
pub(crate) mod rendering;
|
||||||
pub(crate) mod world;
|
pub(crate) mod world;
|
||||||
|
@ -42,6 +43,7 @@ pub(crate) mod color;
|
||||||
pub(crate) mod fixed_timestamp;
|
pub(crate) mod fixed_timestamp;
|
||||||
pub(crate) mod filesystem;
|
pub(crate) mod filesystem;
|
||||||
pub(crate) mod client_physics;
|
pub(crate) mod client_physics;
|
||||||
|
pub(crate) mod chat;
|
||||||
|
|
||||||
use world::{
|
use world::{
|
||||||
init_game_world,
|
init_game_world,
|
||||||
|
@ -76,7 +78,7 @@ use rendering::{
|
||||||
};
|
};
|
||||||
use block_placement::update_block_placement;
|
use block_placement::update_block_placement;
|
||||||
use delta_time::{DeltaTime, init_delta_time};
|
use delta_time::{DeltaTime, init_delta_time};
|
||||||
use cursor_lock::{insert_lock_state, update_cursor_lock_state, lock_cursor_now};
|
use cursor_lock::{debug_toggle_lock, insert_lock_state, lock_cursor_now, update_cursor_lock_state};
|
||||||
use control_flow::{exit_on_esc, insert_control_flow_unique, RequestExit};
|
use control_flow::{exit_on_esc, insert_control_flow_unique, RequestExit};
|
||||||
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, is_singleplayer};
|
use networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer};
|
||||||
|
@ -87,6 +89,8 @@ use connecting_screen::update_connecting_screen;
|
||||||
use fixed_timestamp::init_fixed_timestamp_storage;
|
use fixed_timestamp::init_fixed_timestamp_storage;
|
||||||
use filesystem::AssetManager;
|
use filesystem::AssetManager;
|
||||||
use client_physics::{init_client_physics, update_client_physics_late};
|
use client_physics::{init_client_physics, update_client_physics_late};
|
||||||
|
use chat_ui::render_chat;
|
||||||
|
use chat::init_chat_manager;
|
||||||
|
|
||||||
/// stuff required to init the renderer and other basic systems
|
/// stuff required to init the renderer and other basic systems
|
||||||
fn pre_startup() -> Workload {
|
fn pre_startup() -> Workload {
|
||||||
|
@ -111,11 +115,13 @@ fn startup() -> Workload {
|
||||||
insert_control_flow_unique,
|
insert_control_flow_unique,
|
||||||
init_delta_time,
|
init_delta_time,
|
||||||
init_client_physics,
|
init_client_physics,
|
||||||
|
init_chat_manager,
|
||||||
).into_sequential_workload()
|
).into_sequential_workload()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update() -> Workload {
|
fn update() -> Workload {
|
||||||
(
|
(
|
||||||
|
debug_toggle_lock,
|
||||||
update_window_size,
|
update_window_size,
|
||||||
update_cursor_lock_state,
|
update_cursor_lock_state,
|
||||||
process_inputs,
|
process_inputs,
|
||||||
|
@ -144,6 +150,7 @@ fn update() -> Workload {
|
||||||
update_raycasts,
|
update_raycasts,
|
||||||
update_block_placement,
|
update_block_placement,
|
||||||
apply_queued_blocks,
|
apply_queued_blocks,
|
||||||
|
render_chat,
|
||||||
).into_sequential_workload().run_if(is_ingame),
|
).into_sequential_workload().run_if(is_ingame),
|
||||||
update_networking_late.run_if(is_multiplayer),
|
update_networking_late.run_if(is_multiplayer),
|
||||||
compute_cameras,
|
compute_cameras,
|
||||||
|
|
|
@ -5,9 +5,21 @@ use kubi_shared::networking::{
|
||||||
state::ClientJoinState,
|
state::ClientJoinState,
|
||||||
channels::Channel,
|
channels::Channel,
|
||||||
};
|
};
|
||||||
use crate::player::{spawn_local_player_multiplayer, spawn_remote_player_multiplayer};
|
use rand::prelude::*;
|
||||||
|
use crate::{chat::ChatHistory, player::{spawn_local_player_multiplayer, spawn_remote_player_multiplayer}};
|
||||||
use super::{UdpClient, NetworkEvent};
|
use super::{UdpClient, NetworkEvent};
|
||||||
|
|
||||||
|
const USERNAME_BANK: &[&str] = &[
|
||||||
|
"XxX-FishFucker-69420",
|
||||||
|
"Sbeve34",
|
||||||
|
"ShadowBladeX",
|
||||||
|
"CyberNinja92",
|
||||||
|
"sputnik1",
|
||||||
|
"dumbpotato",
|
||||||
|
"FortNiteNinja",
|
||||||
|
"MinecraftMiner",
|
||||||
|
];
|
||||||
|
|
||||||
#[derive(Unique)]
|
#[derive(Unique)]
|
||||||
pub struct ConnectionRejectionReason {
|
pub struct ConnectionRejectionReason {
|
||||||
pub reason: String,
|
pub reason: String,
|
||||||
|
@ -23,7 +35,8 @@ pub fn set_client_join_state_to_connected(
|
||||||
pub fn say_hello(
|
pub fn say_hello(
|
||||||
mut client: UniqueViewMut<UdpClient>,
|
mut client: UniqueViewMut<UdpClient>,
|
||||||
) {
|
) {
|
||||||
let username = "XxX-FishFucker-69420-XxX".into();
|
let mut rng = thread_rng();
|
||||||
|
let username = (*USERNAME_BANK.choose(&mut rng).unwrap()).to_owned();
|
||||||
let password = None;
|
let password = None;
|
||||||
log::info!("Authenticating");
|
log::info!("Authenticating");
|
||||||
client.0.send(
|
client.0.send(
|
||||||
|
@ -64,7 +77,10 @@ pub fn check_server_hello_response(
|
||||||
// direction: Quat,
|
// direction: Quat,
|
||||||
// health: Health,
|
// health: Health,
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
let client_id = init.user.client_id;
|
||||||
|
let username = init.user.username.clone();
|
||||||
|
|
||||||
//Add components to main player
|
//Add components to main player
|
||||||
spawn_local_player_multiplayer(&mut storages, init.user);
|
spawn_local_player_multiplayer(&mut storages, init.user);
|
||||||
|
|
||||||
|
@ -78,6 +94,10 @@ pub fn check_server_hello_response(
|
||||||
*join_state = ClientJoinState::Joined;
|
*join_state = ClientJoinState::Joined;
|
||||||
|
|
||||||
log::info!("Joined the server!");
|
log::info!("Joined the server!");
|
||||||
|
|
||||||
|
// Send chat message
|
||||||
|
let mut chat = storages.borrow::<UniqueViewMut<ChatHistory>>().unwrap();
|
||||||
|
chat.add_player_join(client_id, username);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_server_fuck_off_response(
|
pub fn check_server_fuck_off_response(
|
||||||
|
|
|
@ -4,12 +4,13 @@ use uflow::{SendMode, client::Event as ClientEvent};
|
||||||
use kubi_shared::{
|
use kubi_shared::{
|
||||||
transform::Transform,
|
transform::Transform,
|
||||||
networking::{
|
networking::{
|
||||||
messages::{ClientToServerMessage, ServerToClientMessage, ServerToClientMessageType},
|
|
||||||
channels::Channel,
|
channels::Channel,
|
||||||
client::ClientIdMap,
|
client::{ClientIdMap, Username},
|
||||||
|
messages::{ClientToServerMessage, ServerToClientMessage, ServerToClientMessageType},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
chat::ChatHistory,
|
||||||
events::player_actions::PlayerActionEvent,
|
events::player_actions::PlayerActionEvent,
|
||||||
player::spawn_remote_player_multiplayer,
|
player::spawn_remote_player_multiplayer,
|
||||||
};
|
};
|
||||||
|
@ -96,6 +97,9 @@ 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);
|
||||||
|
let mut chat = storages.borrow::<UniqueViewMut<ChatHistory>>().unwrap();
|
||||||
|
chat.add_player_join(init.client_id, init.username.clone());
|
||||||
|
drop(chat);
|
||||||
spawn_remote_player_multiplayer(&mut storages, init);
|
spawn_remote_player_multiplayer(&mut storages, init);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,12 +124,21 @@ pub fn receive_player_disconnect_events(
|
||||||
for message in messages {
|
for message in messages {
|
||||||
let ServerToClientMessage::PlayerDisconnected { id } = message else { unreachable!() };
|
let ServerToClientMessage::PlayerDisconnected { id } = message else { unreachable!() };
|
||||||
log::info!("player disconnected: {}", id);
|
log::info!("player disconnected: {}", id);
|
||||||
|
|
||||||
let mut id_map = storages.borrow::<UniqueViewMut<ClientIdMap>>().unwrap();
|
let mut id_map = storages.borrow::<UniqueViewMut<ClientIdMap>>().unwrap();
|
||||||
let Some(ent_id) = id_map.0.remove(&id) else {
|
let Some(ent_id) = id_map.0.remove(&id) else {
|
||||||
log::warn!("Disconnected player entity not found in client-id map");
|
log::warn!("Disconnected player entity not found in client-id map");
|
||||||
continue
|
continue
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let username = storages.get::<&Username>(ent_id).unwrap();
|
||||||
|
let mut chat = storages.borrow::<UniqueViewMut<ChatHistory>>().unwrap();
|
||||||
|
chat.add_player_leave(id, username.0.to_string());
|
||||||
|
|
||||||
|
drop(chat);
|
||||||
drop(id_map);
|
drop(id_map);
|
||||||
|
drop(username);
|
||||||
|
|
||||||
if !storages.delete_entity(ent_id) {
|
if !storages.delete_entity(ent_id) {
|
||||||
log::warn!("Disconnected player entity not found in storage");
|
log::warn!("Disconnected player entity not found in storage");
|
||||||
}
|
}
|
||||||
|
|
43
kubi/src/ui/chat_ui.rs
Normal file
43
kubi/src/ui/chat_ui.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
use hui::{color, element::{container::Container, text::Text, UiElementExt}, layout::Alignment, size};
|
||||||
|
use shipyard::{NonSendSync, UniqueView, UniqueViewMut};
|
||||||
|
use crate::{chat::{ChatHistory, ChatMessage}, hui_integration::UiState, rendering::WindowSize};
|
||||||
|
|
||||||
|
pub fn render_chat(
|
||||||
|
mut hui: NonSendSync<UniqueViewMut<UiState>>,
|
||||||
|
size: UniqueView<WindowSize>,
|
||||||
|
chat: UniqueView<ChatHistory>,
|
||||||
|
) {
|
||||||
|
let messages = chat.get_messages();
|
||||||
|
if messages.is_empty() { return }
|
||||||
|
Container::default()
|
||||||
|
.with_size(size!(100%, 100%))
|
||||||
|
.with_align((Alignment::Begin, Alignment::End))
|
||||||
|
.with_children(|ui| {
|
||||||
|
for message in messages.iter().rev().take(10).rev() {
|
||||||
|
let (text, color) = match message {
|
||||||
|
ChatMessage::PlayerMessage { username, id, message } => {
|
||||||
|
(format!("{username} ({id}): {message}"), color::CYAN)
|
||||||
|
}
|
||||||
|
ChatMessage::PlayerJoin { username, id } => {
|
||||||
|
(format!("{username} ({id}) joined the game"), color::YELLOW)
|
||||||
|
}
|
||||||
|
ChatMessage::PlayerLeave { username, id } => {
|
||||||
|
(format!("{username} ({id}) left the game"), color::YELLOW)
|
||||||
|
}
|
||||||
|
ChatMessage::System(message) => {
|
||||||
|
(message.clone(), color::WHITE)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Container::default()
|
||||||
|
.with_background((0., 0., 0., 0.5))
|
||||||
|
.with_padding((5., 2.))
|
||||||
|
.with_children(|ui| {
|
||||||
|
Text::new(text)
|
||||||
|
.with_color(color)
|
||||||
|
.add_child(ui)
|
||||||
|
})
|
||||||
|
.add_child(ui);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.add_root(&mut hui.hui, size.0.as_vec2());
|
||||||
|
}
|
Loading…
Reference in a new issue