diff --git a/Cargo.lock b/Cargo.lock index 77cda2d..c2e59b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1321,6 +1321,9 @@ name = "bit-vec" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +dependencies = [ + "serde", +] [[package]] name = "bitflags" @@ -2504,6 +2507,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "huffman-compress" +version = "0.6.1" +source = "git+https://github.com/zzhgithub/rust-huffman-compress?branch=features-serde#6b09ee0290deab72a51b5927b7553fba8c0f37c7" +dependencies = [ + "bit-vec", + "num-traits", + "serde", +] + [[package]] name = "idna" version = "0.4.0" @@ -2571,10 +2584,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" dependencies = [ "bitflags 1.3.2", - "futures-core", "inotify-sys", "libc", - "tokio", ] [[package]] @@ -2735,6 +2746,7 @@ dependencies = [ "bevy_renet", "bevy_sprite3d", "bincode", + "bit-vec", "block-mesh", "clap 4.1.10", "codespan-reporting", @@ -2742,7 +2754,7 @@ dependencies = [ "egui_extras", "futures-lite", "fxhash", - "inotify", + "huffman-compress", "lock_api", "ndshape", "noise", @@ -4233,16 +4245,6 @@ dependencies = [ "bevy", ] -[[package]] -name = "socket2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "spade" version = "2.2.0" @@ -4467,20 +4469,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" -[[package]] -name = "tokio" -version = "1.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" -dependencies = [ - "backtrace", - "libc", - "mio", - "pin-project-lite", - "socket2", - "windows-sys 0.48.0", -] - [[package]] name = "toml_datetime" version = "0.6.3" diff --git a/Cargo.toml b/Cargo.toml index d14d888..ccea00b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ egui-notify = "0.8.0" rand = "0.8.5" seldom_state = "0.7.0" bevy_sprite3d = "2.5.0" +bit-vec = { version = "0.6", features = ["serde"] } # 解决依赖冲突 num-rational = "^0.4.1" fxhash = "^0.2.1" @@ -50,7 +51,13 @@ bevy_mod_billboard = "0.4.0" notify = "6.1.1" egui_extras = "0.22.0" bevy_easy_localize = "0.3.0" - +huffman-compress = { git = "https://github.com/zzhgithub/rust-huffman-compress", branch = "features-serde", features = [ + "serde", +] } +noise = { version = "0.8.2" } +# 解决冲突 +lock_api = "0.4.10" +codespan-reporting = "0.11.1" [profile.dev.package.bevy_rapier3d] opt-level = 3 @@ -61,13 +68,8 @@ codegen-units = 1 [target.'cfg(target_arch = "x86_64")'.dependencies] simdnoise = { version = "3.1.6" } -[target.'cfg(target_arch = "aarch64")'.dependencies] -noise = { version = "0.8.2" } -# 解决冲突 -lock_api = "0.4.10" -codespan-reporting = "0.11.1" -inotify = "0.9.6" - +# [target.'cfg(target_arch = "aarch64")'.dependencies] +# noise = { version = "0.8.2" } [features] default = ["server_ui"] diff --git a/README.md b/README.md index 111dbb1..2480042 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Just Join (来噻) +![f](pic/f.png) + +---- + this rep is rebuild from https://github.com/zzhgithub/just_join. The goal is to make the original project support the online multiplayer game > ~~Ka Le on windows i5 cpu. optimizing!~~ @@ -18,6 +22,14 @@ For Client cargo run --release --bin client ``` +# Src/lib.rs Const +```rust +// generate map every time when you restart game(use in test mode) +// Before game you should set to flase +pub const CLIENT_MAP_GEN: bool = true; +``` + + # Controller - W - forward - S - backward @@ -26,8 +38,8 @@ cargo run --release --bin client - Space - Jump - ESC - toggle grab cursor - T - toggle One/Thrid Person -- E Open composite rules list -- Q throw active toolbar object +- E - Open composite rules list +- Q - throw active toolbar object # Feature List - [x] Load unlimited maps @@ -58,6 +70,8 @@ cargo run --release --bin client - [ ] opmtimize UI(Show current toolbar and knapsack) - [ ] support search - [ ] add more formula +- [x] more biomes support + - [ ] decorate_terrain - [ ] task system - [ ] manor system - [ ] wind zone diff --git a/README_CN.md b/README_CN.md index 7087a70..eceaeac 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,5 +1,9 @@ # Just Join (来噻) +![f](pic/f.png) + +---- + 此仓库是 https://github.com/zzhgithub/just_join 的网络化重构施工中。敬请期待。 @@ -30,6 +34,12 @@ cargo run --release --bin client - E 打开合成表 - Q 丢弃toolbar上激活中的物品 +# LIB 中的配置常量 +```rust +// 是否每次都重新生成地形 +// 游戏前 请设置成false +pub const CLIENT_MAP_GEN: bool = true; +``` # 功能列表 @@ -61,6 +71,8 @@ cargo run --release --bin client - [ ] 增强交互(显示当前背包) - [ ] 支撑合成公式搜索 - [ ] 添加更多游戏内的公式 +- [x] 更多群落支撑 + - [ ] 装饰群落 - [ ] 任务系统 - [ ] 领地系统 - [ ] 风场 diff --git "a/assets/textures/\345\271\262\350\215\211\344\276\247\351\235\242.png" "b/assets/textures/\345\271\262\350\215\211\344\276\247\351\235\242.png" new file mode 100644 index 0000000..05a5101 Binary files /dev/null and "b/assets/textures/\345\271\262\350\215\211\344\276\247\351\235\242.png" differ diff --git "a/assets/textures/\345\271\262\350\215\211\345\234\260.png" "b/assets/textures/\345\271\262\350\215\211\345\234\260.png" new file mode 100644 index 0000000..199e086 Binary files /dev/null and "b/assets/textures/\345\271\262\350\215\211\345\234\260.png" differ diff --git "a/assets/textures/\350\213\215\347\277\240\344\276\247\351\235\242.png" "b/assets/textures/\350\213\215\347\277\240\344\276\247\351\235\242.png" new file mode 100644 index 0000000..b359f71 Binary files /dev/null and "b/assets/textures/\350\213\215\347\277\240\344\276\247\351\235\242.png" differ diff --git "a/assets/textures/\350\213\215\347\277\240\345\234\260.png" "b/assets/textures/\350\213\215\347\277\240\345\234\260.png" new file mode 100644 index 0000000..e9a646c Binary files /dev/null and "b/assets/textures/\350\213\215\347\277\240\345\234\260.png" differ diff --git a/pic/f.png b/pic/f.png new file mode 100644 index 0000000..0f96b04 Binary files /dev/null and b/pic/f.png differ diff --git a/src/bin/server.rs b/src/bin/server.rs index 187be1b..7fb55ef 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -16,7 +16,8 @@ use just_join::{ common::ServerClipSpheresPlugin, connection_config, server::{ - async_chunk::ChunkDataPlugin, chunk::ServerChunkPlugin, deal_message_system, + async_chunk::ChunkDataPlugin, chunk::ServerChunkPlugin, + cross_through_check::CossTroughCheckPlugin, deal_message_system, object_filing::ObjectFilingPlugin, player::ServerLobby, server_connect_system, staff_rule_sync::ServerStaffRulePlugin, sync_body_and_head, terrain_physics::TerrainPhysicsPlugin, @@ -117,6 +118,7 @@ fn main() { ServerSkyPlugins, ObjectFilingPlugin, ServerStaffRulePlugin, + CossTroughCheckPlugin, )); let (server, transport) = new_renet_server(); diff --git a/src/client/filled_object/mod.rs b/src/client/filled_object/mod.rs index 9a407c2..624ac19 100644 --- a/src/client/filled_object/mod.rs +++ b/src/client/filled_object/mod.rs @@ -168,3 +168,13 @@ fn sync_filled_objects( } } } + +pub fn setdown_filled_object( + mut commands: Commands, + mut filled_object_pool: ResMut, +) { + for (_, entity) in filled_object_pool.entities_map.clone() { + commands.entity(entity).despawn(); + } + filled_object_pool.entities_map = HashMap::new(); +} diff --git a/src/client/mesh_display.rs b/src/client/mesh_display.rs index ab9bf40..e20b2ff 100644 --- a/src/client/mesh_display.rs +++ b/src/client/mesh_display.rs @@ -20,13 +20,14 @@ use ndshape::{ConstShape, ConstShape3u32}; use crate::{ common::ClipSpheres, server::message_def::{chunk_result::ChunkResult, ServerChannel}, - tools::get_empty_chunk, + tools::get_all_v_chunk, voxel_world::{ chunk::{ find_chunk_keys_array_by_shpere_y_0, generate_offset_resoure, generate_offset_resoure_min_1, ChunkKey, NeighbourOffest, }, chunk_map::ChunkMap, + compress::uncompress, voxel::Voxel, }, CHUNK_SIZE, CHUNK_SIZE_U32, MATERIAL_RON, VIEW_RADIUS, @@ -165,11 +166,11 @@ pub fn async_chunk_result( let chunk_result: ChunkResult = bincode::deserialize(&message).unwrap(); match chunk_result { ChunkResult::ChunkData { key, data } => { - let task = pool.spawn(async move { (key, data) }); + let task = pool.spawn(async move { (key, uncompress(&data.0, data.1)) }); chunk_sync_task.tasks.push(task); } - ChunkResult::ChunkEmpty(key) => { - let task = pool.spawn(async move { (key, get_empty_chunk()) }); + ChunkResult::ChunkSame((key, voxel)) => { + let task = pool.spawn(async move { (key, get_all_v_chunk(voxel)) }); chunk_sync_task.tasks.push(task); } ChunkResult::ChunkUpdateOne { @@ -260,15 +261,16 @@ pub fn update_mesh( let volexs: Vec = chunk_map.get_with_neighbor_full_y(chunk_key_y0); match gen_mesh(volexs.to_owned(), material_config.clone()) { Some(render_mesh) => { - let mesh_handle = mesh_manager.mesh_storge.get(&chunk_key_y0).unwrap(); - if let Some(mesh) = mesh_assets.get_mut(mesh_handle) { - // 更新AABB - if let Some(entity) = mesh_manager.entities.get(&chunk_key_y0) { - if let Some(aabb) = render_mesh.compute_aabb() { - commands.entity(*entity).insert(aabb); + if let Some(mesh_handle) = mesh_manager.mesh_storge.get(&chunk_key_y0) { + if let Some(mesh) = mesh_assets.get_mut(mesh_handle) { + // 更新AABB + if let Some(entity) = mesh_manager.entities.get(&chunk_key_y0) { + if let Some(aabb) = render_mesh.compute_aabb() { + commands.entity(*entity).insert(aabb); + } } + *mesh = render_mesh; } - *mesh = render_mesh; } // 没有生成mesh就不管反正后面要生成 } @@ -281,14 +283,15 @@ pub fn update_mesh( }; match gen_mesh_water(pick_water(volexs), material_config) { Some(water_mesh) => { - let mesh_handle = mesh_manager.water_mesh_storge.get(&chunk_key_y0).unwrap(); - if let Some(mesh) = mesh_assets.get_mut(mesh_handle) { - if let Some(entity) = mesh_manager.water_entities.get(&chunk_key_y0) { - if let Some(aabb) = water_mesh.compute_aabb() { - commands.entity(*entity).insert(aabb); + if let Some(mesh_handle) = mesh_manager.water_mesh_storge.get(&chunk_key_y0) { + if let Some(mesh) = mesh_assets.get_mut(mesh_handle) { + if let Some(entity) = mesh_manager.water_entities.get(&chunk_key_y0) { + if let Some(aabb) = water_mesh.compute_aabb() { + commands.entity(*entity).insert(aabb); + } } + *mesh = water_mesh; } - *mesh = water_mesh; } } None => { @@ -468,10 +471,11 @@ fn cycle_check_mesh( let now: Instant = Instant::now(); let duration: Duration = now - *instant; // 每2s检查一下五秒内没有加载好的数据 - if !state && duration.as_millis() > 5 * 1000 && need_keys.contains(key) { + if !state && duration.as_millis() > 10 * 1000 && need_keys.contains(key) { println!("超时重新请求chunkkey{:?}", key); + // TODO: 这可以检查具体少什么数据? let message = bincode::serialize(&ChunkQuery::GetFullY(*key)).unwrap(); - // todo 对边缘数据不处理! + // 对边缘数据不处理! client.send_message(ClientChannel::ChunkQuery, message); } if duration.as_millis() > 5 * 1000 @@ -484,3 +488,22 @@ fn cycle_check_mesh( } } } + +pub fn mesh_chunk_map_setdown( + mut commands: Commands, + mut mesh_manager: ResMut, + mut chunk_sync_task: ResMut, + mut chunk_map: ResMut, + mut chunk_update_task: ResMut, +) { + chunk_update_task.tasks.drain(..); + chunk_sync_task.tasks.drain(..); + chunk_map.map_data.clear(); + for (_, entity) in mesh_manager.entities.clone() { + commands.entity(entity).despawn(); + } + for (_, entity) in mesh_manager.water_entities.clone() { + commands.entity(entity).despawn(); + } + *mesh_manager.as_mut() = MeshManager::default(); +} diff --git a/src/client/player/controller.rs b/src/client/player/controller.rs index b70a6b8..49193b6 100644 --- a/src/client/player/controller.rs +++ b/src/client/player/controller.rs @@ -9,7 +9,9 @@ use bevy::{ use bevy_egui::EguiSet; use bevy_renet::renet::RenetClient; -use crate::client::{ state_manager::GameState, message_def::{player_input::PlayerInput, ClientChannel}, +use crate::client::{ + message_def::{player_input::PlayerInput, ClientChannel}, + state_manager::GameState, }; use super::{ @@ -138,7 +140,7 @@ fn initial_grab_cursor(mut primary_window: Query<&mut Window, With>) { +pub fn back_grab_cursor(mut primary_window: Query<&mut Window, With>) { if let Ok(mut window) = primary_window.get_single_mut() { { window.cursor.grab_mode = CursorGrabMode::None; diff --git a/src/client/player/mod.rs b/src/client/player/mod.rs index 74f6493..7057538 100644 --- a/src/client/player/mod.rs +++ b/src/client/player/mod.rs @@ -24,7 +24,7 @@ pub mod mouse_control; pub mod player_input; pub mod throw_system; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct PlayerInfo { // 客户端 实体 pub client_entity: Entity, diff --git a/src/client/state_manager/game.rs b/src/client/state_manager/game.rs index cafe55c..14a7737 100644 --- a/src/client/state_manager/game.rs +++ b/src/client/state_manager/game.rs @@ -4,9 +4,9 @@ use bevy::{ app::AppExit, input::mouse::MouseWheel, prelude::{ - in_state, AmbientLight, Commands, Entity, EventReader, EventWriter, Input, - IntoSystemConfigs, KeyCode, Local, NextState, OnEnter, Plugin, Query, Res, ResMut, State, - States, Update, Vec2, With, + in_state, AmbientLight, Commands, DespawnRecursiveExt, Entity, EventReader, EventWriter, + Input, IntoSystemConfigs, KeyCode, Local, NextState, OnEnter, OnExit, Plugin, Query, Res, + ResMut, State, States, Update, Vec2, With, }, window::{CursorGrabMode, PrimaryWindow, Window, WindowCloseRequested}, }; @@ -21,8 +21,8 @@ use crate::{ client::{ client_sync_players, client_sync_players_state, console_commands::ConsoleCommandPlugins, - filled_object::ClientFilledObjectnPlugin, - mesh_display::ClientMeshPlugin, + filled_object::{setdown_filled_object, ClientFilledObjectnPlugin}, + mesh_display::{mesh_chunk_map_setdown, ClientMeshPlugin}, player::{ controller::{CharacterController, CharacterControllerPlugin, ControllerFlag}, mouse_control::MouseControlPlugin, @@ -115,13 +115,26 @@ impl Plugin for GamePlugin { .run_if(in_state(GameState::Game)) .run_if(bevy_renet::transport::client_just_diconnected()), ); + app.add_systems( + OnExit(GameState::Game), + (setdown, mesh_chunk_map_setdown, setdown_filled_object), + ); + } +} + +fn setdown(mut commands: Commands, mut client_lobby: ResMut) { + for (_, info) in client_lobby.players.clone() { + commands.entity(info.client_entity).despawn_recursive(); } + // 清空数据 + *client_lobby.as_mut() = ClientLobby::default(); } fn setup( mut commands: Commands, connection_addr: Res, mut play_state: ResMut>, + mut flags: ResMut, ) { let (client, transport) = new_renet_client(connection_addr.clone()); commands.insert_resource(client); @@ -132,6 +145,8 @@ fn setup( }); commands.insert_resource(ClientLobby::default()); play_state.set(PlayState::Main); + // 重新进入游戏后可以控制 + flags.flag = true; } // 切换合成公式 diff --git a/src/client/state_manager/menu.rs b/src/client/state_manager/menu.rs index 92efc9b..26566c7 100644 --- a/src/client/state_manager/menu.rs +++ b/src/client/state_manager/menu.rs @@ -1,10 +1,10 @@ use bevy::{ app::AppExit, prelude::{ - in_state, Entity, EventWriter, IntoSystemConfigs, NextState, OnEnter, Plugin, Query, Res, - ResMut, Resource, States, Update, With, + in_state, not, Entity, EventReader, EventWriter, IntoSystemConfigs, NextState, OnEnter, + Plugin, Query, Res, ResMut, Resource, States, Update, With, }, - window::{PrimaryWindow, Window}, + window::{PrimaryWindow, Window, WindowCloseRequested}, }; use bevy_easy_localize::Localize; use bevy_egui::{egui, EguiContext, EguiContexts, EguiUserTextures}; @@ -13,11 +13,14 @@ use std::time::Duration; use super::{ENGLISH, CHINESE}; use super::{notification::Notification, ConnectionAddr, GameState}; use crate::{ - client::ui::{ - test::toggle_ui, - tool_bar::{tool_bar, ToolBar}, - tool_box::tool_box, - UiPicResourceManager, + client::{ + player::controller::back_grab_cursor, + ui::{ + test::toggle_ui, + tool_bar::{tool_bar, ToolBar}, + tool_box::tool_box, + UiPicResourceManager, + }, }, staff::StaffInfoStroge, tools::string::{is_port, is_valid_server_address}, @@ -40,13 +43,18 @@ impl Plugin for MenuPlugin { app.add_state::(); app.insert_resource(TestResource::default()); app.insert_resource(ToolBar::default()); - app.add_systems(OnEnter(GameState::Menu), setup); + app.add_systems(OnEnter(GameState::Menu), (setup, back_grab_cursor)); app.add_systems(Update, menu_main.run_if(in_state(MenuState::Main))); app.add_systems(Update, test.run_if(in_state(MenuState::Test))); app.add_systems( Update, menu_multiplayer.run_if(in_state(MenuState::Multiplayer)), ); + + app.add_systems( + Update, + disconnect_on_close_without_connected.run_if(not(in_state(GameState::Game))), + ); } } @@ -199,3 +207,12 @@ fn test( } } } + +fn disconnect_on_close_without_connected( + mut exit: EventWriter, + mut closed: EventReader, +) { + for _ in closed.iter() { + exit.send(AppExit); + } +} diff --git a/src/lib.rs b/src/lib.rs index 77f1083..73f307f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,11 +27,13 @@ pub const CHUNK_SIZE: i32 = 16; pub const CHUNK_SIZE_U32: u32 = CHUNK_SIZE as u32; pub const CHUNK_SIZE_ADD_2_U32: u32 = CHUNK_SIZE_U32 + 2; // 贴图个数 -pub const MAX_TEXTURE_COUNT: usize = 9; +pub const MAX_TEXTURE_COUNT: usize = 13; // 物体选择半径 pub const TOUCH_RADIUS: f32 = 5.; pub const CLIENT_DEBUG: bool = false; pub const CLIENT_FPS: bool = false; +// 是否每次都重新生成地形 +pub const CLIENT_MAP_GEN: bool = true; // 最大物品堆放 pub const MAX_STAFF_FIXED: usize = 999; diff --git a/src/server/async_chunk.rs b/src/server/async_chunk.rs index 7e1aa65..7c4575c 100644 --- a/src/server/async_chunk.rs +++ b/src/server/async_chunk.rs @@ -9,10 +9,10 @@ use crate::{ client::message_def::{chunk_query::ChunkQuery, ClientChannel}, server::{message_def::ServerChannel, object_filing::put_object::put_object}, staff::StaffInfoStroge, - tools::all_empty, voxel_world::{ chunk::ChunkKey, chunk_map::ChunkMap, + compress::compress, map_database::{DbSaveTasks, MapDataBase}, player_state::PlayerOntimeState, voxel::{BasicStone, Voxel, VoxelMaterial}, @@ -65,12 +65,14 @@ pub fn deal_chunk_query_system( } else { voxels = db.find_by_chunk_key(new_key, db_save_task.as_mut()); } - let message = if all_empty(&voxels) { - bincode::serialize(&ChunkResult::ChunkEmpty(new_key)).unwrap() + let (buffer, tree) = compress(voxels.clone()); + let message = if buffer.len() == 0 { + bincode::serialize(&ChunkResult::ChunkSame((new_key, voxels[0]))) + .unwrap() } else { bincode::serialize(&ChunkResult::ChunkData { key: new_key, - data: voxels.clone(), + data: (buffer, tree), }) .unwrap() }; diff --git a/src/server/cross_through_check.rs b/src/server/cross_through_check.rs new file mode 100644 index 0000000..9478f9a --- /dev/null +++ b/src/server/cross_through_check.rs @@ -0,0 +1,79 @@ +// 穿透检查 + +use bevy::prelude::{ + Commands, Component, Entity, Plugin, PreUpdate, Query, Res, ResMut, Transform, Update, Vec3, + Without, +}; +use bevy_rapier3d::{ + prelude::{RapierContext, RapierRigidBodyHandle}, + rapier::prelude::RigidBodyType, +}; + +use crate::{ + tools::{chunk_key_any_xyz_to_vec3, pos_to_center, vec3_to_chunk_key_any_xyz}, + voxel_world::{chunk_map::ChunkMap, voxel::Voxel}, +}; + +#[derive(Debug, Clone, Copy, Component)] +pub struct CossTroughCheck; + +#[derive(Debug, Clone, Copy, Component)] +pub struct CossTroughFixed(Vec3); + +pub struct CossTroughCheckPlugin; + +impl Plugin for CossTroughCheckPlugin { + fn build(&self, app: &mut bevy::prelude::App) { + app.add_systems(PreUpdate, cross_through_check); + app.add_systems(Update, cross_through_fixed); + } +} + +fn cross_through_check( + mut commands: Commands, + mut context: ResMut, + query: Query< + (Entity, &RapierRigidBodyHandle, &Transform, &CossTroughCheck), + Without, + >, + chunk_map: Res, +) { + for (entity, body_handle, trf, _) in query.iter() { + let (chunk_key, xyz) = vec3_to_chunk_key_any_xyz(pos_to_center(trf.translation)); + if let Some(test_voxel) = chunk_map.get_block(chunk_key, xyz) { + if test_voxel.id != Voxel::EMPTY.id { + if let Some((new_chunk_key, new_xyz)) = + chunk_map.find_closest_block_y(chunk_key, xyz, Voxel::EMPTY.id) + { + if let Some(body) = context.bodies.get_mut(body_handle.0) { + body.set_body_type(RigidBodyType::KinematicPositionBased, true); + let mut pos = chunk_key_any_xyz_to_vec3(new_chunk_key, new_xyz); + pos.x = trf.translation.x; + pos.z = trf.translation.z; + commands.entity(entity).insert(CossTroughFixed(pos)); + } + } + } + } + } +} + +fn cross_through_fixed( + mut commands: Commands, + mut context: ResMut, + mut query: Query<( + Entity, + &RapierRigidBodyHandle, + &mut Transform, + &CossTroughFixed, + )>, +) { + for (entity, body_handle, mut trf, fixed) in query.iter_mut() { + if let Some(body) = context.bodies.get_mut(body_handle.0) { + // println!("修复了物体 to {}", fixed.0); + trf.translation = fixed.0; + body.set_body_type(RigidBodyType::Dynamic, true); + commands.entity(entity).remove::(); + } + } +} diff --git a/src/server/message_def/chunk_result.rs b/src/server/message_def/chunk_result.rs index e95974b..85d7844 100644 --- a/src/server/message_def/chunk_result.rs +++ b/src/server/message_def/chunk_result.rs @@ -1,4 +1,6 @@ use bevy::prelude::Component; +use bit_vec::BitVec; +use huffman_compress::Tree; use serde::{Deserialize, Serialize}; use crate::voxel_world::{chunk::ChunkKey, voxel::Voxel}; @@ -7,9 +9,9 @@ use crate::voxel_world::{chunk::ChunkKey, voxel::Voxel}; pub enum ChunkResult { ChunkData { key: ChunkKey, - data: Vec, + data: (BitVec, Tree), }, - ChunkEmpty(ChunkKey), + ChunkSame((ChunkKey,Voxel)), ChunkUpdateOne { chunk_key: ChunkKey, pos: [u32; 3], diff --git a/src/server/mod.rs b/src/server/mod.rs index 607928b..745d04c 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -27,6 +27,7 @@ use self::{ pub mod async_chunk; pub mod chunk; +pub mod cross_through_check; pub mod message_def; pub mod object_filing; pub mod player; diff --git a/src/server/object_filing/follow.rs b/src/server/object_filing/follow.rs index 03704e0..5991e40 100644 --- a/src/server/object_filing/follow.rs +++ b/src/server/object_filing/follow.rs @@ -21,6 +21,7 @@ use seldom_state::{ use crate::{ server::{ + cross_through_check::CossTroughCheck, message_def::{tool_bar_message::ToolBarMessage, ServerChannel}, player::Player, }, @@ -135,21 +136,25 @@ fn load_up_state_machine( query: Query<(Entity, &FilledObject), (Without, Without)>, ) { for (entity, _) in query.iter() { - commands.entity(entity).insert(Idle).insert( - StateMachine::default() - .trans_builder(Near { range: NEAR_RANGE }, |_: &Idle, entity: Entity| { - Some(Follow { - target: entity, - speed: PICK_SPEED, + commands + .entity(entity) + .insert(CossTroughCheck) + .insert(Idle) + .insert( + StateMachine::default() + .trans_builder(Near { range: NEAR_RANGE }, |_: &Idle, entity: Entity| { + Some(Follow { + target: entity, + speed: PICK_SPEED, + }) }) - }) - .trans::(Near { range: NEAR_RANGE }.not(), Idle) - .trans_builder(CloseTo { range: CLOSE_RANGE }, |follow: &Follow, _| { - Some(Picked { - target: follow.target, - }) - }), - ); + .trans::(Near { range: NEAR_RANGE }.not(), Idle) + .trans_builder(CloseTo { range: CLOSE_RANGE }, |follow: &Follow, _| { + Some(Picked { + target: follow.target, + }) + }), + ); } } diff --git a/src/tools/mod.rs b/src/tools/mod.rs index a1a3866..8700571 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -35,6 +35,19 @@ pub fn get_empty_chunk() -> Vec { voxels } +/** + * 获取完全空的区块数据 + * Get all empty chunk data + */ +pub fn get_all_v_chunk(voxel: Voxel) -> Vec { + let mut voxels = Vec::new(); + type SampleShape = ConstShape3u32; + for _ in 0..SampleShape::SIZE { + voxels.push(voxel.clone()); + } + voxels +} + /** * 获取cube中心点 属于的 chunkKey和x y z坐标 */ @@ -47,3 +60,24 @@ pub fn vec3_to_chunk_key_any_xyz(pos: Vec3) -> (ChunkKey, [u32; 3]) { (chunk_key, [x, y, z]) } + +// chunkKey和x y z坐标 还原到 Cube的中心位置 +pub fn chunk_key_any_xyz_to_vec3(chunk_key: ChunkKey, xyz: [u32; 3]) -> Vec3 { + let x = + (chunk_key.0.x as f32) * (CHUNK_SIZE as f32) - CHUNK_SIZE as f32 / 2. + xyz[0] as f32 + 0.5; + let y = + (chunk_key.0.y as f32) * (CHUNK_SIZE as f32) - CHUNK_SIZE as f32 / 2. + xyz[1] as f32 + 0.5; + let z = + (chunk_key.0.z as f32) * (CHUNK_SIZE as f32) - CHUNK_SIZE as f32 / 2. + xyz[2] as f32 + 0.5; + Vec3::new(x, y, z) +} + +// 计算点所在的 方块终点是什么 +pub fn pos_to_center(pos: Vec3) -> Vec3 { + let res = Vec3 { + x: pos.x.floor(), + y: pos.y.floor(), + z: pos.z.floor(), + }; + res + Vec3::splat(0.5) +} diff --git a/src/voxel_world/biomes/basic_land.rs b/src/voxel_world/biomes/basic_land.rs new file mode 100644 index 0000000..dc7c745 --- /dev/null +++ b/src/voxel_world/biomes/basic_land.rs @@ -0,0 +1,49 @@ +// 基础大陆 + +use ndshape::ConstShape; + +use crate::voxel_world::voxel::{Grass, Soli, Sown, Stone, Voxel, VoxelMaterial}; + +use super::{BiomesGenerator, SampleShape, MOUNTAIN_LEVEL, SEE_LEVEL, SNOW_LEVEL}; + +// 基础大陆 +// 1. 雪顶 +// 2. 石块 +// 3. 草 +// 4. 泥土 5 +pub struct BasicLandBiomes; + +impl BiomesGenerator for BasicLandBiomes { + fn gen_land_with_info( + &self, + _chunk_key: crate::voxel_world::chunk::ChunkKey, + voxels: &mut Vec, + chunk_index: u32, + _plane_index: u32, + height: f32, + xyz: [u32; 3], + ) { + let [x, y, z] = xyz; + if height >= SNOW_LEVEL { + // 雪线之上 + voxels[chunk_index as usize] = Sown::into_voxel(); + if y > 0 { + // 雪之下雪乃 + let under_sown = SampleShape::linearize([x, y - 1, z]); + voxels[under_sown as usize] = Sown::into_voxel(); + } + } else if height >= MOUNTAIN_LEVEL { + voxels[chunk_index as usize] = Stone::into_voxel(); + // 一层实体 + } else if height >= SEE_LEVEL { + // 一层 草 5层的土 + voxels[chunk_index as usize] = Grass::into_voxel(); + for y_offset in 1..=5 { + if y > y_offset { + let under_grass = SampleShape::linearize([x, y - y_offset, z]); + voxels[under_grass as usize] = Soli::into_voxel(); + } + } + } + } +} diff --git a/src/voxel_world/biomes/bule_land.rs b/src/voxel_world/biomes/bule_land.rs new file mode 100644 index 0000000..043c861 --- /dev/null +++ b/src/voxel_world/biomes/bule_land.rs @@ -0,0 +1,44 @@ +// 苍翠大陆 + +use ndshape::ConstShape; + +use crate::voxel_world::voxel::{BuleGrass, Soli, Sown, Stone, VoxelMaterial}; + +use super::{BiomesGenerator, SampleShape, MOUNTAIN_LEVEL, SEE_LEVEL, SNOW_LEVEL}; + +pub struct BuleLandBoimes; + +impl BiomesGenerator for BuleLandBoimes { + fn gen_land_with_info( + &self, + _chunk_key: crate::voxel_world::chunk::ChunkKey, + voxels: &mut Vec, + chunk_index: u32, + _plane_index: u32, + height: f32, + xyz: [u32; 3], + ) { + let [x, y, z] = xyz; + if height >= SNOW_LEVEL { + // 雪线之上 + voxels[chunk_index as usize] = Sown::into_voxel(); + if y > 0 { + // 雪之下雪乃 + let under_sown = SampleShape::linearize([x, y - 1, z]); + voxels[under_sown as usize] = Sown::into_voxel(); + } + } else if height >= MOUNTAIN_LEVEL + 6.0 { + voxels[chunk_index as usize] = Stone::into_voxel(); + // 一层实体 + } else if height >= SEE_LEVEL { + // 一层 草 5层的土 + voxels[chunk_index as usize] = BuleGrass::into_voxel(); + for y_offset in 1..=5 { + if y > y_offset { + let under_grass = SampleShape::linearize([x, y - y_offset, z]); + voxels[under_grass as usize] = Soli::into_voxel(); + } + } + } + } +} diff --git a/src/voxel_world/biomes/dry_land.rs b/src/voxel_world/biomes/dry_land.rs new file mode 100644 index 0000000..59dcc06 --- /dev/null +++ b/src/voxel_world/biomes/dry_land.rs @@ -0,0 +1,44 @@ +// 干燥大陆 + +use ndshape::ConstShape; + +use crate::voxel_world::voxel::{DryGrass, Soli, Sown, Stone, VoxelMaterial}; + +use super::{BiomesGenerator, SampleShape, MOUNTAIN_LEVEL, SEE_LEVEL, SNOW_LEVEL}; + +pub struct DryLandBiomes; + +impl BiomesGenerator for DryLandBiomes { + fn gen_land_with_info( + &self, + _chunk_key: crate::voxel_world::chunk::ChunkKey, + voxels: &mut Vec, + chunk_index: u32, + _plane_index: u32, + height: f32, + xyz: [u32; 3], + ) { + let [x, y, z] = xyz; + if height >= SNOW_LEVEL { + // 雪线之上 + voxels[chunk_index as usize] = Sown::into_voxel(); + if y > 0 { + // 雪之下雪乃 + let under_sown = SampleShape::linearize([x, y - 1, z]); + voxels[under_sown as usize] = Sown::into_voxel(); + } + } else if height >= MOUNTAIN_LEVEL + 2.0 { + voxels[chunk_index as usize] = Stone::into_voxel(); + // 一层实体 + } else if height >= SEE_LEVEL { + // 一层 草 5层的土 + voxels[chunk_index as usize] = DryGrass::into_voxel(); + for y_offset in 1..=5 { + if y > y_offset { + let under_grass = SampleShape::linearize([x, y - y_offset, z]); + voxels[under_grass as usize] = Soli::into_voxel(); + } + } + } + } +} diff --git a/src/voxel_world/biomes/mod.rs b/src/voxel_world/biomes/mod.rs new file mode 100644 index 0000000..c87a256 --- /dev/null +++ b/src/voxel_world/biomes/mod.rs @@ -0,0 +1,130 @@ +use ndshape::{ConstShape, ConstShape2u32, ConstShape3u32}; +use noise::{ + core::worley::{distance_functions::euclidean, ReturnType}, + utils::NoiseMapBuilder, + Worley, +}; + +use crate::{CHUNK_SIZE, CHUNK_SIZE_U32}; + +use self::{ + basic_land::BasicLandBiomes, bule_land::BuleLandBoimes, dry_land::DryLandBiomes, + sand_land::SandLandBiomes, snow_land::SnowLandBiomes, +}; + +use super::{chunk::ChunkKey, voxel::Voxel}; + +pub mod basic_land; +pub mod bule_land; +pub mod dry_land; +pub mod sand_land; +pub mod snow_land; + +pub type SampleShape = ConstShape3u32; +pub type PanleShap = ConstShape2u32; + +// 处理 生物群落 +pub fn biomes_generate( + chunk_key: ChunkKey, + seed: i32, + suface_index: Vec, + voxels: &mut Vec, +) { + if suface_index.len() == 0 { + return; + } + // 生成噪声 + let noise = biomes_noise(chunk_key, seed); + + for index in suface_index { + // 由噪声生产的特征值 + let [x, _, z] = SampleShape::delinearize(index); + let index_2d = PanleShap::linearize([x, z]); + let atrr = noise[index_2d as usize]; + let generator = get_generator_by_atrr(atrr); + generator.gen_land(chunk_key.clone(), voxels, index, index_2d); + // fixme: 这里要记录对于其他方块的影响 + } +} + +// 获取不同的生成器 +fn get_generator_by_atrr(data: f32) -> Box { + if data < 0.1 { + return BasicLandBiomes.into_boxed_generator(); + } else if data < 0.4 { + return DryLandBiomes.into_boxed_generator(); + } else if data < 0.6 { + return SnowLandBiomes.into_boxed_generator(); + } else if data < 0.8 { + return SandLandBiomes.into_boxed_generator(); + } else { + return BuleLandBoimes.into_boxed_generator(); + } +} + +pub fn biomes_noise(chunk_key: ChunkKey, seed: i32) -> Vec { + let noise = Worley::new(seed as u32) + .set_distance_function(euclidean) + .set_return_type(ReturnType::Value) + .set_frequency(0.008); + + let x_offset = (chunk_key.0.x * CHUNK_SIZE) as f64; + let z_offset = (chunk_key.0.z * CHUNK_SIZE) as f64; + + noise::utils::PlaneMapBuilder::<_, 2>::new(noise) + .set_size(CHUNK_SIZE as usize, CHUNK_SIZE as usize) + .set_x_bounds(x_offset, x_offset + CHUNK_SIZE as f64) + .set_y_bounds(z_offset, z_offset + CHUNK_SIZE as f64) + .build() + .into_iter() + .map(|x| x as f32) + .collect() +} + +pub trait BiomesGenerator: 'static + Sync + Send { + fn gen_land_with_info( + &self, + chunk_key: ChunkKey, + voxels: &mut Vec, + chunk_index: u32, + plane_index: u32, + height: f32, + xyz: [u32; 3], + ); + + fn gen_land( + &self, + chunk_key: ChunkKey, + voxels: &mut Vec, + chunk_index: u32, + plane_index: u32, + ) { + let base_y: f32 = (chunk_key.0.y * CHUNK_SIZE) as f32; + let [x, y, z] = SampleShape::delinearize(chunk_index); + let height = base_y + y as f32; + self.gen_land_with_info( + chunk_key, + voxels, + chunk_index, + plane_index, + height, + [x, y, z], + ); + } +} + +pub trait IntoBoxedTerrainGenerator: BiomesGenerator + Sized { + fn into_boxed_generator(self) -> Box; +} + +impl IntoBoxedTerrainGenerator for T { + fn into_boxed_generator(self) -> Box { + Box::new(self) + } +} +// 海平面 +pub const SEE_LEVEL: f32 = -60. + 76.; +// 山峰线 +pub const MOUNTAIN_LEVEL: f32 = -60. + 100.; +// 雪线 +pub const SNOW_LEVEL: f32 = -60. + 100.; diff --git a/src/voxel_world/biomes/sand_land.rs b/src/voxel_world/biomes/sand_land.rs new file mode 100644 index 0000000..8deb7ee --- /dev/null +++ b/src/voxel_world/biomes/sand_land.rs @@ -0,0 +1,26 @@ +use ndshape::ConstShape; + +use crate::voxel_world::voxel::{Sand, VoxelMaterial}; + +// 沙漠大陆 +use super::{BiomesGenerator, SampleShape}; + +pub struct SandLandBiomes; + +impl BiomesGenerator for SandLandBiomes { + fn gen_land_with_info( + &self, + _chunk_key: crate::voxel_world::chunk::ChunkKey, + voxels: &mut Vec, + _chunk_index: u32, + _plane_index: u32, + _height: f32, + xyz: [u32; 3], + ) { + let [x, y, z] = xyz; + for y_offset in 0..=y { + let index = SampleShape::linearize([x, y - y_offset, z]); + voxels[index as usize] = Sand::into_voxel(); + } + } +} diff --git a/src/voxel_world/biomes/snow_land.rs b/src/voxel_world/biomes/snow_land.rs new file mode 100644 index 0000000..8cc84f9 --- /dev/null +++ b/src/voxel_world/biomes/snow_land.rs @@ -0,0 +1,26 @@ +use ndshape::ConstShape; + +use crate::voxel_world::voxel::{Sown, VoxelMaterial}; + +use super::{BiomesGenerator, SampleShape}; + +// 雪原大陆 +pub struct SnowLandBiomes; + +impl BiomesGenerator for SnowLandBiomes { + fn gen_land_with_info( + &self, + _chunk_key: crate::voxel_world::chunk::ChunkKey, + voxels: &mut Vec, + _chunk_index: u32, + _plane_index: u32, + _height: f32, + xyz: [u32; 3], + ) { + let [x, y, z] = xyz; + for y_offset in 0..=y { + let index = SampleShape::linearize([x, y - y_offset, z]); + voxels[index as usize] = Sown::into_voxel(); + } + } +} diff --git a/src/voxel_world/chunk_map.rs b/src/voxel_world/chunk_map.rs index db8be55..592631a 100644 --- a/src/voxel_world/chunk_map.rs +++ b/src/voxel_world/chunk_map.rs @@ -20,13 +20,53 @@ impl ChunkMap { Self { map_data: data_map } } + // 获取某个位置的方块 + pub fn get_block(&self, chunk_key: ChunkKey, xyz: [u32; 3]) -> Option { + type DataShape = ConstShape3u32; + let index = DataShape::linearize(xyz) as usize; + if let Some(voxels) = self.get(chunk_key) { + return Some(voxels[index]); + } + None + } + + // 寻找y轴上最进的数据 + pub fn find_closest_block_y( + &self, + chunk_key: ChunkKey, + xyz: [u32; 3], + id: u8, + ) -> Option<(ChunkKey, [u32; 3])> { + type DataShape = ConstShape3u32; + for chunk_y in chunk_key.0.y..=128 / CHUNK_SIZE { + let start = if chunk_y == chunk_key.0.y { xyz[1] } else { 0 }; + let new_chunk_key = ChunkKey(IVec3 { + x: chunk_key.0.x, + y: chunk_y, + z: chunk_key.0.z, + }); + if let Some(chunk_data) = self.get(new_chunk_key) { + for y in start..CHUNK_SIZE_U32 { + let pos = [xyz[0], y, xyz[2]]; + let index = DataShape::linearize(pos); + let data = chunk_data[index as usize]; + if data.id == id { + return Some((new_chunk_key, pos)); + } + } + } + } + None + } + pub fn chunk_for_mesh_ready(&self, chunk_key: ChunkKey) -> bool { let px = &IVec3::new(1, 0, 0); let nx = &IVec3::new(-1, 0, 0); let pz = &IVec3::new(0, 0, 1); let nz = &IVec3::new(0, 0, -1); + let n_self = &IVec3::new(0, 0, 0); - let offsets = [px, nx, pz, nz]; + let offsets = [px, nx, pz, nz, n_self]; let last_inex = -128 / CHUNK_SIZE + 1; for y_offset in last_inex..=128 / CHUNK_SIZE { diff --git a/src/voxel_world/compress.rs b/src/voxel_world/compress.rs new file mode 100644 index 0000000..962daba --- /dev/null +++ b/src/voxel_world/compress.rs @@ -0,0 +1,78 @@ +use bevy::utils::HashMap; +use bit_vec::BitVec; +use huffman_compress::{CodeBuilder, Tree}; + +use super::voxel::Voxel; + +pub fn compress(data: Vec) -> (BitVec, Tree) { + let mut weights: HashMap = HashMap::new(); + for &voxel in &data { + let count = weights.entry(voxel).or_insert(0); + *count += 1; + } + + let (book, tree) = CodeBuilder::from_iter(weights).finish(); + let mut buffer = BitVec::new(); + + for voxel in &data { + match book.encode(&mut buffer, voxel) { + Ok(_) => {} + Err(err) => { + println!("{}", err); + } + } + } + (buffer, tree) +} + +pub fn uncompress(buffer: &BitVec, tree: Tree) -> Vec { + tree.decoder(buffer, buffer.len()).collect() +} + +#[test] +fn test() { + let data = vec![ + Voxel::EMPTY, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + ]; + let (buffer, tree) = compress(data.clone()); + + let new_data = uncompress(&buffer, tree); + print!("{}", data.len()); + print!("{}", new_data.len()); + assert_eq!(data, new_data); +} + +#[test] +fn test_serialization() { + let data = vec![ + Voxel::EMPTY, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + ]; + let (buffer, tree) = compress(data.clone()); + let tree_s = bincode::serialize(&tree).unwrap(); + let tree_ds: Tree = bincode::deserialize(&tree_s).unwrap(); + + let a = uncompress(&buffer.clone(), tree); + let b = uncompress(&buffer.clone(), tree_ds); + + assert_eq!(a, b); +} diff --git a/src/voxel_world/map_database.rs b/src/voxel_world/map_database.rs index 460bd74..879862f 100644 --- a/src/voxel_world/map_database.rs +++ b/src/voxel_world/map_database.rs @@ -7,7 +7,7 @@ use bevy::{ use ndshape::{ConstShape, ConstShape3u32}; use sled::Db; -use crate::{voxel_world::map_generator::gen_chunk_data_by_seed, CHUNK_SIZE_U32}; +use crate::{voxel_world::map_generator::gen_chunk_data_by_seed, CHUNK_SIZE_U32, CLIENT_MAP_GEN}; use super::{chunk::ChunkKey, voxel::Voxel}; @@ -36,7 +36,7 @@ impl MapDataBase { } let key = chunk_key.as_u8_array(); match self.db.get(key) { - Ok(rs) => match rs { + Ok(rs) => match if CLIENT_MAP_GEN { None } else { rs } { Some(data) => bincode::deserialize(&data).unwrap(), // 这里在没有获取到的情况下使用算法的值 None => { diff --git a/src/voxel_world/map_generator.rs b/src/voxel_world/map_generator.rs index 1fbb455..7e0e16b 100644 --- a/src/voxel_world/map_generator.rs +++ b/src/voxel_world/map_generator.rs @@ -5,7 +5,10 @@ use noise::utils::NoiseMapBuilder; use simdnoise::NoiseBuilder; use crate::{ - voxel_world::voxel::{BasicStone, Grass, Sand, Soli, Sown, Stone, VoxelMaterial, Water}, + voxel_world::{ + biomes::biomes_generate, + voxel::{BasicStone, Grass, Sand, Soli, Sown, Stone, VoxelMaterial, Water}, + }, CHUNK_SIZE, CHUNK_SIZE_U32, }; @@ -22,17 +25,23 @@ pub fn gen_chunk_data_by_seed(seed: i32, chunk_key: ChunkKey) -> Vec { let noise = noise2d(chunk_key, seed); let noise2 = noise2d_ridge(chunk_key, seed); + // 表面 索引 + let mut suface_index: Vec = Vec::new(); + for i in 0..SampleShape::SIZE { let [x, y, z] = SampleShape::delinearize(i); - // let p_x = base_x + x as f32; let p_y = base_y + y as f32; - // let p_z = base_z + z as f32; - let h = -60.; - // println!("({},{})", h, p_y); let index = PanleShap::linearize([x, z]); let top = h + fn_height(noise[index as usize]) + noise2[index as usize] * 5.0; if p_y <= top { + if p_y + 1.0 > top && p_y - 1.0 < top + // 必须大于海平面之上 + && p_y >= -60. + 76. + // 在山之下 + && p_y < -60. + 100. { + suface_index.push(i); + } if p_y >= -60. + 110. { voxels.push(Sown::into_voxel()); continue; @@ -71,6 +80,9 @@ pub fn gen_chunk_data_by_seed(seed: i32, chunk_key: ChunkKey) -> Vec { } } + // 处理不同群落 + biomes_generate(chunk_key, seed, suface_index, &mut voxels); + //生成 沙子 if water_flag { for i in 0..SampleShape::SIZE { diff --git a/src/voxel_world/mod.rs b/src/voxel_world/mod.rs index 199c807..f49e36a 100644 --- a/src/voxel_world/mod.rs +++ b/src/voxel_world/mod.rs @@ -4,3 +4,5 @@ pub mod map_database; pub mod map_generator; pub mod player_state; pub mod voxel; +pub mod compress; +pub mod biomes; \ No newline at end of file diff --git a/src/voxel_world/voxel.rs b/src/voxel_world/voxel.rs index 98e8c04..7195143 100644 --- a/src/voxel_world/voxel.rs +++ b/src/voxel_world/voxel.rs @@ -5,11 +5,23 @@ use serde::{Deserialize, Serialize}; /** * 体素类型 */ -#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, Reflect)] +#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, Reflect, PartialEq, Eq, Hash)] pub struct Voxel { pub id: u8, } +impl PartialOrd for Voxel { + fn partial_cmp(&self, other: &Self) -> Option { + self.id.partial_cmp(&other.id) + } +} + +impl Ord for Voxel { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id.cmp(&other.id) + } +} + impl Voxel { pub const EMPTY: Self = Self { id: 0 }; pub const FILLED: Self = Self { id: 1 }; @@ -65,3 +77,5 @@ voxel_material!(Sown, 雪方块, 4); voxel_material!(Water, 水, 5); voxel_material!(Sand, 沙子, 6); voxel_material!(BasicStone, 基岩, 7); +voxel_material!(DryGrass, 干草地, 8); +voxel_material!(BuleGrass, 苍翠地, 9); diff --git a/staff.ron b/staff.ron index 0b34ce8..a7540e6 100644 --- a/staff.ron +++ b/staff.ron @@ -6,5 +6,7 @@ (id:3,name:"Sand",icon_string:"textures/沙子.png",staff_type:Voxel((id:6))), (id:4,name:"Sown",icon_string:"textures/雪.png",staff_type:Voxel((id:4))), (id:5,name:"FireWork",icon_string:"staff/烟花.png",staff_type:Consumable(0)), + (id:6,name:"DryGrass",icon_string:"textures/干草地.png",staff_type:Voxel((id:8))), + (id:7,name:"BlueGrass",icon_string:"textures/苍翠地.png",staff_type:Voxel((id:9))), ] ) \ No newline at end of file diff --git a/volex.ron b/volex.ron index 10a49df..1384374 100644 --- a/volex.ron +++ b/volex.ron @@ -1,5 +1,13 @@ ( voxels:{ + 9:(type_name:"BuleGrass",type_ch_name:"苍翠地",default:(index:12,path:"textures/苍翠侧面.png"),normal:{ + 4:(index:11,path:"textures/苍翠地.png"), + 1:(index:1,path:"textures/003.png"), + }), + 8:(type_name:"DryGrass",type_ch_name:"干草块",default:(index:9,path:"textures/干草侧面.png"),normal:{ + 4:(index:10,path:"textures/干草地.png"), + 1:(index:1,path:"textures/003.png"), + }), 7:(type_name:"BaiscSton",type_ch_name:"基岩",default:(index:8,path:"textures/基岩.png"),normal:{}), 6:(type_name:"Sand",type_ch_name:"沙子",default:(index:7,path:"textures/沙子.png"),normal:{}), 5:(type_name:"Water",type_ch_name:"水",default:(index:6,path:"textures/水.png"),normal:{}), @@ -11,13 +19,20 @@ 1:(type_name:"Stone",type_ch_name:"岩石块",default:(index:0,path:"textures/002.png"),normal:{}), 2:(type_name:"Soli",type_ch_name:"土壤",default:(index:1,path:"textures/003.png"),normal:{})}, files:[ + // 0 "textures/002.png", "textures/003.png", "textures/001.png", "textures/草坪.png", "textures/grass_a.png", + //5 "textures/雪.png", "textures/水.png", "textures/沙子.png", - "textures/基岩.png" + "textures/基岩.png", + "textures/干草侧面.png", + // 10 + "textures/干草地.png", + "textures/苍翠地.png", + "textures/苍翠侧面.png" ]) \ No newline at end of file