Compare commits

..

No commits in common. "772a8ea7db51a5a546d8649ec29b73da73fac5d7" and "043bb873c435f6462a0ef624e0110040db864355" have entirely different histories.

10 changed files with 320 additions and 527 deletions

493
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,8 @@
use shipyard::{Unique, UniqueView, UniqueViewMut, Workload, IntoWorkload, AllStoragesView, View, Get, NonSendSync, IntoIter};
use glam::IVec3;
use hashbrown::HashMap;
use kubi_shared::{
chunk::CHUNK_SIZE,
queue::QueuedBlock,
networking::{
channels::Channel,
client::{Client, ClientId},
messages::{ClientToServerMessage, ClientToServerMessageType, ServerToClientMessage}
},
use kubi_shared::networking::{
channels::Channel, client::{Client, ClientId}, messages::{ClientToServerMessage, ClientToServerMessageType, ServerToClientMessage}
};
use uflow::{server::RemoteClient, SendMode};
use lz4_flex::compress_prepend_size as lz4_compress;
@ -27,15 +21,7 @@ pub mod tasks;
use chunk::Chunk;
use self::{
tasks::{ChunkTaskManager, ChunkTask, ChunkTaskResponse, init_chunk_task_manager},
chunk::ChunkState
};
#[derive(Unique, Default)]
pub struct LocalBlockQueue {
pub queue: Vec<QueuedBlock>,
}
use self::{tasks::{ChunkTaskManager, ChunkTask, ChunkTaskResponse, init_chunk_task_manager}, chunk::ChunkState};
#[derive(Unique, Default)]
pub struct ChunkManager {
@ -120,7 +106,6 @@ fn process_finished_tasks(
mut chunk_manager: UniqueViewMut<ChunkManager>,
id_map: UniqueView<ClientIdMap>,
client_addr: View<ClientAddress>,
mut local_queue: UniqueViewMut<LocalBlockQueue>,
) {
'outer: while let Some(res) = task_manager.receive() {
let ChunkTaskResponse::ChunkLoaded { chunk_position, blocks, queue } = res;
@ -135,14 +120,12 @@ fn process_finished_tasks(
chunk.state = ChunkState::Loaded;
chunk.blocks = Some(blocks.clone());
local_queue.queue.extend_from_slice(&queue);
log::debug!("Chunk {chunk_position} loaded, {} subs", chunk.subscriptions.len());
let chunk_packet = &ServerToClientMessage::ChunkResponse {
chunk: chunk_position,
data: blocks,
queued: queue //should this be here?
queued: queue
};
for &subscriber in &chunk.subscriptions {
@ -174,7 +157,6 @@ fn process_block_queue_messages(
addr_map: UniqueView<ClientAddressMap>,
clients: View<Client>,
addrs: View<ClientAddress>,
mut queue: UniqueViewMut<LocalBlockQueue>,
) {
for event in &events.0 {
let Some(message) = check_message_auth
@ -182,10 +164,7 @@ fn process_block_queue_messages(
(&server, event, &clients, &addr_map) else { continue };
let ClientToServerMessage::QueueBlock { item } = message.message else { unreachable!() };
//place in our local world
queue.queue.push(item);
//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
@ -209,48 +188,23 @@ fn process_block_queue_messages(
}
}
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(
fn init_chunk_manager(
storages: AllStoragesView
) {
storages.add_unique(ChunkManager::new());
storages.add_unique(LocalBlockQueue::default());
}
pub fn init_world() -> Workload {
(
init_chunk_manager_and_block_queue,
init_chunk_manager,
init_chunk_task_manager,
).into_workload()
}
pub fn update_world() -> Workload {
(
process_chunk_requests,
process_finished_tasks,
process_block_queue_messages,
process_block_queue,
process_chunk_requests,
).into_sequential_workload()
).into_workload()
}

View file

@ -20,7 +20,7 @@ winit = { version = "0.29", features = ["android-native-activity"] }
glutin-winit = "0.4"
raw-window-handle = "=0.5.*"
glam = { version = "0.27", features = ["debug-glam-assert", "fast-math"] }
image = { version = "0.25", default_features = false, features = ["png"] }
image = { version = "0.24", default_features = false, features = ["png"] }
strum = { version = "0.26", features = ["derive"] }
hashbrown = "0.14"
nohash-hasher = "0.2"
@ -35,14 +35,13 @@ lz4_flex = { version = "0.11", default-features = false, features = ["std"] }
static_assertions = "1.1"
tinyset = "0.4"
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]
android-activity = "^0.5.2"
ndk = "0.8"
[target.'cfg(target_os = "windows")'.dependencies]
winapi = { version = "0.3", features = ["wincon"] }
winapi = "0.3"
[features]
default = ["raw-evt"]
@ -50,7 +49,7 @@ raw-evt = [] #required for mouse input, but breaks keyboard on android
generate_visualizer_data = ["dep:serde_json", "shipyard/serde1"]
safe_lz4 = ["lz4_flex/safe-encode", "lz4_flex/safe-decode"]
parallel = ["shipyard/parallel"] # causes some serious issues!
nightly = ["hashbrown/nightly", "glam/core-simd", "static_assertions/nightly", "lz4_flex/nightly", "kubi-shared/nightly", "rand/nightly"]
nightly = ["hashbrown/nightly", "glam/core-simd", "static_assertions/nightly", "lz4_flex/nightly", "kubi-shared/nightly"]
#part of wip android support
[package.metadata.android]

View file

@ -1,49 +1,48 @@
use glam::{Vec3, Mat4};
use shipyard::{ViewMut, View, IntoIter, Workload, IntoWorkload, track, UniqueView, SystemModificator};
use crate::{transform::Transform, rendering::WindowSize, events::WindowResizedEvent};
use super::Camera;
//maybe parallelize these two?
fn update_view_matrix(
mut vm_camera: ViewMut<Camera>,
v_transform: View<Transform, track::All>
) {
for (mut camera, transform) in (&mut vm_camera, v_transform.inserted_or_modified()).iter() {
let (_, rotation, translation) = transform.0.to_scale_rotation_translation();
let direction = (rotation.normalize() * Vec3::NEG_Z).normalize();
camera.view_matrix = Mat4::look_to_rh(translation, direction, camera.up);
}
}
fn update_perspective_matrix(
mut vm_camera: ViewMut<Camera>,
size: UniqueView<WindowSize>,
) {
for mut camera in (&mut vm_camera).iter() {
camera.perspective_matrix = Mat4::perspective_rh_gl(
camera.fov,
size.0.x as f32 / size.0.y as f32,
camera.z_near,
camera.z_far,
)
}
}
fn need_perspective_calc(
v_camera: View<Camera>,
resize_event: View<WindowResizedEvent>,
) -> bool {
(resize_event.len() > 0) ||
(v_camera.iter().any(|camera| {
camera.perspective_matrix == Mat4::default()
}))
}
pub fn update_matrices() -> Workload {
(
update_view_matrix,
//update_perspective_matrix,
update_perspective_matrix.run_if(need_perspective_calc),
).into_sequential_workload()
}
use glam::{Vec3, Mat4};
use shipyard::{ViewMut, View, IntoIter, Workload, IntoWorkload, track, UniqueView, SystemModificator};
use crate::{transform::Transform, rendering::WindowSize, events::WindowResizedEvent};
use super::Camera;
//maybe parallelize these two?
fn update_view_matrix(
mut vm_camera: ViewMut<Camera>,
v_transform: View<Transform, track::All>
) {
for (mut camera, transform) in (&mut vm_camera, v_transform.inserted_or_modified()).iter() {
let (_, rotation, translation) = transform.0.to_scale_rotation_translation();
let direction = (rotation.normalize() * Vec3::NEG_Z).normalize();
camera.view_matrix = Mat4::look_to_rh(translation, direction, camera.up);
}
}
fn update_perspective_matrix(
mut vm_camera: ViewMut<Camera>,
size: UniqueView<WindowSize>,
) {
for mut camera in (&mut vm_camera).iter() {
camera.perspective_matrix = Mat4::perspective_rh_gl(
camera.fov,
size.0.x as f32 / size.0.y as f32,
camera.z_near,
camera.z_far,
)
}
}
fn need_perspective_calc(
v_camera: View<Camera>,
resize_event: View<WindowResizedEvent>,
) -> bool {
(resize_event.len() > 0) ||
(v_camera.iter().any(|camera| {
camera.perspective_matrix == Mat4::default()
}))
}
pub fn update_matrices() -> Workload {
(
update_view_matrix,
update_perspective_matrix.run_if(need_perspective_calc),
).into_sequential_workload()
}

View file

@ -1,64 +0,0 @@
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);
}

View file

@ -1,10 +1,6 @@
use shipyard::{AllStoragesView, IntoIter, NonSendSync, Unique, UniqueView, UniqueViewMut, View};
use crate::{events::InputDeviceEvent, rendering::Renderer};
use winit::{
event::{DeviceEvent, ElementState, RawKeyEvent},
keyboard::{KeyCode, PhysicalKey},
window::CursorGrabMode
};
use shipyard::{AllStoragesView, Unique, NonSendSync, UniqueView, UniqueViewMut};
use crate::rendering::Renderer;
use winit::window::CursorGrabMode;
#[derive(Unique)]
pub struct CursorLock(pub bool);
@ -38,18 +34,3 @@ pub fn lock_cursor_now(
) {
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;
}
}
}

View file

@ -18,9 +18,8 @@ pub(crate) use kubi_shared::transform;
mod ui {
pub(crate) mod loading_screen;
pub(crate) mod connecting_screen;
pub(crate) mod chat_ui;
}
pub(crate) use ui::{loading_screen, connecting_screen, chat_ui};
pub(crate) use ui::{loading_screen, connecting_screen};
pub(crate) mod rendering;
pub(crate) mod world;
@ -43,7 +42,6 @@ pub(crate) mod color;
pub(crate) mod fixed_timestamp;
pub(crate) mod filesystem;
pub(crate) mod client_physics;
pub(crate) mod chat;
use world::{
init_game_world,
@ -78,7 +76,7 @@ use rendering::{
};
use block_placement::update_block_placement;
use delta_time::{DeltaTime, init_delta_time};
use cursor_lock::{debug_toggle_lock, insert_lock_state, lock_cursor_now, update_cursor_lock_state};
use cursor_lock::{insert_lock_state, update_cursor_lock_state, lock_cursor_now};
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 networking::{update_networking, update_networking_late, is_multiplayer, disconnect_on_exit, is_singleplayer};
@ -89,8 +87,6 @@ use connecting_screen::update_connecting_screen;
use fixed_timestamp::init_fixed_timestamp_storage;
use filesystem::AssetManager;
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
fn pre_startup() -> Workload {
@ -115,13 +111,11 @@ fn startup() -> Workload {
insert_control_flow_unique,
init_delta_time,
init_client_physics,
init_chat_manager,
).into_sequential_workload()
}
fn update() -> Workload {
(
debug_toggle_lock,
update_window_size,
update_cursor_lock_state,
process_inputs,
@ -150,7 +144,6 @@ fn update() -> Workload {
update_raycasts,
update_block_placement,
apply_queued_blocks,
render_chat,
).into_sequential_workload().run_if(is_ingame),
update_networking_late.run_if(is_multiplayer),
compute_cameras,

View file

@ -5,21 +5,9 @@ use kubi_shared::networking::{
state::ClientJoinState,
channels::Channel,
};
use rand::prelude::*;
use crate::{chat::ChatHistory, player::{spawn_local_player_multiplayer, spawn_remote_player_multiplayer}};
use crate::player::{spawn_local_player_multiplayer, spawn_remote_player_multiplayer};
use super::{UdpClient, NetworkEvent};
const USERNAME_BANK: &[&str] = &[
"XxX-FishFucker-69420",
"Sbeve34",
"ShadowBladeX",
"CyberNinja92",
"sputnik1",
"dumbpotato",
"FortNiteNinja",
"MinecraftMiner",
];
#[derive(Unique)]
pub struct ConnectionRejectionReason {
pub reason: String,
@ -35,8 +23,7 @@ pub fn set_client_join_state_to_connected(
pub fn say_hello(
mut client: UniqueViewMut<UdpClient>,
) {
let mut rng = thread_rng();
let username = (*USERNAME_BANK.choose(&mut rng).unwrap()).to_owned();
let username = "XxX-FishFucker-69420-XxX".into();
let password = None;
log::info!("Authenticating");
client.0.send(
@ -77,10 +64,7 @@ pub fn check_server_hello_response(
// direction: Quat,
// health: Health,
// }
let client_id = init.user.client_id;
let username = init.user.username.clone();
//Add components to main player
spawn_local_player_multiplayer(&mut storages, init.user);
@ -94,10 +78,6 @@ pub fn check_server_hello_response(
*join_state = ClientJoinState::Joined;
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(

View file

@ -4,13 +4,12 @@ use uflow::{SendMode, client::Event as ClientEvent};
use kubi_shared::{
transform::Transform,
networking::{
channels::Channel,
client::{ClientIdMap, Username},
messages::{ClientToServerMessage, ServerToClientMessage, ServerToClientMessageType},
channels::Channel,
client::ClientIdMap,
},
};
use crate::{
chat::ChatHistory,
events::player_actions::PlayerActionEvent,
player::spawn_remote_player_multiplayer,
};
@ -97,9 +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);
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);
}
}
@ -124,21 +120,12 @@ pub fn receive_player_disconnect_events(
for message in messages {
let ServerToClientMessage::PlayerDisconnected { id } = message else { unreachable!() };
log::info!("player disconnected: {}", id);
let mut id_map = storages.borrow::<UniqueViewMut<ClientIdMap>>().unwrap();
let Some(ent_id) = id_map.0.remove(&id) else {
log::warn!("Disconnected player entity not found in client-id map");
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(username);
if !storages.delete_entity(ent_id) {
log::warn!("Disconnected player entity not found in storage");
}

View file

@ -1,43 +0,0 @@
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());
}