mirror of
https://github.com/griffi-gh/kubi.git
synced 2025-02-16 14:38:20 -06:00
Compare commits
No commits in common. "772a8ea7db51a5a546d8649ec29b73da73fac5d7" and "043bb873c435f6462a0ef624e0110040db864355" have entirely different histories.
772a8ea7db
...
043bb873c4
493
Cargo.lock
generated
493
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
Loading…
Reference in a new issue