From e9076f22fe2a49d1cd8933e54b7b00c5dd943269 Mon Sep 17 00:00:00 2001 From: untodesu Date: Fri, 12 Sep 2025 13:33:52 +0500 Subject: It compiles --- game/client/experiments.cc | 6 +- game/client/game.cc | 36 +++++-- game/client/session.cc | 10 +- game/client/world/chunk_mesher.cc | 192 +++++++++++++++---------------------- game/client/world/chunk_quad.hh | 6 +- game/client/world/player_target.cc | 15 +-- game/client/world/player_target.hh | 3 +- game/client/world/voxel_sounds.cc | 53 +++++----- game/client/world/voxel_sounds.hh | 6 +- 9 files changed, 151 insertions(+), 176 deletions(-) (limited to 'game/client') diff --git a/game/client/experiments.cc b/game/client/experiments.cc index 2b9fe89..b947012 100644 --- a/game/client/experiments.cc +++ b/game/client/experiments.cc @@ -22,7 +22,7 @@ static void on_glfw_mouse_button(const io::GlfwMouseButtonEvent& event) { if(!globals::gui_screen && session::is_ingame()) { - if((event.action == GLFW_PRESS) && (world::player_target::voxel != NULL_VOXEL_ID)) { + if((event.action == GLFW_PRESS) && world::player_target::voxel) { if(event.button == GLFW_MOUSE_BUTTON_LEFT) { experiments::attack(); return; @@ -68,13 +68,13 @@ void experiments::update_late(void) void experiments::attack(void) { - globals::dimension->set_voxel(NULL_VOXEL_ID, world::player_target::coord); + globals::dimension->set_voxel(nullptr, world::player_target::coord); } void experiments::interact(void) { if(auto info = world::item_registry::find(gui::hotbar::slots[gui::hotbar::active_slot])) { - if(info->place_voxel != NULL_VOXEL_ID) { + if(info->place_voxel) { globals::dimension->set_voxel(info->place_voxel, world::player_target::coord + world::player_target::normal); } } diff --git a/game/client/game.cc b/game/client/game.cc index a59aec3..257bd2d 100644 --- a/game/client/game.cc +++ b/game/client/game.cc @@ -372,24 +372,40 @@ void client_game::init_late(void) // NOTE: this is very debug, early and a quite // conservative limit choice; there must be a better // way to make this limit way smaller than it currently is - for(const std::shared_ptr& info : world::voxel_registry::voxels) { - for(const world::VoxelTexture& vtex : info->textures) { - max_texture_count += vtex.paths.size(); - } + for(const auto& voxel : world::voxel_registry::voxels) { + max_texture_count += voxel->get_default_textures().size(); + max_texture_count += voxel->get_face_textures(world::VFACE_NORTH).size(); + max_texture_count += voxel->get_face_textures(world::VFACE_SOUTH).size(); + max_texture_count += voxel->get_face_textures(world::VFACE_EAST).size(); + max_texture_count += voxel->get_face_textures(world::VFACE_WEST).size(); + max_texture_count += voxel->get_face_textures(world::VFACE_TOP).size(); + max_texture_count += voxel->get_face_textures(world::VFACE_BOTTOM).size(); + max_texture_count += voxel->get_face_textures(world::VFACE_CROSS_NWSE).size(); + max_texture_count += voxel->get_face_textures(world::VFACE_CROSS_NESW).size(); } // UNDONE: asset packs for non-16x16 stuff world::voxel_atlas::create(16, 16, max_texture_count); - for(std::shared_ptr& info : world::voxel_registry::voxels) { - for(world::VoxelTexture& vtex : info->textures) { - if(auto strip = world::voxel_atlas::find_or_load(vtex.paths)) { - vtex.cached_offset = strip->offset; - vtex.cached_plane = strip->plane; + for(auto& voxel : world::voxel_registry::voxels) { + constexpr std::array faces = { + world::VFACE_NORTH, + world::VFACE_SOUTH, + world::VFACE_EAST, + world::VFACE_WEST, + world::VFACE_TOP, + world::VFACE_BOTTOM, + world::VFACE_CROSS_NWSE, + world::VFACE_CROSS_NESW, + }; + + for(auto face : faces) { + if(auto strip = world::voxel_atlas::find_or_load(voxel->get_face_textures(face))) { + voxel->set_face_cache(face, strip->offset, strip->plane); continue; } - spdlog::critical("client_gl: {}: failed to load atlas strips", info->name); + spdlog::critical("client_gl: {}: failed to load atlas strips", voxel->get_name()); std::terminate(); } } diff --git a/game/client/session.cc b/game/client/session.cc index 7e0d36c..c33b4d9 100644 --- a/game/client/session.cc +++ b/game/client/session.cc @@ -98,8 +98,10 @@ static void on_set_voxel_packet(const protocol::SetVoxel& packet) auto index = coord::to_index(lpos); if(auto chunk = globals::dimension->find_chunk(cpos)) { - if(chunk->get_voxel(index) != packet.voxel) { - chunk->set_voxel(packet.voxel, index); + auto packet_voxel = world::voxel_registry::find(packet.voxel); + + if(chunk->get_voxel(index) != packet_voxel) { + chunk->set_voxel(packet_voxel, index); world::ChunkUpdateEvent event; event.dimension = globals::dimension; @@ -125,7 +127,7 @@ static void on_voxel_set(const world::VoxelSetEvent& event) // FIXME: should we also validate things here or wait for the server to do so protocol::SetVoxel packet; packet.vpos = coord::to_voxel(event.cpos, event.lpos); - packet.voxel = event.voxel; + packet.voxel = event.voxel->get_id(); protocol::send(session::peer, protocol::encode(packet)); } @@ -283,7 +285,7 @@ void session::send_login_request(void) { protocol::LoginRequest packet; packet.version = protocol::VERSION; - packet.voxel_registry_checksum = world::voxel_registry::calculate_checksum(); + packet.voxel_registry_checksum = world::voxel_registry::get_checksum(); packet.item_registry_checksum = world::item_registry::calculate_checksum(); packet.password_hash = server_password_hash; packet.username = client_game::username.get(); diff --git a/game/client/world/chunk_mesher.cc b/game/client/world/chunk_mesher.cc index a8ee817..bc90a03 100644 --- a/game/client/world/chunk_mesher.cc +++ b/game/client/world/chunk_mesher.cc @@ -8,6 +8,7 @@ #include "shared/world/chunk.hh" #include "shared/world/dimension.hh" +#include "shared/world/voxel.hh" #include "shared/world/voxel_registry.hh" #include "shared/coord.hh" @@ -56,37 +57,6 @@ static const CachedChunkCoord get_cached_cpos(const chunk_pos& pivot, const chun return CPOS_ITSELF; } -static world::voxel_facing get_facing(world::voxel_face face, world::voxel_type type) -{ - if(type == world::voxel_type::CROSS) { - switch(face) { - case world::voxel_face::CROSS_NESW: - return world::voxel_facing::NESW; - case world::voxel_face::CROSS_NWSE: - return world::voxel_facing::NWSE; - default: - return world::voxel_facing::NORTH; - } - } - - switch(face) { - case world::voxel_face::CUBE_NORTH: - return world::voxel_facing::NORTH; - case world::voxel_face::CUBE_SOUTH: - return world::voxel_facing::SOUTH; - case world::voxel_face::CUBE_EAST: - return world::voxel_facing::EAST; - case world::voxel_face::CUBE_WEST: - return world::voxel_facing::WEST; - case world::voxel_face::CUBE_TOP: - return world::voxel_facing::UP; - case world::voxel_face::CUBE_BOTTOM: - return world::voxel_facing::DOWN; - default: - return world::voxel_facing::NORTH; - } -} - class GL_MeshingTask final : public Task { public: explicit GL_MeshingTask(entt::entity entity, const chunk_pos& cpos); @@ -95,11 +65,10 @@ public: virtual void finalize(void) override; private: - bool vis_test(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos) const; - void push_quad_a(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face); - void push_quad_v(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face, - std::size_t entropy); - void make_cube(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos, world::voxel_vis vis, std::size_t entropy); + bool vis_test(const world::Voxel* voxel, const local_pos& lpos) const; + void push_quad_a(const world::Voxel* voxel, const glm::fvec3& pos, const glm::fvec2& size, world::VoxelFace face); + void push_quad_v(const world::Voxel* voxel, const glm::fvec3& pos, const glm::fvec2& size, world::VoxelFace face, std::size_t entropy); + void make_cube(const world::Voxel* voxel, const local_pos& lpos, world::VoxelVisBits vis, std::size_t entropy); void cache_chunk(const chunk_pos& cpos); private: @@ -138,41 +107,39 @@ void GL_MeshingTask::process(void) return; } - const auto voxel = voxels[i]; const auto lpos = coord::to_local(i); + const auto voxel = world::voxel_registry::find(voxels[i]); - const auto info = world::voxel_registry::find(voxel); - - if(info == nullptr) { + if(voxel == nullptr) { // Either a NULL_VOXEL_ID or something went // horribly wrong and we don't what this is continue; } - world::voxel_vis vis = 0; + unsigned int vis = 0U; - if(vis_test(voxel, info, lpos + DIR_NORTH)) { - vis |= world::VIS_NORTH; + if(vis_test(voxel, lpos + DIR_NORTH)) { + vis |= world::VVIS_NORTH; } - if(vis_test(voxel, info, lpos + DIR_SOUTH)) { - vis |= world::VIS_SOUTH; + if(vis_test(voxel, lpos + DIR_SOUTH)) { + vis |= world::VVIS_SOUTH; } - if(vis_test(voxel, info, lpos + DIR_EAST)) { - vis |= world::VIS_EAST; + if(vis_test(voxel, lpos + DIR_EAST)) { + vis |= world::VVIS_EAST; } - if(vis_test(voxel, info, lpos + DIR_WEST)) { - vis |= world::VIS_WEST; + if(vis_test(voxel, lpos + DIR_WEST)) { + vis |= world::VVIS_WEST; } - if(vis_test(voxel, info, lpos + DIR_UP)) { - vis |= world::VIS_UP; + if(vis_test(voxel, lpos + DIR_UP)) { + vis |= world::VVIS_UP; } - if(vis_test(voxel, info, lpos + DIR_DOWN)) { - vis |= world::VIS_DOWN; + if(vis_test(voxel, lpos + DIR_DOWN)) { + vis |= world::VVIS_DOWN; } const auto vpos = coord::to_voxel(m_cpos, lpos); @@ -180,7 +147,7 @@ void GL_MeshingTask::process(void) const auto entropy = math::crc64(&entropy_src, sizeof(entropy_src)); // FIXME: handle different voxel types - make_cube(voxel, info, lpos, vis, entropy); + make_cube(voxel, lpos, world::VoxelVisBits(vis), entropy); } } @@ -254,7 +221,7 @@ void GL_MeshingTask::finalize(void) } } -bool GL_MeshingTask::vis_test(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos) const +bool GL_MeshingTask::vis_test(const world::Voxel* voxel, const local_pos& lpos) const { const auto pvpos = coord::to_voxel(m_cpos, lpos); const auto pcpos = coord::to_chunk(pvpos); @@ -263,26 +230,18 @@ bool GL_MeshingTask::vis_test(voxel_id voxel, const world::VoxelInfo* info, cons const auto cached_cpos = get_cached_cpos(m_cpos, pcpos); const auto& voxels = m_cache.at(cached_cpos); - const auto neighbour = voxels[index]; + const auto neighbour = world::voxel_registry::find(voxels[index]); bool result; - if(neighbour == NULL_VOXEL_ID) { + if(neighbour == nullptr) { result = true; } else if(neighbour == voxel) { result = false; } - else if(auto neighbour_info = world::voxel_registry::find(neighbour)) { - if(neighbour_info->blending != info->blending) { - // Voxel types that use blending are semi-transparent; - // this means they're rendered using a different setup - // and they must have visible faces with opaque voxels - result = neighbour_info->blending; - } - else { - result = false; - } + else if(neighbour->get_render_mode() != voxel->get_render_mode()) { + result = true; } else { result = false; @@ -291,88 +250,95 @@ bool GL_MeshingTask::vis_test(voxel_id voxel, const world::VoxelInfo* info, cons return result; } -void GL_MeshingTask::push_quad_a(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face) +void GL_MeshingTask::push_quad_a(const world::Voxel* voxel, const glm::fvec3& pos, const glm::fvec2& size, world::VoxelFace face) { - const world::voxel_facing facing = get_facing(face, info->type); - const world::VoxelTexture& vtex = info->textures[static_cast(face)]; - - if(info->blending) { - m_quads_b[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset, vtex.paths.size())); - } - else { - m_quads_s[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset, vtex.paths.size())); + auto cached_offset = voxel->get_cached_face_offset(face); + auto cached_plane = voxel->get_cached_face_plane(face); + auto& textures = voxel->get_face_textures(face); + + switch(voxel->get_render_mode()) { + case world::VRENDER_OPAQUE: + m_quads_s[cached_plane].push_back(make_chunk_quad(pos, size, face, cached_offset, textures.size())); + break; + + case world::VRENDER_BLEND: + m_quads_b[cached_plane].push_back(make_chunk_quad(pos, size, face, cached_offset, textures.size())); + break; } } -void GL_MeshingTask::push_quad_v(const world::VoxelInfo* info, const glm::fvec3& pos, const glm::fvec2& size, world::voxel_face face, +void GL_MeshingTask::push_quad_v(const world::Voxel* voxel, const glm::fvec3& pos, const glm::fvec2& size, world::VoxelFace face, std::size_t entropy) { - const world::voxel_facing facing = get_facing(face, info->type); - const world::VoxelTexture& vtex = info->textures[static_cast(face)]; - const std::size_t entropy_mod = entropy % vtex.paths.size(); - - if(info->blending) { - m_quads_b[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset + entropy_mod, 0)); - } - else { - m_quads_s[vtex.cached_plane].push_back(make_chunk_quad(pos, size, facing, vtex.cached_offset + entropy_mod, 0)); + auto cached_offset = voxel->get_cached_face_offset(face); + auto cached_plane = voxel->get_cached_face_plane(face); + auto& textures = voxel->get_face_textures(face); + auto index = entropy % textures.size(); + + switch(voxel->get_render_mode()) { + case world::VRENDER_OPAQUE: + m_quads_s[cached_plane].push_back(make_chunk_quad(pos, size, face, cached_offset + index, 0)); + break; + + case world::VRENDER_BLEND: + m_quads_b[cached_plane].push_back(make_chunk_quad(pos, size, face, cached_offset + index, 0)); + break; } } -void GL_MeshingTask::make_cube(voxel_id voxel, const world::VoxelInfo* info, const local_pos& lpos, world::voxel_vis vis, - std::size_t entropy) +void GL_MeshingTask::make_cube(const world::Voxel* voxel, const local_pos& lpos, world::VoxelVisBits vis, std::size_t entropy) { const glm::fvec3 fpos = glm::fvec3(lpos); const glm::fvec2 fsize = glm::fvec2(1.0f, 1.0f); - if(info->animated) { - if(vis & world::VIS_NORTH) { - push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_NORTH); + if(voxel->is_animated()) { + if(vis & world::VVIS_NORTH) { + push_quad_a(voxel, fpos, fsize, world::VFACE_NORTH); } - if(vis & world::VIS_SOUTH) { - push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_SOUTH); + if(vis & world::VVIS_SOUTH) { + push_quad_a(voxel, fpos, fsize, world::VFACE_SOUTH); } - if(vis & world::VIS_EAST) { - push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_EAST); + if(vis & world::VVIS_EAST) { + push_quad_a(voxel, fpos, fsize, world::VFACE_EAST); } - if(vis & world::VIS_WEST) { - push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_WEST); + if(vis & world::VVIS_WEST) { + push_quad_a(voxel, fpos, fsize, world::VFACE_WEST); } - if(vis & world::VIS_UP) { - push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_TOP); + if(vis & world::VVIS_UP) { + push_quad_a(voxel, fpos, fsize, world::VFACE_TOP); } - if(vis & world::VIS_DOWN) { - push_quad_a(info, fpos, fsize, world::voxel_face::CUBE_BOTTOM); + if(vis & world::VVIS_DOWN) { + push_quad_a(voxel, fpos, fsize, world::VFACE_BOTTOM); } } else { - if(vis & world::VIS_NORTH) { - push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_NORTH, entropy); + if(vis & world::VVIS_NORTH) { + push_quad_v(voxel, fpos, fsize, world::VFACE_NORTH, entropy); } - if(vis & world::VIS_SOUTH) { - push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_SOUTH, entropy); + if(vis & world::VVIS_SOUTH) { + push_quad_v(voxel, fpos, fsize, world::VFACE_SOUTH, entropy); } - if(vis & world::VIS_EAST) { - push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_EAST, entropy); + if(vis & world::VVIS_EAST) { + push_quad_v(voxel, fpos, fsize, world::VFACE_EAST, entropy); } - if(vis & world::VIS_WEST) { - push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_WEST, entropy); + if(vis & world::VVIS_WEST) { + push_quad_v(voxel, fpos, fsize, world::VFACE_WEST, entropy); } - if(vis & world::VIS_UP) { - push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_TOP, entropy); + if(vis & world::VVIS_UP) { + push_quad_v(voxel, fpos, fsize, world::VFACE_TOP, entropy); } - if(vis & world::VIS_DOWN) { - push_quad_v(info, fpos, fsize, world::voxel_face::CUBE_BOTTOM, entropy); + if(vis & world::VVIS_DOWN) { + push_quad_v(voxel, fpos, fsize, world::VFACE_BOTTOM, entropy); } } } diff --git a/game/client/world/chunk_quad.hh b/game/client/world/chunk_quad.hh index c15bb7a..d68977e 100644 --- a/game/client/world/chunk_quad.hh +++ b/game/client/world/chunk_quad.hh @@ -13,8 +13,8 @@ using ChunkQuad = std::array; namespace world { -constexpr inline static ChunkQuad make_chunk_quad(const glm::fvec3& position, const glm::fvec2& size, voxel_facing facing, - std::size_t texture, std::size_t frames) +constexpr inline static ChunkQuad make_chunk_quad(const glm::fvec3& position, const glm::fvec2& size, VoxelFace face, std::size_t texture, + std::size_t frames) { ChunkQuad result = {}; result[0] = 0x00000000; @@ -30,7 +30,7 @@ constexpr inline static ChunkQuad make_chunk_quad(const glm::fvec3& position, co result[0] |= (0x0000000FU & static_cast(size.y * 16.0f - 1.0f)); // [1] FFFF---------------------------- - result[1] |= (0x0000000FU & static_cast(facing)) << 28U; + result[1] |= (0x0000000FU & static_cast(face)) << 28U; // [1] ----TTTTTTTTTTTAAAAA------------ result[1] |= (0x000007FFU & static_cast(texture)) << 17U; diff --git a/game/client/world/player_target.cc b/game/client/world/player_target.cc index f0550c0..3ede47e 100644 --- a/game/client/world/player_target.cc +++ b/game/client/world/player_target.cc @@ -16,17 +16,15 @@ constexpr static float MAX_REACH = 16.0f; -voxel_id world::player_target::voxel; voxel_pos world::player_target::coord; voxel_pos world::player_target::normal; -const world::VoxelInfo* world::player_target::info; +const world::Voxel* world::player_target::voxel; void world::player_target::init(void) { - world::player_target::voxel = NULL_VOXEL_ID; world::player_target::coord = voxel_pos(); world::player_target::normal = voxel_pos(); - world::player_target::info = nullptr; + world::player_target::voxel = nullptr; } void world::player_target::update(void) @@ -37,29 +35,26 @@ void world::player_target::update(void) do { world::player_target::voxel = ray.step(); - if(world::player_target::voxel != NULL_VOXEL_ID) { + if(world::player_target::voxel) { world::player_target::coord = ray.vpos; world::player_target::normal = ray.vnormal; - world::player_target::info = world::voxel_registry::find(world::player_target::voxel); break; } world::player_target::coord = voxel_pos(); world::player_target::normal = voxel_pos(); - world::player_target::info = nullptr; } while(ray.distance < MAX_REACH); } else { - world::player_target::voxel = NULL_VOXEL_ID; + world::player_target::voxel = nullptr; world::player_target::coord = voxel_pos(); world::player_target::normal = voxel_pos(); - world::player_target::info = nullptr; } } void world::player_target::render(void) { - if((world::player_target::voxel != NULL_VOXEL_ID) && !client_game::hide_hud) { + if(world::player_target::voxel && !client_game::hide_hud) { auto cpos = coord::to_chunk(world::player_target::coord); auto fpos = coord::to_local(world::player_target::coord); diff --git a/game/client/world/player_target.hh b/game/client/world/player_target.hh index f0db9be..34532c3 100644 --- a/game/client/world/player_target.hh +++ b/game/client/world/player_target.hh @@ -4,10 +4,9 @@ namespace world::player_target { -extern voxel_id voxel; extern voxel_pos coord; extern voxel_pos normal; -extern const VoxelInfo* info; +extern const Voxel* voxel; } // namespace world::player_target namespace world::player_target diff --git a/game/client/world/voxel_sounds.cc b/game/client/world/voxel_sounds.cc index 481e615..fe91f01 100644 --- a/game/client/world/voxel_sounds.cc +++ b/game/client/world/voxel_sounds.cc @@ -4,24 +4,21 @@ #include "client/resource/sound_effect.hh" -constexpr static std::size_t NUM_SURFACES = static_cast(world::voxel_surface::COUNT); - -static std::vector> footsteps_sounds[NUM_SURFACES]; +static std::vector> footsteps_sounds[world::VMAT_COUNT]; static std::mt19937_64 randomizer; -static void add_footsteps_effect(world::voxel_surface surface, std::string_view name) +static void add_footsteps_effect(world::VoxelMaterial material, std::string_view name) { if(auto effect = resource::load(name)) { - auto surface_index = static_cast(surface); - footsteps_sounds[surface_index].push_back(effect); + footsteps_sounds[material].push_back(effect); } } -static resource_ptr get_footsteps_effect(world::voxel_surface surface) +static resource_ptr get_footsteps_effect(world::VoxelMaterial material) { - auto surface_index = static_cast(surface); + auto surface_index = static_cast(material); - if(surface_index >= NUM_SURFACES) { + if(surface_index >= world::VMAT_COUNT) { // Surface index out of range return nullptr; } @@ -39,48 +36,48 @@ static resource_ptr get_footsteps_effect(world::voxel_surface surfa void world::voxel_sounds::init(void) { - add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default1.wav"); - add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default2.wav"); - add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default3.wav"); - add_footsteps_effect(voxel_surface::DEFAULT, "sounds/surface/default4.wav"); + add_footsteps_effect(VMAT_DEFAULT, "sounds/surface/default1.wav"); + add_footsteps_effect(VMAT_DEFAULT, "sounds/surface/default2.wav"); + add_footsteps_effect(VMAT_DEFAULT, "sounds/surface/default3.wav"); + add_footsteps_effect(VMAT_DEFAULT, "sounds/surface/default4.wav"); - add_footsteps_effect(voxel_surface::DIRT, "sounds/surface/dirt1.wav"); + add_footsteps_effect(VMAT_DIRT, "sounds/surface/dirt1.wav"); - add_footsteps_effect(voxel_surface::GRASS, "sounds/surface/grass1.wav"); - add_footsteps_effect(voxel_surface::GRASS, "sounds/surface/grass2.wav"); - add_footsteps_effect(voxel_surface::GRASS, "sounds/surface/grass3.wav"); + add_footsteps_effect(VMAT_GRASS, "sounds/surface/grass1.wav"); + add_footsteps_effect(VMAT_GRASS, "sounds/surface/grass2.wav"); + add_footsteps_effect(VMAT_GRASS, "sounds/surface/grass3.wav"); - add_footsteps_effect(voxel_surface::GRAVEL, "sounds/surface/gravel1.wav"); + add_footsteps_effect(VMAT_GRAVEL, "sounds/surface/gravel1.wav"); - add_footsteps_effect(voxel_surface::SAND, "sounds/surface/sand1.wav"); - add_footsteps_effect(voxel_surface::SAND, "sounds/surface/sand2.wav"); + add_footsteps_effect(VMAT_SAND, "sounds/surface/sand1.wav"); + add_footsteps_effect(VMAT_SAND, "sounds/surface/sand2.wav"); - add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood1.wav"); - add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood2.wav"); - add_footsteps_effect(voxel_surface::WOOD, "sounds/surface/wood3.wav"); + add_footsteps_effect(VMAT_WOOD, "sounds/surface/wood1.wav"); + add_footsteps_effect(VMAT_WOOD, "sounds/surface/wood2.wav"); + add_footsteps_effect(VMAT_WOOD, "sounds/surface/wood3.wav"); } void world::voxel_sounds::shutdown(void) { - for(std::size_t i = 0; i < NUM_SURFACES; ++i) { + for(std::size_t i = 0; i < world::VMAT_COUNT; ++i) { footsteps_sounds[i].clear(); } } -resource_ptr world::voxel_sounds::get_footsteps(voxel_surface surface) +resource_ptr world::voxel_sounds::get_footsteps(world::VoxelMaterial material) { - if(auto effect = get_footsteps_effect(surface)) { + if(auto effect = get_footsteps_effect(material)) { return effect; } - if(auto effect = get_footsteps_effect(voxel_surface::DEFAULT)) { + if(auto effect = get_footsteps_effect(VMAT_DEFAULT)) { return effect; } return nullptr; } -resource_ptr world::voxel_sounds::get_placebreak(voxel_surface surface) +resource_ptr world::voxel_sounds::get_placebreak(world::VoxelMaterial material) { return nullptr; } diff --git a/game/client/world/voxel_sounds.hh b/game/client/world/voxel_sounds.hh index 09f5e2e..d0f3e07 100644 --- a/game/client/world/voxel_sounds.hh +++ b/game/client/world/voxel_sounds.hh @@ -2,7 +2,7 @@ #include "core/resource/resource.hh" -#include "shared/world/voxel_registry.hh" +#include "shared/world/voxel.hh" struct SoundEffect; @@ -14,6 +14,6 @@ void shutdown(void); namespace world::voxel_sounds { -resource_ptr get_footsteps(voxel_surface surface); -resource_ptr get_placebreak(voxel_surface surface); +resource_ptr get_footsteps(VoxelMaterial material); +resource_ptr get_placebreak(VoxelMaterial material); } // namespace world::voxel_sounds -- cgit From 73cbcdd6e8c849e32abbf9757e603e6a6654e870 Mon Sep 17 00:00:00 2001 From: untodesu Date: Fri, 12 Sep 2025 14:09:34 +0500 Subject: Metaitems --- game/client/experiments.cc | 9 ++++++--- game/client/game.cc | 4 ++-- game/client/gui/hotbar.cc | 19 +++++++++---------- game/client/gui/hotbar.hh | 9 ++++++--- game/client/gui/status_lines.cc | 4 ++-- game/client/gui/status_lines.hh | 2 +- game/client/session.cc | 4 ++-- 7 files changed, 28 insertions(+), 23 deletions(-) (limited to 'game/client') diff --git a/game/client/experiments.cc b/game/client/experiments.cc index b947012..8b0b526 100644 --- a/game/client/experiments.cc +++ b/game/client/experiments.cc @@ -73,9 +73,12 @@ void experiments::attack(void) void experiments::interact(void) { - if(auto info = world::item_registry::find(gui::hotbar::slots[gui::hotbar::active_slot])) { - if(info->place_voxel) { - globals::dimension->set_voxel(info->place_voxel, world::player_target::coord + world::player_target::normal); + auto active_item = gui::hotbar::slots[gui::hotbar::active_slot]; + + if(active_item) { + if(auto place_voxel = active_item->get_place_voxel()) { + globals::dimension->set_voxel(place_voxel, world::player_target::coord + world::player_target::normal); + return; } } } diff --git a/game/client/game.cc b/game/client/game.cc index 257bd2d..d61ce84 100644 --- a/game/client/game.cc +++ b/game/client/game.cc @@ -412,8 +412,8 @@ void client_game::init_late(void) world::voxel_atlas::generate_mipmaps(); - for(std::shared_ptr& info : world::item_registry::items) { - info->cached_texture = resource::load(info->texture.c_str(), TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T); + for(auto& item : world::item_registry::items) { + item->set_cached_texture(resource::load(item->get_texture(), TEXTURE_GUI_LOAD_CLAMP_S | TEXTURE_GUI_LOAD_CLAMP_T)); } experiments::init_late(); diff --git a/game/client/gui/hotbar.cc b/game/client/gui/hotbar.cc index 806d82b..e9458dd 100644 --- a/game/client/gui/hotbar.cc +++ b/game/client/gui/hotbar.cc @@ -25,7 +25,7 @@ constexpr static float SELECTOR_PADDING = 1.0f; constexpr static float HOTBAR_PADDING = 2.0f; unsigned int gui::hotbar::active_slot = 0U; -item_id gui::hotbar::slots[HOTBAR_SIZE]; +std::array gui::hotbar::slots = {}; static config::KeyBind hotbar_keys[HOTBAR_SIZE]; @@ -40,14 +40,13 @@ static ImU32 get_color_alpha(ImGuiCol style_color, float alpha) static void update_hotbar_item(void) { - if(gui::hotbar::slots[gui::hotbar::active_slot] == NULL_ITEM_ID) { + auto current_item = gui::hotbar::slots[gui::hotbar::active_slot]; + + if(current_item == nullptr) { gui::status_lines::unset(gui::STATUS_HOTBAR); - return; } - - if(auto info = world::item_registry::find(gui::hotbar::slots[gui::hotbar::active_slot])) { - gui::status_lines::set(gui::STATUS_HOTBAR, info->name, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), 5.0f); - return; + else { + gui::status_lines::set(gui::STATUS_HOTBAR, current_item->get_name(), ImVec4(1.0f, 1.0f, 1.0f, 1.0f), 5.0f); } } @@ -154,9 +153,9 @@ void gui::hotbar::layout(void) // Draw individual item textures in the hotbar for(std::size_t i = 0; i < HOTBAR_SIZE; ++i) { - const auto info = world::item_registry::find(gui::hotbar::slots[i]); + auto item = gui::hotbar::slots[i]; - if((info == nullptr) || (info->cached_texture == nullptr)) { + if((item == nullptr) || (item->get_cached_texture() == nullptr)) { // There's either no item in the slot // or the item doesn't have a texture continue; @@ -164,7 +163,7 @@ void gui::hotbar::layout(void) const auto item_start = ImVec2(background_start.x + i * item_size + item_padding_a, background_start.y + item_padding_a); const auto item_end = ImVec2(item_start.x + item_size - item_padding_b, item_start.y + item_size - item_padding_b); - draw_list->AddImage(info->cached_texture->handle, item_start, item_end); + draw_list->AddImage(item->get_cached_texture()->handle, item_start, item_end); } } diff --git a/game/client/gui/hotbar.hh b/game/client/gui/hotbar.hh index 88ce791..223dbc9 100644 --- a/game/client/gui/hotbar.hh +++ b/game/client/gui/hotbar.hh @@ -1,16 +1,19 @@ #pragma once -#include "shared/types.hh" - // TODO: design an inventory system and an item // registry and integrate the hotbar into that system +namespace world +{ +class Item; +} // namespace world + constexpr static unsigned int HOTBAR_SIZE = 9U; namespace gui::hotbar { extern unsigned int active_slot; -extern item_id slots[HOTBAR_SIZE]; +extern std::array slots; } // namespace gui::hotbar namespace gui::hotbar diff --git a/game/client/gui/status_lines.cc b/game/client/gui/status_lines.cc index d1a919a..c146478 100644 --- a/game/client/gui/status_lines.cc +++ b/game/client/gui/status_lines.cc @@ -65,11 +65,11 @@ void gui::status_lines::layout(void) } } -void gui::status_lines::set(unsigned int line, const std::string& text, const ImVec4& color, float fadeout) +void gui::status_lines::set(unsigned int line, std::string_view text, const ImVec4& color, float fadeout) { line_text_colors[line] = ImVec4(color.x, color.y, color.z, color.w); line_shadow_colors[line] = ImVec4(color.x * 0.1f, color.y * 0.1f, color.z * 0.1f, color.w); - line_strings[line] = std::string(text); + line_strings[line] = text; line_spawns[line] = globals::curtime; line_fadeouts[line] = fadeout; } diff --git a/game/client/gui/status_lines.hh b/game/client/gui/status_lines.hh index 7245d68..f694fd3 100644 --- a/game/client/gui/status_lines.hh +++ b/game/client/gui/status_lines.hh @@ -16,6 +16,6 @@ void layout(void); namespace gui::status_lines { -void set(unsigned int line, const std::string& text, const ImVec4& color, float fadeout); +void set(unsigned int line, std::string_view text, const ImVec4& color, float fadeout); void unset(unsigned int line); } // namespace gui::status_lines diff --git a/game/client/session.cc b/game/client/session.cc index c33b4d9..907b789 100644 --- a/game/client/session.cc +++ b/game/client/session.cc @@ -127,7 +127,7 @@ static void on_voxel_set(const world::VoxelSetEvent& event) // FIXME: should we also validate things here or wait for the server to do so protocol::SetVoxel packet; packet.vpos = coord::to_voxel(event.cpos, event.lpos); - packet.voxel = event.voxel->get_id(); + packet.voxel = event.voxel ? event.voxel->get_id() : NULL_VOXEL_ID; protocol::send(session::peer, protocol::encode(packet)); } @@ -286,7 +286,7 @@ void session::send_login_request(void) protocol::LoginRequest packet; packet.version = protocol::VERSION; packet.voxel_registry_checksum = world::voxel_registry::get_checksum(); - packet.item_registry_checksum = world::item_registry::calculate_checksum(); + packet.item_registry_checksum = world::item_registry::get_checksum(); packet.password_hash = server_password_hash; packet.username = client_game::username.get(); -- cgit From f210a86c1406ccc6dfd6f14181dd7a1274ee0de4 Mon Sep 17 00:00:00 2001 From: untodesu Date: Fri, 12 Sep 2025 15:09:01 +0500 Subject: Random ticking? In my game?! Hell yeah! --- game/client/io/glfw.hh | 4 ---- 1 file changed, 4 deletions(-) (limited to 'game/client') diff --git a/game/client/io/glfw.hh b/game/client/io/glfw.hh index bbd767a..7697d97 100644 --- a/game/client/io/glfw.hh +++ b/game/client/io/glfw.hh @@ -1,5 +1,3 @@ -#ifndef CLIENTFW -#define CLIENTFW 1 #pragma once namespace io @@ -36,5 +34,3 @@ struct GlfwScrollEvent final { float dy; }; } // namespace io - -#endif // CLIENTFW -- cgit From 522a7514012da86f7b9643179f0763746f3b232e Mon Sep 17 00:00:00 2001 From: untodesu Date: Fri, 12 Sep 2025 16:15:32 +0500 Subject: Protocol and versioning changes --- game/client/gui/bother.cc | 8 +++- game/client/gui/bother.hh | 4 +- game/client/gui/play_menu.cc | 92 +++++++++++++++++++++++++++-------------- game/client/gui/window_title.cc | 11 +---- game/client/session.cc | 6 ++- 5 files changed, 76 insertions(+), 45 deletions(-) (limited to 'game/client') diff --git a/game/client/gui/bother.cc b/game/client/gui/bother.cc index 1bb7097..e87b9ff 100644 --- a/game/client/gui/bother.cc +++ b/game/client/gui/bother.cc @@ -2,6 +2,8 @@ #include "client/gui/bother.hh" +#include "core/version.hh" + #include "shared/protocol.hh" #include "client/globals.hh" @@ -29,10 +31,12 @@ static void on_status_response_packet(const protocol::StatusResponse& packet) gui::BotherResponseEvent event; event.identity = identity; event.is_server_unreachable = false; - event.protocol_version = packet.version; event.num_players = packet.num_players; event.max_players = packet.max_players; event.motd = packet.motd; + event.game_version_major = packet.game_version_major; + event.game_version_minor = packet.game_version_minor; + event.game_version_patch = packet.game_version_patch; globals::dispatcher.trigger(event); enet_peer_disconnect(packet.peer, protocol::CHANNEL); @@ -89,7 +93,7 @@ void gui::bother::update_late(void) if(0 < enet_host_service(bother_host, &enet_event, 0)) { if(enet_event.type == ENET_EVENT_TYPE_CONNECT) { protocol::StatusRequest packet; - packet.version = protocol::VERSION; + packet.game_version_major = version::major; protocol::send(enet_event.peer, protocol::encode(packet)); return; } diff --git a/game/client/gui/bother.hh b/game/client/gui/bother.hh index fc5bab4..75e56d1 100644 --- a/game/client/gui/bother.hh +++ b/game/client/gui/bother.hh @@ -5,9 +5,11 @@ namespace gui struct BotherResponseEvent final { unsigned int identity; bool is_server_unreachable; - std::uint32_t protocol_version; std::uint16_t num_players; std::uint16_t max_players; + std::uint32_t game_version_major; + std::uint32_t game_version_minor; + std::uint32_t game_version_patch; std::string motd; }; } // namespace gui diff --git a/game/client/gui/play_menu.cc b/game/client/gui/play_menu.cc index 5b9887e..ad85141 100644 --- a/game/client/gui/play_menu.cc +++ b/game/client/gui/play_menu.cc @@ -10,6 +10,8 @@ #include "core/utils/string.hh" +#include "core/version.hh" + #include "shared/protocol.hh" #include "client/gui/bother.hh" @@ -25,9 +27,8 @@ constexpr static ImGuiWindowFlags WINDOW_FLAGS = ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoDecoration; constexpr static std::string_view DEFAULT_SERVER_NAME = "Voxelius Server"; constexpr static std::string_view SERVERS_TXT = "servers.txt"; -constexpr static std::string_view WARNING_TOAST = "[!]"; -constexpr static std::size_t MAX_SERVER_ITEM_NAME = 24; +constexpr static std::size_t MAX_SERVER_ITEM_NAME = 18; enum class item_status : unsigned int { UNKNOWN = 0x0000U, @@ -43,10 +44,12 @@ struct ServerStatusItem final { std::uint16_t port; // Things pulled from bother events - std::uint32_t protocol_version; std::uint16_t num_players; std::uint16_t max_players; std::string motd; + std::uint16_t game_version_major; + std::uint16_t game_version_minor; + std::uint16_t game_version_patch; // Unique identifier that monotonically // grows with each new server added and @@ -69,9 +72,6 @@ static std::string str_status_init; static std::string str_status_ping; static std::string str_status_fail; -static std::string str_outdated_client; -static std::string str_outdated_server; - static std::string input_itemname; static std::string input_hostname; static std::string input_password; @@ -106,11 +106,13 @@ static void add_new_server(void) { auto item = new ServerStatusItem(); item->port = protocol::PORT; - item->protocol_version = protocol::VERSION; item->max_players = UINT16_MAX; item->num_players = UINT16_MAX; item->identity = next_identity; item->status = item_status::UNKNOWN; + item->game_version_major = 0U; + item->game_version_minor = 0U; + item->game_version_patch = 0U; next_identity += 1U; @@ -202,9 +204,6 @@ static void on_language_set(const gui::LanguageSetEvent& event) str_status_init = gui::language::resolve("play_menu.status.init"); str_status_ping = gui::language::resolve("play_menu.status.ping"); str_status_fail = gui::language::resolve("play_menu.status.fail"); - - str_outdated_client = gui::language::resolve("play_menu.outdated_client"); - str_outdated_server = gui::language::resolve("play_menu.outdated_server"); } static void on_bother_response(const gui::BotherResponseEvent& event) @@ -212,18 +211,22 @@ static void on_bother_response(const gui::BotherResponseEvent& event) for(auto item : servers_deque) { if(item->identity == event.identity) { if(event.is_server_unreachable) { - item->protocol_version = 0U; item->num_players = UINT16_MAX; item->max_players = UINT16_MAX; item->motd = str_status_fail; item->status = item_status::FAILURE; + item->game_version_major = 0U; + item->game_version_minor = 0U; + item->game_version_patch = 0U; } else { - item->protocol_version = event.protocol_version; item->num_players = event.num_players; item->max_players = event.max_players; item->motd = event.motd; item->status = item_status::REACHED; + item->game_version_major = event.game_version_major; + item->game_version_minor = event.game_version_minor; + item->game_version_patch = event.game_version_patch; } break; @@ -264,30 +267,50 @@ static void layout_server_item(ServerStatusItem* item) if(item->status == item_status::REACHED) { auto stats = std::format("{}/{}", item->num_players, item->max_players); - auto stats_width = ImGui::CalcTextSize(stats.c_str(), stats.c_str() + stats.size()).x; - auto stats_pos = ImVec2(cursor.x + item_width - stats_width - padding.x, cursor.y + padding.y); + auto stats_size = ImGui::CalcTextSize(stats.c_str(), stats.c_str() + stats.size()); + auto stats_pos = ImVec2(cursor.x + item_width - stats_size.x - padding.x, cursor.y + padding.y); draw_list->AddText(stats_pos, ImGui::GetColorU32(ImGuiCol_TextDisabled), stats.c_str(), stats.c_str() + stats.size()); - if(item->protocol_version != protocol::VERSION) { - auto warning_size = ImGui::CalcTextSize(WARNING_TOAST.data(), WARNING_TOAST.data() + WARNING_TOAST.size()); - auto warning_pos = ImVec2(stats_pos.x - warning_size.x - padding.x - 4.0f * globals::gui_scale, cursor.y + padding.y); - auto warning_end = ImVec2(warning_pos.x + warning_size.x, warning_pos.y + warning_size.y); - draw_list->AddText(warning_pos, ImGui::GetColorU32(ImGuiCol_DragDropTarget), WARNING_TOAST.data(), - WARNING_TOAST.data() + WARNING_TOAST.size()); + auto major_version_mismatch = item->game_version_major != version::major; + auto minor_version_mismatch = item->game_version_minor != version::minor; + auto patch_version_mismatch = item->game_version_patch != version::patch; - if(ImGui::IsMouseHoveringRect(warning_pos, warning_end)) { - ImGui::BeginTooltip(); + ImU32 version_color; - if(item->protocol_version < protocol::VERSION) { - ImGui::TextUnformatted(str_outdated_server.c_str(), str_outdated_server.c_str() + str_outdated_server.size()); - } - else { - ImGui::TextUnformatted(str_outdated_client.c_str(), str_outdated_client.c_str() + str_outdated_client.size()); - } + if(major_version_mismatch || minor_version_mismatch || patch_version_mismatch) { + version_color = ImGui::GetColorU32(major_version_mismatch ? ImGuiCol_PlotLinesHovered : ImGuiCol_DragDropTarget); + } + else { + version_color = ImGui::GetColorU32(ImGuiCol_PlotHistogram); + } - ImGui::EndTooltip(); - } + ImGui::PushFont(globals::font_unscii8, 4.0f); + + std::string version_toast; + + if(item->game_version_major < 16U) { + // Pre v16.x.x servers didn't send minor and patch versions + // and also used a different versioning scheme; post v16 the + // major version became the protocol version and the semver lost the tweak part + version_toast = std::string("0.0.1"); + } + else { + version_toast = std::format("{}.{}.{}", item->game_version_major, item->game_version_minor, item->game_version_patch); } + + auto version_size = ImGui::CalcTextSize(version_toast.c_str(), version_toast.c_str() + version_toast.size()); + auto version_pos = ImVec2(stats_pos.x - version_size.x - padding.x - 4.0f * globals::gui_scale, + cursor.y + padding.y + 0.5f * (stats_size.y - version_size.y)); + auto version_end = ImVec2(version_pos.x + version_size.x, version_pos.y + version_size.y); + + auto outline_pos = ImVec2(version_pos.x - 2U * globals::gui_scale, version_pos.y - 2U * globals::gui_scale); + auto outline_end = ImVec2(version_end.x + 2U * globals::gui_scale, version_end.y + 2U * globals::gui_scale); + auto outline_thickness = math::max(1.0f, 0.5f * static_cast(globals::gui_scale)); + + draw_list->AddRect(outline_pos, outline_end, version_color, 0.0f, 0, outline_thickness); + draw_list->AddText(version_pos, version_color, version_toast.c_str(), version_toast.c_str() + version_toast.size()); + + ImGui::PopFont(); } ImU32 motd_color = {}; @@ -460,11 +483,13 @@ void gui::play_menu::init(void) auto item = new ServerStatusItem(); item->port = protocol::PORT; - item->protocol_version = protocol::VERSION; item->max_players = UINT16_MAX; item->num_players = UINT16_MAX; item->identity = next_identity; item->status = item_status::UNKNOWN; + item->game_version_major = version::major; + item->game_version_minor = version::minor; + item->game_version_patch = version::patch; next_identity += 1U; @@ -543,6 +568,11 @@ void gui::play_menu::layout(void) ImGui::EndTabItem(); } + if(ImGui::BeginTabItem("debug###play_menu.debug.child")) { + ImGui::ShowStyleEditor(); + ImGui::EndTabItem(); + } + ImGui::EndTabBar(); } diff --git a/game/client/gui/window_title.cc b/game/client/gui/window_title.cc index 6e2387c..2f96205 100644 --- a/game/client/gui/window_title.cc +++ b/game/client/gui/window_title.cc @@ -10,14 +10,5 @@ void gui::window_title::update(void) { - std::string title; - - if(globals::sound_ctx && globals::sound_dev) { - title = std::format("Voxelius {}: {}", version::semver, splash::get()); - } - else { - title = std::format("Voxelius {}: {} [NOSOUND]", version::semver, splash::get()); - } - - glfwSetWindowTitle(globals::window, title.c_str()); + glfwSetWindowTitle(globals::window, std::format("Voxelius {}: {}", version::semver, splash::get()).c_str()); } diff --git a/game/client/session.cc b/game/client/session.cc index 907b789..ce3d616 100644 --- a/game/client/session.cc +++ b/game/client/session.cc @@ -6,6 +6,8 @@ #include "core/math/crc64.hh" +#include "core/version.hh" + #include "shared/entity/head.hh" #include "shared/entity/player.hh" #include "shared/entity/transform.hh" @@ -284,11 +286,13 @@ void session::disconnect(std::string_view reason) void session::send_login_request(void) { protocol::LoginRequest packet; - packet.version = protocol::VERSION; + packet.game_version_major = version::major; packet.voxel_registry_checksum = world::voxel_registry::get_checksum(); packet.item_registry_checksum = world::item_registry::get_checksum(); packet.password_hash = server_password_hash; packet.username = client_game::username.get(); + packet.game_version_minor = version::minor; + packet.game_version_patch = version::patch; protocol::send(session::peer, protocol::encode(packet)); -- cgit