29 Commits

Author SHA1 Message Date
d6f304e6ca feat(entity): add client-to-server entity creation and destruction requests
Extend the network protocol with `C2SEntityCreateRequest` and `C2SEntityDestoryRequest` packets. Refactor entity managers to split public client-facing create/destroy methods that send requests over the network, from internal handlers that process received packets. Add utility functions for converting protobuf Vec3 to glm::vec3.
2026-07-30 15:53:09 +08:00
826c6600d0 feat(gameplay): add entity destruction support 2026-07-30 15:24:19 +08:00
b166bab4f3 feat(gameplay): implement entity system with concurrent task handling and fix model loading 2026-07-30 15:05:56 +08:00
efa49a98b7 refactor(gameplay): replace client thread with timer-based system and add entity manager 2026-07-29 18:17:15 +08:00
3a9ab6a14a feat(gameplay): add entity managers and refactor to ECS components 2026-07-29 18:06:04 +08:00
d15037e7a3 refactor(gameplay): separate player logic into manager and ECS components 2026-07-28 21:00:04 +08:00
0ff5820e6e refactor(gameplay): rename ClientPlayer to LocalPlayer 2026-07-28 18:09:43 +08:00
5e96c1573b build: add entt library 2026-07-28 18:03:51 +08:00
90273e87f2 refactor(gameplay): integrate model and hitbox ID system into Entity
- Introduce HitboxID, EntityID, and ModelID types for safer ID-based lookups
- Replace AABB struct with Hitbox (includes HitboxID)
- Convert ModelManager and HitboxManager to singletons with Handle structs
- Update Entity to store IDs for model and hitbox, removing direct references
- Reorganize creature model assets into subdirectories per entity type
- Add player model (player.glb) and collision data for pig
- Remove ModelManager dependency from App and Renderer
- Add namespace parsing utility for asset paths
- Mark sparse_vector::insert() with [[nodiscard]]
2026-07-28 14:28:22 +08:00
1e106d75a3 refactor(gameplay): extract physics and collision from ClientPlayer into PhysicalSystem
Move per-axis collision detection and move distance calculation to new
PhysicalSystem. Move SpeedSystem implementation from inline header to
separate .cpp file. Add const accessors to Entity. Replace inline AABB
helper with HitboxManager registration of player hitbox using new
PLAYER_SIZE constant. Remove obsolete members and functions from
ClientPlayer.
2026-07-28 11:43:10 +08:00
bd1a9557a5 refactor: move movement logic into SpeedSystem and Entity components 2026-07-28 10:54:13 +08:00
831569cec6 feat(player): split max speed into horizontal/vertical, set spawn pos 2026-07-28 10:10:01 +08:00
a5c1c7d4aa refactor(gameplay): extract Entity base class from ClientPlayer
Move position, walk pose, velocity, orientation, movement, and gravity
fields and their accessors into a new Entity base class. ClientPlayer now
inherits from Entity, removing duplicated members. Also update velocity
handling to use 3D vector per axis and adjust related logic.
2026-07-28 09:27:28 +08:00
802d2ecb5a refactor(gameplay): extract movement, gravity, orientation, and walk pose into structs 2026-07-28 08:53:51 +08:00
ab605f7590 feat(render): add model ID system with concurrent lookup and namespace-based loading
- Extract `ModelID` and `Model` struct to new `model.hpp`
- Replace `std::unordered_map` with `tbb::concurrent_hash_map` for thread safety
- Add `get_model(ModelID)`, `get_model_id`, `get_model_name` methods
- Parse model names in `namespace:name` format to construct asset paths
- Update `load_model` to accept `string_view` and use ID-based management
2026-07-27 21:03:31 +08:00
15d5af34b9 refactor(gameplay): replace entt components with SparseVector for player data
Refactor client world to use a custom SparseVector for player data instead of entt registry. Consolidate Transform, ViewAngles, and related structs into Position, Orientation, WalkPose. Introduce PlayerData and PlayerRenderData. Remove the unused move_system.cpp. Unify player render and shadow render into a single function.
2026-07-27 20:21:44 +08:00
6d0e60e241 build: remove entt library 2026-07-27 09:33:51 +08:00
7b530968af fix(gameplay): correct hitbox insertion in HitboxManager::load 2026-07-25 17:52:13 +08:00
930226cc47 feat(gameplay): implement entity movement system with collision detection
- Add Velocity and HitBoxes components to entity
- Introduce HitboxManager for loading per-entity collision AABBs from JSON
- Create MoveSystem with per-axis collision handling
- Move get_block_aabb to base World class and add virtual get_per_tick_time
- Remove static get_block_aabb from ClientWorld; use member m_per_tick_time for tick duration
2026-07-25 17:47:13 +08:00
f9bf2f4b89 refactor(gameplay): add base classes Chunk and World for shared logic 2026-07-25 15:47:46 +08:00
df3ac5b5db refactor(collision): convert AABB to center-half representation and migrate player data to ECS 2026-07-25 14:53:19 +08:00
2568eb7d18 refactor(render): unify model and player rendering with single shader pipeline 2026-07-25 13:29:52 +08:00
2e3b4f4f0f feat(render): add shadow pass for entity models 2026-07-25 11:57:00 +08:00
5951a5e81c feat(entity): add ECS-based entity rendering 2026-07-25 11:19:27 +08:00
849386a3bc build: add EnTT library 2026-07-25 10:29:04 +08:00
3e7ce71219 refactor(render): rename depth player shaders to depth model and clean up 2026-07-24 19:53:35 +08:00
94fed9a810 feat(render): add model loading and rendering pipeline 2026-07-24 18:35:09 +08:00
f4174724c6 build(deps): add assimp library as dependency 2026-07-24 10:59:05 +08:00
zhenyan121
def311c7dd feature: hud (#37)
* build: replace FetchContent with direct includes for glm

* build(deps): replace SOIL2 with stb for image and vorbis decoding

* build: replace FetchContent with direct includes for toml++

* refactor(texture): bump block texture size to 512 and disable items tab

* feat: add hotbar system with ItemStack and RowLayout

* fix(ui): restrict Ctrl key handling to typing mode

* feat(ui): add widget border support and highlight selected hotbar slot

* fix(ui): update borders on scale change and correct Image width

- Added update_border() calls in set_scale() for Button, ChatBox, Image, Label, and TextField.
- Fixed Image::width() to multiply by width instead of height.
- Added early return in Widget::update_border() if border is not supported.

* feat(ui): add border visual feedback on slider drag

* feat(ui): add ItemSlot widget and refactor hotbar to use it

* feat(world_scene): add inventory UI and pause type enum

* feat(ui): add item slot tooltip and make window size static

* fix(ui label): account for background offset in width/height

* refactor(ui): centralize item info label in inventory and add update

* feat(ui): add hotbar interaction in inventory UI

* fix(inventory-ui): correct row creation logic for first item

The loop starting at index 0 created a new row on the first iteration
(i % 10 == 0). Shift the loop to start at 1 and adjust modulus condition
to create rows correctly every 10 items.

* feat(block): add name_key for localized block names

Add `name_key` field to all block TOML definitions, enabling per-block localization. Update `BlockData` struct and `BlockManager` with `local_name()` method that returns a localized string via the translation system. Add corresponding entries to `en_US.json` and `zh_CN.json`. Modify inventory UI to use the localized name when displaying block info, and fix a bug where emptying a hotbar slot did not clear the item locally.

* fix(ui): change inventory item slot color from white to gray

* fix(ui): add missing include for std::array
2026-07-23 14:07:11 +08:00
748 changed files with 126477 additions and 1156 deletions

View File

@@ -39,6 +39,8 @@ add_subdirectory(third_party/imgui)
add_subdirectory(third_party/dr_libs)
add_subdirectory(third_party/stb)
add_executable(${PROJECT_NAME})
add_subdirectory(src)
@@ -130,14 +132,12 @@ target_link_libraries(${PROJECT_NAME}
PRIVATE
SDL3::SDL3
glad
glm::glm
OpenGL::GL
soil2
Freetype::Freetype
tomlplusplus::tomlplusplus
imgui
dr_libs
tbb
stb
protobuf::libprotobuf
absl::log
absl::check
@@ -148,6 +148,7 @@ target_link_libraries(${PROJECT_NAME}
OpenAL::OpenAL
harfbuzz::harfbuzz
Opus::Opus
assimp::assimp
$<$<PLATFORM_ID:Windows>:ws2_32>
)

View File

@@ -8,4 +8,5 @@ is_passable = true
is_transitional = false
is_transparent = true
name = 'air'
name_key = 'block.air'
roughness = 1.0

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = true
is_transparent = false
name = 'dirt'
name_key = 'block.dirt'
roughness = 1.0

View File

@@ -8,4 +8,5 @@ is_passable = true
is_transitional = false
is_transparent = true
name = 'grass'
roughness = 0.9
name_key = 'block.grass'
roughness = 0.90000000000000002

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = true
is_transparent = false
name = 'grass_block'
roughness = 0.9
name_key = 'block.grass_block'
roughness = 0.90000000000000002

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = false
is_transparent = true
name = 'leaf'
roughness = 0.7
name_key = 'block.leaf'
roughness = 0.69999999999999996

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = false
is_transparent = false
name = 'log'
roughness = 0.7
name_key = 'block.log'
roughness = 0.69999999999999996

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = true
is_transparent = false
name = 'sand'
roughness = 0.8
name_key = 'block.sand'
roughness = 0.80000000000000004

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = true
is_transparent = false
name = 'snowy_grass_block'
roughness = 0.9
name_key = 'block.snowy_grass_block'
roughness = 0.90000000000000002

View File

@@ -8,4 +8,5 @@ is_passable = false
is_transitional = true
is_transparent = false
name = 'stone'
name_key = 'block.stone'
roughness = 0.75

View File

@@ -8,4 +8,5 @@ is_transparent = false
is_discard = false
is_blend = false
is_transitional = false
roughness = 1.0
roughness = 1.0
name_key = "block.template"

View File

@@ -8,4 +8,5 @@ is_passable = true
is_transitional = false
is_transparent = true
name = 'water'
name_key = 'block.water'
roughness = 0.02

View File

@@ -35,5 +35,15 @@
"joingame.join_a_world": "Join A World",
"joingame.server_ip": "Server Ip",
"joingame.join_world": "Join World",
"error.disable_voice": "Server Disable Voice Chat"
"error.disable_voice": "Server Disable Voice Chat",
"block.air": "air",
"block.dirt": "dirt",
"block.grass_block": "grass block",
"block.grass": "grass",
"block.leaf": "leaf",
"block.log": "log",
"block.sand": "sand",
"block.snowy_grass_block": "snowy grass block",
"block.stone": "stone",
"block.water": "water"
}

View File

@@ -35,5 +35,15 @@
"joingame.join_a_world": "加入世界",
"joingame.server_ip": "服务器IP",
"joingame.join_world": "加入世界",
"error.disable_voice": "服务器已禁用语音聊天"
"error.disable_voice": "服务器已禁用语音聊天",
"block.air": "空气",
"block.dirt": "泥土",
"block.grass_block": "草方块",
"block.grass": "草",
"block.leaf": "树叶",
"block.log": "原木",
"block.sand": "沙子",
"block.snowy_grass_block": "覆雪草方块",
"block.stone": "石头",
"block.water": "水"
}

View File

@@ -0,0 +1,16 @@
{
"boxes": [
{
"center": [
0,
0.78,
0
],
"half": [
0.53,
0.78,
0.406
]
}
]
}

Binary file not shown.

Binary file not shown.

View File

@@ -7,7 +7,6 @@ uniform mat4 lightSpaceMatrix;
uniform mat4 modelMatrix;
out vec2 tc;
flat out int tex_layer;
void main() {
tc = texCoord;

View File

@@ -3,7 +3,6 @@
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 aNormal;
layout (location = 3) in vec3 aTangent;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 B

After

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 B

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 890 B

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 B

After

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 877 B

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 B

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 915 B

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 874 B

After

Width:  |  Height:  |  Size: 215 KiB

BIN
assets/texture/ui/slot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

View File

@@ -10,29 +10,8 @@ find_package(harfbuzz REQUIRED)
find_package(Freetype REQUIRED)
find_package(SDL3 REQUIRED)
find_package(Opus REQUIRED)
find_package(assimp REQUIRED)
# Third-party libraries
FetchContent_Declare(
glm
GIT_REPOSITORY https://github.com/g-truc/glm.git
GIT_TAG 1.0.3
)
FetchContent_MakeAvailable(glm)
FetchContent_Declare(
soil2
GIT_REPOSITORY https://github.com/SpartanJ/SOIL2.git
GIT_TAG 1.31
)
FetchContent_MakeAvailable(soil2)
FetchContent_Declare(
tomlplusplus
GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
GIT_TAG v3.4.0
)
FetchContent_MakeAvailable(tomlplusplus)
if (WIN32)

View File

@@ -1,20 +0,0 @@
#pragma once
#include <glm/glm.hpp>
namespace Cubed {
struct AABB {
glm::vec3 min{0.0f, 0.0f, 0.0f};
glm::vec3 max{0.0f, 0.0f, 0.0f};
AABB(glm::vec3 min_point, glm::vec3 max_point)
: min(min_point), max(max_point) {}
bool intersects(const AABB& other) const {
return (min.x <= other.max.x && max.x >= other.min.x) &&
(min.y <= other.max.y && max.y >= other.min.y) &&
(min.z <= other.max.z && max.z >= other.min.z);
}
};
} // namespace Cubed

View File

@@ -49,7 +49,6 @@ private:
Window m_window;
TextureManager m_texture_manager;
AudioEngine m_audio;
Renderer m_renderer;

View File

@@ -8,7 +8,7 @@
namespace Cubed {
class ClientPlayer;
class LocalPlayer;
class Camera {
private:
@@ -18,7 +18,7 @@ private:
THIRD_PERSON_FRONT,
};
ClientPlayer* m_player;
LocalPlayer* m_player;
float m_last_mouse_x, m_last_mouse_y;
glm::vec3 m_camera_pos;
bool m_under_water = false;
@@ -35,7 +35,7 @@ public:
void update_move_camera();
void camera_init(ClientPlayer* player);
void camera_init(LocalPlayer* player);
void hot_reload();
void update_cursor_position_camera(float offset_x, float offset_y);
@@ -47,7 +47,7 @@ public:
void change_perspective();
bool is_first_person() const;
bool handle_event(const Event& e);
ClientPlayer* player();
LocalPlayer* player();
};
} // namespace Cubed

View File

@@ -7,7 +7,7 @@
namespace Cubed {
class WorldScene;
class ClientPlayer;
class LocalPlayer;
class App;
class DevPanel {
struct ConfigView {
@@ -42,7 +42,7 @@ private:
WorldScene& m_world_scene;
Config& m_config;
ConfigView m_config_view;
ClientPlayer* m_player;
LocalPlayer* m_player;
PlayerProfile m_player_profile;
bool m_need_save_config = false;
bool m_gen_thread_running = true;
@@ -63,7 +63,7 @@ private:
void show_server_world_table_bar();
void show_client_world_table_bar();
void show_player_tab_item();
void show_items_tab_item();
// void show_items_tab_item();
void show_shader_tab_item();
void update_config_view();

View File

@@ -42,6 +42,7 @@ struct LookBlock {
struct BlockData {
std::string name;
std::string name_key;
BlockType id = 0;
bool is_liquid = false;
@@ -55,11 +56,11 @@ struct BlockData {
bool is_blend = false;
bool is_transitional = false;
float roughness = 1.0f;
BlockData(BlockType b_id, std::string_view b_name, bool liquid,
bool passable, bool cross_plane, bool transparent, bool gas,
bool discard, bool blend, bool transitional, float r)
: name(b_name), id(b_id), is_liquid(liquid), is_gas(gas),
is_passable(passable), is_cross_plane(cross_plane),
BlockData(std::string_view b_name, std::string_view name_k, BlockType b_id,
bool liquid, bool passable, bool cross_plane, bool transparent,
bool gas, bool discard, bool blend, bool transitional, float r)
: name(b_name), name_key(name_k), id(b_id), is_liquid(liquid),
is_gas(gas), is_passable(passable), is_cross_plane(cross_plane),
is_transparent(transparent), is_discard(discard), is_blend(blend),
is_transitional(transitional), roughness(r) {}
};
@@ -72,7 +73,7 @@ public:
static unsigned sums();
static unsigned cross_plane_sum();
static const std::string& name_form_id(BlockType id);
static std::string local_name(BlockType id);
static bool is_gas(BlockType id);
static bool is_liquid(BlockType id);

View File

@@ -0,0 +1,27 @@
#pragma once
#include "Cubed/gameplay/chunk_pos.hpp"
#include <glm/glm.hpp>
#include <tuple>
namespace Cubed {
class Chunk {
public:
Chunk() = default;
Chunk(const Chunk&) = delete;
Chunk(Chunk&&) = delete;
Chunk& operator=(const Chunk&) = delete;
Chunk& operator=(Chunk&&) = delete;
virtual ~Chunk() = default;
static int index(int x, int y, int z);
static int index(const glm::vec3& pos);
static std::tuple<int, int, int> world_to_block(int world_x, int world_y,
int world_z, int chunk_x,
int chunk_z);
static std::tuple<int, int, int> world_to_block(const glm::ivec3& block_pos,
ChunkPos chunk_pos);
static std::tuple<int, int, int> block_to_world(int x, int y, int z,
int chunk_x, int chunk_z);
static std::tuple<int, int, int> block_to_world(const glm::ivec3& block_pos,
ChunkPos chunk_pos);
};
} // namespace Cubed

View File

@@ -2,6 +2,7 @@
#include "Cubed/constants.hpp"
#include "Cubed/gameplay/biome.hpp"
#include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk.hpp"
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/vertex_data.hpp"
#include "world/chunk_data.pb.h"
@@ -26,7 +27,7 @@ struct ChunkRenderSnapshot {
glm::vec3 center;
glm::vec3 half_extents;
};
class ClientChunk {
class ClientChunk : public Chunk {
public:
ClientChunk(ClientWorld& world);
~ClientChunk();
@@ -35,17 +36,6 @@ public:
ClientChunk(ClientChunk&&) noexcept;
ClientChunk& operator=(ClientChunk&&) noexcept;
static int index(int x, int y, int z);
static int index(const glm::vec3& pos);
static std::tuple<int, int, int> world_to_block(int world_x, int world_y,
int world_z, int chunk_x,
int chunk_z);
static std::tuple<int, int, int> world_to_block(const glm::ivec3& block_pos,
ChunkPos chunk_pos);
static std::tuple<int, int, int> block_to_world(int x, int y, int z,
int chunk_x, int chunk_z);
static std::tuple<int, int, int> block_to_world(const glm::ivec3& block_pos,
ChunkPos chunk_pos);
BiomeType get_biome() const;
ChunkPos get_chunk_pos() const;
const std::vector<BlockType>& get_chunk_blocks() const;

View File

@@ -0,0 +1,68 @@
#pragma once
#include "Cubed/gameplay/ecs/entity.hpp"
#include "glm/ext/vector_float3.hpp"
#include "world/entity.pb.h"
#include <entt/entt.hpp>
#include <tbb/concurrent_hash_map.h>
#include <tbb/concurrent_queue.h>
namespace Cubed {
class ClientWorld;
class ClientEntityManager {
public:
enum class Command { CREATE, DESTORY };
ClientEntityManager(ClientWorld& world);
void update();
void init();
void receive_entity_create(S2CEntityCreate& s2c);
void receive_entity_destory(EntityID id);
void destory(EntityID id);
void create(std::string_view name, const glm::vec3& pos);
const entt::registry& get_registry() const;
private:
struct EntityCreateElement {
EntityID id;
std::string name;
glm::vec3 pos;
};
using EntityMap = tbb::concurrent_hash_map<EntityID, entt::entity>;
using acc = EntityMap::accessor;
using cacc = EntityMap::const_accessor;
using CreateFunc = std::function<void(EntityID id)>;
using TaskElement = std::variant<EntityCreateElement, EntityID>;
using TaskPair = std::pair<Command, TaskElement>;
ClientWorld& m_world;
entt::registry m_registry;
EntityMap m_entities;
std::unordered_map<std::string_view, CreateFunc> m_factories;
tbb::concurrent_queue<TaskPair> m_tasks;
void handle_task();
void handle_entity_destory(EntityID id);
// not thread safe
void handle_entity_create(EntityID id, std::string_view name,
const glm::vec3& pos);
template <typename... Args>
void create_entity_in_registry(EntityID id, Args&&... args) {
{
cacc a;
if (m_entities.find(a, id)) {
return;
}
}
auto entity = m_registry.create();
((m_registry.emplace<std::remove_cvref_t<Args>>(
entity, std::forward<Args>(args))),
...);
m_entities.emplace(id, entity);
return;
}
};
} // namespace Cubed

View File

@@ -1,161 +1,25 @@
#pragma once
#include "Cubed/AABB.hpp"
#include "Cubed/constants.hpp"
#include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/game_mode.hpp"
#include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/player.hpp"
#include "Cubed/input/event.hpp"
#include "Cubed/input/input.hpp"
#include <absl/container/flat_hash_set.h>
#include <glm/glm.hpp>
#include <optional>
#include <shared_mutex>
#include "Cubed/gameplay/ecs/animation.hpp"
#include "Cubed/gameplay/ecs/identity.hpp"
#include "Cubed/gameplay/ecs/transform.hpp"
#include "Cubed/gameplay/player.hpp"
namespace Cubed {
class ClientWorld;
class ClientPlayer {
public:
static constexpr float WALK_SOUND_INTERVAL = 0.45f;
static constexpr float RUN_SOUND_INTERVAL = 0.3f;
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
ClientPlayer(ClientWorld& world);
~ClientPlayer();
bool handle_mouse_button_event(const MouseButtonEvent& e);
bool handle_key_event(const KeyEvent& e);
bool handle_mouse_wheel_event(const MouseWheelEvent& e);
void update_front_vec(float offset_x, float offset_y);
bool update_player_move_state(Key key, KeyAction action);
bool update_scroll(float yoffset);
void update_chunk_set(const ChunkPosSet& set);
const ChunkPosSet& get_chunk_pos_set() const;
ChunkPosSet get_chunk_pos_set();
static AABB get_aabb(const glm::vec3& pos);
const glm::vec3& get_front() const;
Gait get_gait() const;
const std::optional<LookBlock>& get_look_block_pos() const;
// thread safe
glm::vec3 get_player_pos() const;
const MoveState& get_move_state() const;
void change_mode(GameMode mode);
void reload_config();
void set_player_pos(const glm::vec3& pos);
void set_place_block(unsigned id);
void update(float delta_time);
float& max_walk_speed();
float& max_run_speed();
float& max_speed();
float& acceleration();
float& deceleration();
float& g();
float& fly_y_speed();
unsigned get_current_block() const;
void set_gait(Gait gait);
GameMode& game_mode();
ClientWorld& get_world();
void set_uuid(std::string_view uuid);
std::string get_uuid() const;
const std::string& get_name() const;
void reset_key_status();
void init(std::string_view name);
float yaw() const;
float pitch() const;
float& angle();
float& walk_time();
bool ray_cast(const glm::vec3& start, const glm::vec3& dir,
glm::ivec3& block_pos, glm::vec3& normal,
float distance = 4.0f);
bool is_underwater() const;
void set_underwater(bool u);
void place_block(float dt);
private:
using enum GameMode;
float m_max_walk_speed = DEFAULT_MAX_WALK_SPEED;
float m_max_run_speed = DEFAULT_MAX_RUN_SPEED;
float m_acceleration = DEFAULT_ACCELERATION;
float m_deceleration = DEFAULT_DECELERATION;
float m_g = DEFAULT_G;
constexpr static float MAX_SPACE_ON_TIME = 0.3f;
constexpr static float PLACE_BLOCK_INTERVAL = 0.2f;
float m_place_time = PLACE_BLOCK_INTERVAL;
std::atomic<float> m_yaw = 0.0f;
std::atomic<float> m_pitch = 0.0f;
float m_sensitivity = 0.15f;
float m_max_speed = m_max_walk_speed;
float m_y_speed = 0.0f;
float m_fly_y_speed = 7.5f;
bool can_up = true;
float space_on_time = 0.0f;
bool space_on = false;
bool is_fly = false;
float m_xz_speed = 0.0f;
unsigned m_place_block = 1;
bool m_moving = false;
bool m_sprinting = false;
bool m_underwater = false;
glm::vec3 direction = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 move_distance{0.0f, 0.0f, 0.0f};
// player is tow block tall, the pos is the lower pos
glm::vec3 m_player_pos{0.0f, 255.0f, 0.0f};
ChunkPos m_last_chunk_pos{0, 0};
glm::vec3 m_front{0, 0, -1};
glm::vec3 m_right{0, 0, 0};
static constexpr glm::vec3 M_SIZE{0.6f, 1.8f, 0.6f};
std::atomic<Gait> m_gait = Gait::STOP;
MoveState m_move_state{};
MouseState m_mouse_state{};
GameMode m_game_mode = CREATIVE;
std::optional<LookBlock> m_look_block = std::nullopt;
std::string m_name{};
mutable std::shared_mutex m_uuid_mutex;
std::string m_uuid;
ClientWorld& m_world;
float m_angle{0.0f};
float m_walk_time{0.0f};
std::unordered_map<std::string, Timer> m_timers;
mutable std::shared_mutex m_player_pos_mutex;
mutable std::shared_mutex m_chunk_pos_mutex;
ChunkPosSet m_player_chunk_pos_set;
void update_direction();
void update_lookup_block();
void update_move(float delta_time);
void update_x_move(glm::vec3& player_pos);
void update_y_move(glm::vec3& player_pos);
void update_z_move(glm::vec3& player_pos);
void update_player_chunk();
void play_walk_sound(float dt);
Gait compute_gait() const;
struct ClientPlayer {
Position pos{};
Position render_pos{};
EntityInfo entity{};
WalkPose walk{};
Orientation angle{};
Orientation render_angle{};
};
} // namespace Cubed
struct PlayerRenderData {
EntityInfo info{};
Position render_pos{};
Orientation angle{};
Gait gait{};
};
} // namespace Cubed

View File

@@ -0,0 +1,45 @@
#pragma once
#include "Cubed/gameplay/client_player.hpp"
#include "Cubed/gameplay/local_player.hpp"
#include "Cubed/gameplay/network_client.hpp"
#include "Cubed/tools/sparse_vector.hpp"
namespace Cubed {
class ClientWorld;
class ClientPlayerManager {
public:
ClientPlayerManager(const ClientPlayerManager&) = delete;
ClientPlayerManager(ClientPlayerManager&&) = delete;
ClientPlayerManager& operator=(const ClientPlayerManager&) = delete;
ClientPlayerManager& operator=(ClientPlayerManager&&) = delete;
ClientPlayerManager(ClientWorld& world);
~ClientPlayerManager();
void init(std::string_view local_name);
void update(float dt);
std::span<PlayerRenderData> render_player_data();
bool has_player(const Hitbox& hitbox) const;
LocalPlayer& get_local();
const LocalPlayer& get_local() const;
void receive_remote_player(const PlayerInfoRsp& rsp);
void receive_player_logout(const LogoutRsp& rsp);
void reload_config();
void report_player_info(NetworkClient* client);
private:
ClientWorld& m_world;
mutable std::shared_mutex m_players_mutex;
using PlayerHandle = SparseVector<ClientPlayer>::Handle;
SparseVector<ClientPlayer> m_players;
std::vector<PlayerRenderData> m_render_data;
std::unordered_map<std::string, PlayerHandle> m_players_handle;
LocalPlayer m_local;
void update_players_data(float dt);
};
} // namespace Cubed

View File

@@ -5,9 +5,12 @@
#include "Cubed/gameplay/chat_message.hpp"
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/client_chunk.hpp"
#include "Cubed/gameplay/client_player.hpp"
#include "Cubed/gameplay/client_entity_manager.hpp"
#include "Cubed/gameplay/client_player_manager.hpp"
#include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/local_player.hpp"
#include "Cubed/gameplay/network_client.hpp"
#include "Cubed/gameplay/world.hpp"
#include "Cubed/input/event.hpp"
#include "Cubed/tools/cubed_random.hpp"
#include "Cubed/tools/priority_thread_pool.hpp"
@@ -19,46 +22,26 @@
#include <tbb/concurrent_unordered_map.h>
namespace Cubed {
struct PlayerInfo {
std::string name;
std::string uuid;
glm::vec3 render_pos;
glm::vec3 target_pos;
float render_yaw;
float yaw;
float render_pitch;
float pitch;
Gait gait;
float angle = 0.0f;
float walk_time = 0.0f;
float moving_time = 0.0f;
};
struct PlayerRenderData {
std::string name;
std::string uuid;
glm::vec3 render_pos;
float yaw;
float pitch;
Gait gait;
float angle;
};
class WorldScene;
class ClientWorld {
class ClientWorld : public World {
public:
ClientWorld(const ClientWorld&) = delete;
ClientWorld(ClientWorld&&) = delete;
ClientWorld& operator=(const ClientWorld&) = delete;
ClientWorld& operator=(ClientWorld&&) = delete;
ClientWorld(AudioEngine& auido, Config& config, WorldScene& scene);
~ClientWorld();
void init(std::string_view player_name,
std::shared_ptr<NetworkClient> client);
void update(float delta_time);
void update(float dt);
bool handle_event(const Event& e);
const std::optional<LookBlock>& get_look_block_pos() const;
ClientPlayer& get_player();
const ClientPlayer& get_player() const;
int get_block(const glm::ivec3& block_pos) const;
bool is_solid(const glm::ivec3& block_pos) const;
bool can_pass_block(const glm::ivec3& block_pos) const;
BlockType get_block_tpye(const glm::ivec3& block_pos) const;
LocalPlayer& get_player();
const LocalPlayer& get_player() const;
int get_block(const glm::ivec3& block_pos) const override;
bool is_solid(const glm::ivec3& block_pos) const override;
bool can_pass_block(const glm::ivec3& block_pos) const override;
BlockType get_block_tpye(const glm::ivec3& block_pos) const override;
void rebuild_world();
@@ -71,7 +54,6 @@ public:
void receive_block_change(const BlockChangeRsp& rsp);
void receive_time(const UpdateTime& rsp);
void receive_remote_player(const PlayerInfoRsp& rsp);
void receive_player_logout(const LogoutRsp& rsp);
void receive_player_water_sound(const PlayerWaterSound& rsp);
void send_player_water_sound(bool underwater, const glm::vec3& pos);
@@ -90,8 +72,6 @@ public:
void reset_key_status();
std::vector<glm::vec4>& planes();
const std::vector<const ChunkRenderSnapshot*>& render_snapshots() const;
const std::vector<PlayerRenderData>& render_player_data() const;
std::vector<PlayerRenderData>& render_player_data();
glm::vec3 sunlight_dir() const;
bool sphere_collide_world(glm::vec3 center, float radius) const;
@@ -99,22 +79,26 @@ public:
void request_exit();
bool is_receive_exit();
int chunk_size() const;
static AABB get_block_aabb(const glm::ivec3& pos);
AudioEngine& get_audio();
const AudioEngine& get_audio() const;
Config& get_config();
WorldScene& world_scene();
ClientPlayerManager& player_manager();
ClientEntityManager& entity_manager();
std::shared_ptr<NetworkClient> get_client() const;
void set_direct_exit();
void receive_chat_message(ChatMsg& msg);
void send_chat_message(ChatMessage& message);
void receive_voice_message(VoiceMsg& msg);
bool enable_voice_chat() const;
int get_per_tick_time() const override;
template <typename Fn>
void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) {
m_ticktimers.emplace(
std::piecewise_construct, std::forward_as_tuple(std::string(id)),
std::forward_as_tuple(threshold, std::forward<Fn>(f)));
void register_timer(std::string_view id, float threshold, Fn&& f) {
m_timers.emplace(std::piecewise_construct,
std::forward_as_tuple(std::string(id)),
std::forward_as_tuple(threshold, std::forward<Fn>(f)));
}
private:
@@ -135,7 +119,6 @@ private:
ChunkPos::TBBHash>;
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
using ChunkPosVector = std::vector<ChunkPos>;
using OtherPlayerHashMap = std::unordered_map<std::string, PlayerInfo>;
using chunk_acc = ChunkHashMap::accessor;
using chunk_cacc = ChunkHashMap::const_accessor;
@@ -146,16 +129,13 @@ private:
static constexpr int WORLD_EXIT_TIMEOUT = 200;
static constexpr int MAX_UPLOAD_CHUNK_SUM = 16;
ClientPlayer m_player;
OtherPlayerHashMap m_player_info;
ClientEntityManager m_entity_manager;
ClientPlayerManager m_player_manager;
ChunkHashMap m_chunks;
AudioEngine& m_audio;
Config& m_config;
WorldScene& m_world_scene;
std::vector<glm::vec4> m_planes;
std::jthread m_client_thread;
mutable std::shared_mutex m_player_info_mutex;
tbb::concurrent_queue<std::unique_ptr<ClientChunk>> m_pending_upload_queue;
tbb::concurrent_queue<ChunkPos> m_dirty_chunk_queue;
@@ -165,9 +145,7 @@ private:
std::deque<ChunkPos> m_dirty_queue;
std::vector<const ChunkRenderSnapshot*> m_render_snapshots;
std::vector<PlayerRenderData> m_render_player_data;
tbb::concurrent_unordered_map<std::string, TickTimer> m_ticktimers;
std::unordered_map<std::string, Timer> m_timers;
std::atomic<bool> m_exit_direct{false};
std::atomic<bool> m_game_running{false};
@@ -175,6 +153,7 @@ private:
std::atomic<int> m_rendering_distance{24};
std::atomic<TickType> m_game_ticks{0};
std::atomic<TickType> m_day_tick{6000};
std::atomic<int> m_per_tick_time = DEFAULT_PER_TICK_TIME;
std::atomic<bool> m_requesting_chunk{false};
std::atomic<bool> m_is_rebuilding{false};
std::atomic<int> m_chunk_task_id{0};
@@ -186,10 +165,6 @@ private:
Random m_random;
void client_run(std::stop_token token);
void report_player_info();
void set_block(const glm::ivec3& pos, unsigned id);
void update_chunk(const ChunkPosSet& old, const ChunkPosSet& now);

View File

@@ -0,0 +1,6 @@
#pragma once
namespace Cubed {
struct PigTag {};
} // namespace Cubed

View File

@@ -0,0 +1,13 @@
#pragma once
#include "Cubed/gameplay/player.hpp"
namespace Cubed {
struct WalkPose {
Gait gait = Gait::STOP;
// for arm roll caculate
float walk_time = 0.0f;
// for sound play
float moving_time = 0.0f;
};
} // namespace Cubed

View File

@@ -0,0 +1,14 @@
#pragma once
#include "Cubed/gameplay/ecs/animation.hpp"
#include "Cubed/gameplay/ecs/transform.hpp"
#include "Cubed/gameplay/model.hpp"
namespace Cubed {
struct BaseClientCreature {
Transform transform;
WalkPose pose;
ModelID model;
};
} // namespace Cubed

View File

@@ -0,0 +1,11 @@
#pragma once
#include <cstdint>
namespace Cubed {
using EntityID = uint64_t;
struct Entity {
EntityID id;
explicit Entity(EntityID id) : id(id) {}
};
} // namespace Cubed

View File

@@ -0,0 +1,10 @@
#pragma once
namespace Cubed {
struct Health {
float hp = 20;
float max_hp = 20;
};
} // namespace Cubed

View File

@@ -0,0 +1,10 @@
#pragma once
#include <string>
namespace Cubed {
struct EntityInfo {
std::string name;
std::string uuid;
};
} // namespace Cubed

View File

@@ -0,0 +1,26 @@
#pragma once
#include "Cubed/constants.hpp"
#include <glm/glm.hpp>
namespace Cubed {
struct Velocity {
glm::vec3 value{0.0f};
glm::vec3 max{4.5f, 7.5f, 7.5f};
};
struct Movement {
float acceleration = DEFAULT_ACCELERATION;
float deceleration = DEFAULT_DECELERATION;
float jump_power = 7.5f;
};
struct Gravity {
float value = DEFAULT_G;
};
} // namespace Cubed

View File

@@ -0,0 +1,27 @@
#pragma once
#include "Cubed/gameplay/ecs/health.hpp"
#include "Cubed/gameplay/ecs/movement.hpp"
#include "Cubed/gameplay/ecs/state.hpp"
#include "Cubed/gameplay/ecs/transform.hpp"
#include "Cubed/gameplay/hitbox.hpp"
namespace Cubed {
struct BaseServerCreature {
Transform transform{};
Velocity velocity{};
Movement movement{};
MoveState move_state{};
Direction direction{};
Gravity gravity{};
Health health{};
HitboxID hitbox{};
};
} // namespace Cubed

View File

@@ -0,0 +1,18 @@
#pragma once
namespace Cubed {
struct MoveState {
bool forward = false;
bool back = false;
bool left = false;
bool right = false;
bool down = false;
bool up = false;
bool is_fly = false;
bool can_up = true;
};
} // namespace Cubed

View File

@@ -0,0 +1,24 @@
#pragma once
#include <glm/glm.hpp>
namespace Cubed {
struct Position {
glm::vec3 value{0.0f};
};
struct Orientation {
float yaw = 0.0f;
float pitch = 0.0f;
float roll = 0.0f;
};
struct Direction {
glm::vec3 value{0.0f};
};
struct Transform {
Position position{};
Orientation orientation{};
Direction direction{};
};
} // namespace Cubed

View File

@@ -0,0 +1,26 @@
#pragma once
#include <glm/glm.hpp>
namespace Cubed {
using HitboxID = uint32_t;
struct Hitbox {
glm::vec3 center{0.0f};
glm::vec3 half{0.0f};
Hitbox(glm::vec3 center_point, glm::vec3 half_size)
: center(center_point), half(half_size) {}
glm::vec3 min() const { return center - half; }
glm::vec3 max() const { return center + half; }
bool intersects(const Hitbox& other) const {
return (glm::abs(center.x - other.center.x) <= half.x + other.half.x) &&
(glm::abs(center.y - other.center.y) <= half.y + other.half.y) &&
(glm::abs(center.z - other.center.z) <= half.z + other.half.z);
}
};
} // namespace Cubed

View File

@@ -0,0 +1,37 @@
#pragma once
#include "Cubed/gameplay/hitbox.hpp"
#include <tbb/concurrent_hash_map.h>
namespace Cubed {
class HitboxManager {
public:
struct Handle {
Hitbox box;
HitboxID id = 0;
};
HitboxManager();
~HitboxManager();
static HitboxManager& instance();
[[nodiscard]]
Handle get_hitbox(const std::string& key);
[[nodiscard]]
Handle get_hitbox(HitboxID id);
[[nodiscard]]
static Handle hitbox(const std::string& name);
[[nodiscard]]
static Handle hitbox(HitboxID id);
HitboxID get_hitbox_id(const std::string& name);
const std::string& get_hitbox_name(HitboxID id);
private:
using HitboxMap = tbb::concurrent_hash_map<HitboxID, Hitbox>;
using IDMap = tbb::concurrent_hash_map<std::string, HitboxID>;
using NameMap = tbb::concurrent_hash_map<HitboxID, std::string>;
HitboxID m_next = 0;
IDMap m_id_map;
NameMap m_name_map;
HitboxMap m_hitboxes;
Handle load(std::string_view name);
};
} // namespace Cubed

View File

@@ -0,0 +1,8 @@
#pragma once
#include "Cubed/gameplay/block.hpp"
namespace Cubed {
struct ItemStack {
BlockType type = 0;
size_t sum = 0;
};
} // namespace Cubed

View File

@@ -0,0 +1,160 @@
#pragma once
#include "Cubed/constants.hpp"
#include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/ecs/animation.hpp"
#include "Cubed/gameplay/ecs/identity.hpp"
#include "Cubed/gameplay/ecs/movement.hpp"
#include "Cubed/gameplay/ecs/state.hpp"
#include "Cubed/gameplay/ecs/transform.hpp"
#include "Cubed/gameplay/game_mode.hpp"
#include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/hitbox.hpp"
#include "Cubed/gameplay/item_stack.hpp"
#include "Cubed/gameplay/player.hpp"
#include "Cubed/input/event.hpp"
#include "Cubed/input/input.hpp"
#include <absl/container/flat_hash_set.h>
#include <glm/glm.hpp>
#include <optional>
#include <shared_mutex>
namespace Cubed {
class ClientWorld;
class LocalPlayer {
public:
static constexpr size_t HOTBAR_SUM = 10;
static constexpr float WALK_SOUND_INTERVAL = 0.45f;
static constexpr float RUN_SOUND_INTERVAL = 0.3f;
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
LocalPlayer(ClientWorld& world);
~LocalPlayer();
bool handle_mouse_button_event(const MouseButtonEvent& e);
bool handle_key_event(const KeyEvent& e);
bool handle_mouse_wheel_event(const MouseWheelEvent& e);
void update_front_vec(float offset_x, float offset_y);
bool update_player_move_state(Key key, KeyAction action);
bool update_scroll(float yoffset);
void update_chunk_set(const ChunkPosSet& set);
const ChunkPosSet& get_chunk_pos_set() const;
ChunkPosSet get_chunk_pos_set();
const glm::vec3& get_front() const;
const std::optional<LookBlock>& get_look_block_pos() const;
// thread safe
glm::vec3 get_player_pos() const;
const MoveState& get_move_state() const;
void change_mode(GameMode mode);
void reload_config();
void set_player_pos(const glm::vec3& pos);
void update(float delta_time);
float& max_walk_speed();
float& max_run_speed();
float& fly_y_speed();
const ItemStack& get_current_itemstack() const;
GameMode& game_mode();
ClientWorld& get_world();
void set_uuid(std::string_view uuid);
std::string get_uuid() const;
const std::string& get_name() const;
void reset_input_status();
void init(std::string_view name);
bool ray_cast(const glm::vec3& start, const glm::vec3& dir,
glm::ivec3& block_pos, glm::vec3& normal,
float distance = 4.0f);
bool is_underwater() const;
void set_underwater(bool u);
void place_block(float dt);
int selected_hotbar() const;
void set_hotbar(int pos, const ItemStack& item);
std::span<const ItemStack, HOTBAR_SUM> get_hotbar() const;
glm::vec3& max_speed();
float& acceleration();
float& deceleration();
float& g();
void set_gait(Gait gait);
float yaw() const;
float pitch() const;
float& roll();
float& walk_time();
Gait get_gait() const;
private:
using enum GameMode;
float m_max_walk_speed = DEFAULT_MAX_WALK_SPEED;
float m_max_run_speed = DEFAULT_MAX_RUN_SPEED;
float m_max_y_speed = 7.5f;
static constexpr float MAX_SPACE_ON_TIME = 0.3f;
static constexpr float PLACE_BLOCK_INTERVAL = 0.2f;
EntityInfo m_info;
Position m_pos;
WalkPose m_walk_pose;
Velocity m_velocity;
Orientation m_angle;
Movement m_movement;
Gravity m_gravity;
MoveState m_move_state;
Direction m_direction;
HitboxID m_hitbox = 0;
float m_place_time = PLACE_BLOCK_INTERVAL;
std::array<ItemStack, HOTBAR_SUM> m_hotbar;
float m_sensitivity = 0.15f;
float space_on_time = 0.0f;
bool space_on = false;
int m_selected_hotbar = 0;
bool m_moving = false;
bool m_sprinting = false;
bool m_underwater = false;
// player is tow block tall, the pos is the lower pos
ChunkPos m_last_chunk_pos{0, 0};
glm::vec3 m_front{0, 0, -1};
glm::vec3 m_right{0, 0, 0};
MouseState m_mouse_state{};
GameMode m_game_mode = CREATIVE;
std::optional<LookBlock> m_look_block = std::nullopt;
std::string m_name{};
mutable std::shared_mutex m_uuid_mutex;
std::string m_uuid;
ClientWorld& m_world;
std::unordered_map<std::string, Timer> m_timers;
mutable std::shared_mutex m_player_pos_mutex;
mutable std::shared_mutex m_chunk_pos_mutex;
ChunkPosSet m_player_chunk_pos_set;
void update_direction();
void update_lookup_block();
void update_move(float delta_time);
void update_player_chunk();
void play_walk_sound(float dt);
Gait compute_gait() const;
};
} // namespace Cubed

View File

@@ -0,0 +1,7 @@
#pragma once
#include <cstdint>
using ModelID = uint32_t;
struct Model {
ModelID id;
};

View File

@@ -61,6 +61,10 @@ enum class PacketEnum : uint16_t {
BLOCK_CHANGE_RSP = 3004,
S2C_CLEAR_ALL_CHUNKS = 3005,
UPDATE_TIME = 3006,
S2C_ENTITY_CREATE = 3007,
S2C_ENTITY_DESTORY = 3008,
C2S_ENTITY_CREATE_REQUEST = 3009,
C2S_ENTITY_DESTORY_REQUEST = 3010,
CHAT_MSG = 4001,
VOICE_MSG = 4002,
PING = 9001,
@@ -111,6 +115,18 @@ template <> constexpr uint16_t get_packet_id<BlockChangeRsp>() {
template <> constexpr uint16_t get_packet_id<S2C_ClearAllChunks>() {
return std::to_underlying(PacketEnum::S2C_CLEAR_ALL_CHUNKS);
}
template <> constexpr uint16_t get_packet_id<S2CEntityCreate>() {
return std::to_underlying(PacketEnum::S2C_ENTITY_CREATE);
}
template <> constexpr uint16_t get_packet_id<S2CEntityDestory>() {
return std::to_underlying(PacketEnum::S2C_ENTITY_DESTORY);
}
template <> constexpr uint16_t get_packet_id<C2SEntityCreateRequest>() {
return std::to_underlying(PacketEnum::C2S_ENTITY_CREATE_REQUEST);
}
template <> constexpr uint16_t get_packet_id<C2SEntityDestoryRequest>() {
return std::to_underlying(PacketEnum::C2S_ENTITY_DESTORY_REQUEST);
}
template <> constexpr uint16_t get_packet_id<UpdateTime>() {
return std::to_underlying(PacketEnum::UPDATE_TIME);
}
@@ -180,6 +196,12 @@ Packet make_packet(const T& msg) {
return packet;
}
template <typename T>
requires std::derived_from<T, google::protobuf::Message>
Packet make_packet(const T* msg) {
return make_packet(*msg);
}
inline PacketHeader decode_packet_header(std::span<const uint8_t> header) {
if (header.size() < HEADER_LEN)
throw std::runtime_error("Invalid header");

View File

@@ -1,4 +1,6 @@
#pragma once
#include "glm/ext/vector_float3.hpp"
#include <stdexcept>
#include <utility>
namespace Cubed {
@@ -17,5 +19,5 @@ inline Gait get_gait_from_id(int id) {
throw std::runtime_error("Unknown Gait");
}
}
static constexpr glm::vec3 PLAYER_SIZE{0.6f, 1.8f, 0.6f};
} // namespace Cubed

View File

@@ -2,6 +2,7 @@
#include "Cubed/constants.hpp"
#include "Cubed/gameplay/biome.hpp"
#include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk.hpp"
#include "Cubed/gameplay/chunk_generator.hpp"
#include "Cubed/gameplay/chunk_pos.hpp"
@@ -11,7 +12,7 @@
#include <tuple>
namespace Cubed {
class ServerWorld;
class ServerChunk {
class ServerChunk : public Chunk {
public:
ServerChunk(ServerWorld& world, ChunkPos chunk_pos,
bool temp_chunk = false);

View File

@@ -0,0 +1,59 @@
#pragma once
#include "Cubed/gameplay/ecs/entity.hpp"
#include "glm/ext/vector_float3.hpp"
#include <entt/entt.hpp>
#include <tbb/concurrent_hash_map.h>
#include <tbb/concurrent_queue.h>
namespace Cubed {
class ServerWorld;
class Session;
class ServerEntityManager {
public:
ServerEntityManager(ServerWorld& world);
void init();
void update();
// not thread safe
void add_entity(std::string_view name, const glm::vec3& pos);
void destory(EntityID id);
void handle_player_login(std::shared_ptr<Session> session);
private:
enum class Command { CREATE, SEND_ALL_ENTITIES, DESTORY };
struct EntityCreateElement {
std::string name;
glm::vec3 pos;
};
using EntityMap = tbb::concurrent_hash_map<EntityID, entt::entity>;
using acc = EntityMap::accessor;
using cacc = EntityMap::const_accessor;
using CreateFunc = std::function<EntityID()>;
using TaskElement =
std::variant<std::shared_ptr<Session>, EntityCreateElement, EntityID>;
using TaskPair = std::pair<Command, TaskElement>;
ServerWorld& m_world;
tbb::concurrent_queue<TaskPair> m_tasks;
entt::registry m_registry;
EntityID m_next = 0;
EntityMap m_entities;
std::unordered_map<std::string_view, CreateFunc> m_factories;
void create_entity(std::string_view name, const glm::vec3& pos);
void handle_entity_create(EntityID id, std::string_view name,
const glm::vec3& pos);
void handle_entity_destory(EntityID id);
void handle_task();
void send_all_entities(std::shared_ptr<Session>& session);
template <typename... Args>
EntityID create_entity_in_factory(Args&&... args) {
auto entity = m_registry.create();
((m_registry.emplace<std::remove_cvref_t<Args>>(
entity, std::forward<Args>(args))),
...);
auto id = m_next++;
m_entities.emplace(id, entity);
return id;
}
};
} // namespace Cubed

View File

@@ -7,7 +7,9 @@
#include "Cubed/gameplay/packet.hpp" // IWYU pragma: keep
#include "Cubed/gameplay/river_worm.hpp"
#include "Cubed/gameplay/server_chunk.hpp"
#include "Cubed/gameplay/server_entity_manager.hpp"
#include "Cubed/gameplay/server_player.hpp"
#include "Cubed/gameplay/world.hpp"
#include "Cubed/tools/priority_thread_pool.hpp"
#include "Cubed/tools/recent_queue.hpp"
#include "Cubed/tools/sensitive_filter.hpp"
@@ -25,7 +27,7 @@
#include <vector>
namespace Cubed {
class Session;
class ServerWorld {
class ServerWorld : public World {
public:
enum class ThreadPoolKind { NET, GEN };
ServerWorld(Config& config);
@@ -84,7 +86,19 @@ public:
void handle_chat_message(ChatMsg& msg);
void handle_voice_message(VoiceMsg& msg);
void handle_entity_create(C2SEntityCreateRequest& req);
void handle_entity_destory(C2SEntityDestoryRequest& req);
int chunk_size() const;
std::vector<std::shared_ptr<Session>> get_all_session() const;
int get_block(const glm::ivec3& block_pos) const override;
bool is_solid(const glm::ivec3& block_pos) const override;
bool can_pass_block(const glm::ivec3& block_pos) const override;
BlockType get_block_tpye(const glm::ivec3& block_pos) const override;
int get_per_tick_time() const override;
template <typename Fn>
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
m_timers.emplace(std::piecewise_construct,
@@ -119,13 +133,13 @@ private:
using PlayerUUIDMap = tbb::concurrent_hash_map<std::string, std::string>;
using chunk_acc = ChunkHashMap::accessor;
using chunk_caac = ChunkHashMap::const_accessor;
using chunk_cacc = ChunkHashMap::const_accessor;
using uuid_acc = PlayerUUIDMap::accessor;
using uuid_cacc = PlayerUUIDMap::const_accessor;
Config& m_config;
ServerEntityManager m_entity_manager;
// key = uuid
PlayerHashMap m_players;
ChunkHashMap m_chunks;
@@ -155,7 +169,7 @@ private:
std::atomic<bool> m_tick_running{true};
std::atomic<int> m_per_tick_time = DEFAULT_PER_TICK_TIME; // ms
mutable std::shared_mutex m_player_mutex;
mutable std::shared_mutex m_players_mutex;
std::mutex m_need_gen_queue_mutex;
std::condition_variable_any m_gen_cv;

View File

@@ -0,0 +1,19 @@
#pragma once
#include "Cubed/gameplay/ecs/movement.hpp"
#include "Cubed/gameplay/ecs/state.hpp"
#include "Cubed/gameplay/ecs/transform.hpp"
#include "Cubed/gameplay/world.hpp"
namespace Cubed {
class PhysicalSystem {
public:
static glm::vec3 get_move_distance(float dt, const Direction& d,
const Velocity& v);
static std::tuple<bool, bool, bool>
update(float dt, World& world, glm::vec3& moved_pos, Velocity& v,
Direction& direction, MoveState& move_state, HitboxID hitbox);
private:
};
} // namespace Cubed

View File

@@ -0,0 +1,16 @@
#pragma once
#include "Cubed/gameplay/ecs/movement.hpp"
#include "Cubed/gameplay/ecs/state.hpp"
#include "Cubed/gameplay/ecs/transform.hpp"
namespace Cubed {
class SpeedSystem {
public:
static void update(float dt, Velocity& v, MoveState& move_state,
Movement& movement, Direction& direction,
const Gravity& g);
private:
};
} // namespace Cubed

View File

@@ -0,0 +1,29 @@
#pragma once
#include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/hitbox.hpp"
#include <glm/glm.hpp>
namespace Cubed {
class World {
public:
World() = default;
World(const World&) = delete;
World(World&&) = delete;
World& operator=(const World&) = delete;
World& operator=(World&&) = delete;
virtual ~World() = default;
virtual int get_block(const glm::ivec3& block_pos) const = 0;
virtual bool is_solid(const glm::ivec3& block_pos) const = 0;
virtual bool can_pass_block(const glm::ivec3& block_pos) const = 0;
virtual BlockType get_block_tpye(const glm::ivec3& block_pos) const = 0;
virtual int get_per_tick_time() const = 0;
static Hitbox get_block_aabb(const glm::ivec3& pos) {
return {glm::vec3{static_cast<float>(pos.x) + 0.5f,
static_cast<float>(pos.y) + 0.5f,
static_cast<float>(pos.z) + 0.5f},
glm::vec3{0.5f, 0.5f, 0.5f}};
}
};
} // namespace Cubed

View File

@@ -2,15 +2,6 @@
namespace Cubed {
struct MoveState {
bool forward = false;
bool back = false;
bool left = false;
bool right = false;
bool down = false;
bool up = false;
};
struct MouseState {
bool left = false;
bool right = false;

View File

@@ -0,0 +1,45 @@
#pragma once
#include "Cubed/gameplay/model.hpp"
#include "Cubed/render/model_node.hpp"
#include "Cubed/tools/model_loader.hpp"
#include <tbb/concurrent_hash_map.h>
namespace Cubed {
class ModelManager {
public:
struct Handle {
const ModelNode& node;
ModelID id = 0;
};
ModelManager();
ModelManager(const ModelManager&) = delete;
ModelManager(ModelManager&&) = delete;
ModelManager& operator=(const ModelManager&) = delete;
ModelManager& operator=(ModelManager&&) = delete;
static ModelManager& instance();
~ModelManager();
[[nodiscard]]
Handle get_model(const std::string& model_name);
[[nodiscard]]
Handle get_model(ModelID id);
[[nodiscard]]
static Handle model(const std::string& model_name);
[[nodiscard]]
static Handle model(ModelID id);
ModelID get_model_id(const std::string& name);
const std::string& get_model_name(ModelID id);
void init();
private:
ModelLoader m_loader;
ModelID m_next = 0;
using ModelMap = tbb::concurrent_hash_map<ModelID, ModelNode>;
using IDMap = tbb::concurrent_hash_map<std::string, ModelID>;
using NameMap = tbb::concurrent_hash_map<ModelID, std::string>;
ModelMap m_models;
IDMap m_id_map;
NameMap m_name_map;
Handle load_model(std::string_view model_name);
};
} // namespace Cubed

View File

@@ -0,0 +1,42 @@
#pragma once
#include "Cubed/primitive_data.hpp"
#include "Cubed/render/texture.hpp"
#include "Cubed/render/vertex_array.hpp"
#include "Cubed/render/vertex_buffer.hpp"
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <memory>
#include <string>
namespace Cubed {
struct Mesh {
std::vector<Vertex3D> vertices;
std::vector<uint32_t> indices;
std::unique_ptr<VertexBuffer> vbo;
std::unique_ptr<VertexBuffer> ebo;
std::unique_ptr<VertexArray> vao;
std::unique_ptr<Texture> texture;
void upload() {
vao = std::make_unique<VertexArray>();
vao->bind();
vbo = std::make_unique<VertexBuffer>();
vbo->buffer_data(vertices.data(), vertices.size() * sizeof(Vertex3D));
ebo = std::make_unique<VertexBuffer>(BufferType::ELEMENT_ARRAY_BUFFER);
ebo->buffer_data(indices.data(), indices.size() * sizeof(uint32_t));
vao->attribute(0, 3, GL_FLOAT, sizeof(Vertex3D), (void*)0);
vao->attribute(1, 2, GL_FLOAT, sizeof(Vertex3D),
(void*)offsetof(Vertex3D, s));
vao->attribute(2, 3, GL_FLOAT, sizeof(Vertex3D),
(void*)offsetof(Vertex3D, nx));
};
};
struct ModelNode {
std::string name;
glm::mat4 transform{1.0f};
std::vector<Mesh> meshes;
std::vector<ModelNode> children;
};
} // namespace Cubed

View File

@@ -0,0 +1,24 @@
#pragma once
#include "Cubed/gameplay/model.hpp"
#include "Cubed/render/model_node.hpp"
#include "Cubed/shader.hpp"
#include <glm/glm.hpp>
namespace Cubed {
class Renderer;
class Camera;
class ModelRender {
public:
ModelRender(Renderer& renderer);
void render_model(ModelID id, const glm::vec3& pos, Camera& camera);
void shadow_pass(ModelID id, const glm::vec3& pos, Camera& camera);
private:
Renderer& m_renderer;
void render_node(const ModelNode& node, const glm::mat4& parent,
const glm::mat4& view, const Shader& shader, bool shadow);
void render_mesh(const Mesh& mesh, bool shadow);
};
} // namespace Cubed

View File

@@ -13,9 +13,7 @@ public:
PlayerRenderer(Renderer& renderer);
~PlayerRenderer();
void init();
void render(const Shader& shader, ClientWorld& world);
void shadow_render(const Shader& shader, glm::mat4& light_matrix,
ClientWorld& world);
void render(const Shader& shader, ClientWorld& world, bool shadow_render);
private:
struct PlayerVertex {

View File

@@ -4,6 +4,7 @@
#include "Cubed/constants.hpp"
#include "Cubed/input/event.hpp"
#include "Cubed/primitive_data.hpp"
#include "Cubed/render/model_renderer.hpp"
#include "Cubed/render/player_renderer.hpp"
#include "Cubed/render/shader_manager.hpp"
#include "Cubed/render/vertex_array.hpp"
@@ -19,6 +20,7 @@
namespace Cubed {
class TextureManager;
class ClientWorld;
class ModelManager;
class DevPanel;
class Renderer {
public:
@@ -79,9 +81,10 @@ public:
bool handle_event(const Event& e);
ModelRender& model_renderer();
private:
TextureManager& m_texture_manager;
bool m_init = false;
float m_aspect = 0.0f;
@@ -118,6 +121,7 @@ private:
std::vector<Vertex2D> m_ui;
WorldRenderer m_world_renderer;
ModelRender m_model_renderer;
Config& m_config;
bool handle_window_resize_event(const WindowResizeEvent& e);

View File

@@ -38,9 +38,11 @@ enum TextureFormat : GLenum {
R8 = GL_R8,
RGB = GL_RGB,
RGBA8 = GL_RGBA8,
BGRA = GL_BGRA
};
// You need to set the texture scaling method, otherwise it will render as
// black.
class Texture {
public:
explicit Texture(TextureType type);

View File

@@ -132,7 +132,8 @@ private:
void render_underwater(ClientWorld& world);
void render_outline(ClientWorld& world);
void render_player(ClientWorld& world);
void shadow_entity(ClientWorld& world, const glm::mat4& light_matrix);
void render_entity(ClientWorld& world);
void render_normal_block(const glm::mat4& model_mat,
const glm::mat4& mv_mat, const glm::mat4& norm_mat,

View File

@@ -6,6 +6,7 @@
#include "Cubed/gameplay/network_server.hpp"
#include "Cubed/scene/scene.hpp"
#include "Cubed/ui/error_ui.hpp"
#include "Cubed/ui/inventory_ui.hpp"
#include "Cubed/ui/pasue_menu_ui_manager.hpp"
#include "Cubed/ui/world_ui_manager.hpp"
namespace Cubed {
@@ -42,6 +43,7 @@ public:
bool is_recording() const;
private:
enum class PauseUI { PAUSE_MENU, INVENTORY };
SceneManager& m_scene_manager;
DevPanel m_dev_panel;
Camera m_camera;
@@ -53,6 +55,8 @@ private:
bool m_show_dev_pannel = true;
bool m_chatting = false;
PauseMenuUIManager m_pasue_menu;
InventoryUI m_inventory_ui;
PauseUI m_paused_ui = PauseUI::PAUSE_MENU;
WorldUIManager m_hud_ui;
ErrorUI m_error_ui;
const Argument& m_argument;

View File

@@ -48,7 +48,7 @@ public:
const Texture* get_cross_plane_array() const;
const Texture* get_image_texture(const std::string& path);
const Texture* get_pbr_texture() const;
const std::vector<std::unique_ptr<Texture>>& item_textures() const;
const std::vector<std::unique_ptr<Texture>>& get_item_textures() const;
const Texture* get_skin() const;
void init_texture();

View File

@@ -0,0 +1,7 @@
#pragma once
#include <type_traits>
namespace Cubed {
template <typename T>
concept Ptr = std::is_pointer_v<T>;
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Cubed/render/model_node.hpp"
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
namespace Cubed {
class ModelLoader {
public:
ModelLoader();
ModelNode load(const std::string& path);
private:
Assimp::Importer m_importer;
ModelNode process_node(aiNode* node, const aiScene* scene);
Mesh process_mesh(aiMesh* mesh, const aiScene* scene);
bool process_texture(Mesh& mesh, aiMaterial* material, const aiScene* scene,
aiTextureType type);
glm::mat4 convert_matrix(const aiMatrix4x4& matrix);
};
} // namespace Cubed

View File

@@ -0,0 +1,19 @@
#pragma once
#include <string_view>
#include <vector>
namespace Cubed {
inline std::vector<std::string_view> parse_namespace(std::string_view str) {
std::vector<std::string_view> space;
space.reserve(4);
std::size_t p = str.find(':');
std::size_t start = 0;
while (p != std::string_view::npos) {
space.emplace_back(str.substr(start, p));
start = p + 1;
p = str.find(':', p + 1);
}
space.emplace_back(str.substr(start));
return space;
}
} // namespace Cubed

View File

@@ -0,0 +1,21 @@
#pragma once
#include "Cubed/tools/cubed_concepts.hpp"
#include "common/vector3.pb.h"
#include "glm/ext/vector_float3.hpp"
namespace Cubed {
namespace Tools {
template <Ptr T> void set_net_pos(T ptr, const glm::vec3& pos) {
Vec3* p = ptr->mutable_pos();
p->set_x(pos.x);
p->set_y(pos.y);
p->set_z(pos.z);
}
inline glm::vec3 get_net_pos(const Vec3* p) {
return glm::vec3{p->x(), p->y(), p->z()};
}
inline glm::vec3 get_net_pos(const Vec3& p) {
return glm::vec3{p.x(), p.y(), p.z()};
}
} // namespace Tools
} // namespace Cubed

View File

@@ -1,6 +1,5 @@
#pragma once
#include <SOIL2.h>
#include <glad/glad.h>
#include <string>
#include <utility>
@@ -48,7 +47,7 @@ bool check_opengl_error();
std::string read_shader_source(const std::string& file_path);
ImageData load_image_data(const std::string& tex_image_path,
bool check_exist = true);
bool check_exist = true, bool full_path = false);
} // namespace Tools

View File

@@ -0,0 +1,318 @@
#pragma once
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <optional>
#include <utility>
#include <vector>
namespace Cubed {
template <typename T> class SparseVector {
private:
std::vector<std::optional<T>> m_data;
std::vector<uint32_t> m_free_list;
std::vector<uint32_t> m_generation;
std::vector<uint32_t> m_dense;
std::vector<uint32_t> m_dense_index;
public:
struct Handle {
uint32_t index;
uint32_t generation;
uint64_t value() const noexcept {
return (uint64_t(index) << 32) | generation;
}
bool operator==(const Handle&) const = default;
struct Hash {
size_t operator()(const Handle& h) const noexcept {
return h.value();
}
};
};
using value_type = T;
using reference = T&;
using const_reference = const T&;
using pointer = T*;
using size_type = size_t;
class iterator { // NOLINT
private:
SparseVector* m_owner;
size_t m_index;
public:
using iterator_category = std::random_access_iterator_tag;
using iterator_concept = std::random_access_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
iterator(SparseVector* owner, size_t index)
: m_owner(owner), m_index(index) {}
reference operator*() {
return m_owner->m_data[m_owner->m_dense[m_index]].value();
}
pointer operator->() { return &(**this); }
iterator& operator++() {
++m_index;
return *this;
}
iterator operator++(int) {
iterator tmp = *this;
++(*this);
return tmp;
}
iterator& operator--() {
--m_index;
return *this;
}
iterator operator--(int) {
iterator tmp = *this;
--(*this);
return tmp;
}
bool operator==(const iterator& other) const {
return m_owner == other.m_owner && m_index == other.m_index;
}
bool operator!=(const iterator& other) const {
return !(*this == other);
}
iterator operator+(difference_type n) const {
return iterator(m_owner, m_index + n);
}
iterator operator-(difference_type n) const {
return iterator(m_owner, m_index - n);
}
difference_type operator-(const iterator& other) const {
return static_cast<difference_type>(m_index) -
static_cast<difference_type>(other.m_index);
}
iterator& operator+=(difference_type n) {
m_index += n;
return *this;
}
iterator& operator-=(difference_type n) {
m_index -= n;
return *this;
}
reference operator[](difference_type n) const { return *(*this + n); }
bool operator<(const iterator& other) const {
return m_index < other.m_index;
}
bool operator>(const iterator& other) const {
return m_index > other.m_index;
}
bool operator<=(const iterator& other) const {
return m_index <= other.m_index;
}
bool operator>=(const iterator& other) const {
return m_index >= other.m_index;
}
friend iterator operator+(difference_type n, const iterator& it) {
return it + n;
}
};
class const_iterator { // NOLINT
private:
const SparseVector* m_owner;
size_t m_index;
public:
using iterator_category = std::random_access_iterator_tag;
using iterator_concept = std::random_access_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = const T*;
using reference = const T&;
const_iterator(const SparseVector* owner, size_t index)
: m_owner(owner), m_index(index) {}
reference operator*() const {
return m_owner->m_data[m_owner->m_dense[m_index]].value();
}
pointer operator->() const { return &(**this); }
const_iterator& operator++() {
++m_index;
return *this;
}
const_iterator operator++(int) {
const_iterator tmp = *this;
++(*this);
return tmp;
}
const_iterator& operator--() {
--m_index;
return *this;
}
const_iterator operator--(int) {
const_iterator tmp = *this;
--(*this);
return tmp;
}
bool operator==(const const_iterator& other) const {
return m_owner == other.m_owner && m_index == other.m_index;
}
bool operator!=(const const_iterator& other) const {
return !(*this == other);
}
const_iterator operator+(difference_type n) const {
return const_iterator(m_owner, m_index + n);
}
const_iterator operator-(difference_type n) const {
return const_iterator(m_owner, m_index - n);
}
difference_type operator-(const const_iterator& other) const {
return static_cast<difference_type>(m_index) -
static_cast<difference_type>(other.m_index);
}
const_iterator& operator+=(difference_type n) {
m_index += n;
return *this;
}
const_iterator& operator-=(difference_type n) {
m_index -= n;
return *this;
}
reference operator[](difference_type n) const { return *(*this + n); }
bool operator<(const const_iterator& other) const {
return m_index < other.m_index;
}
bool operator>(const const_iterator& other) const {
return m_index > other.m_index;
}
bool operator<=(const const_iterator& other) const {
return m_index <= other.m_index;
}
bool operator>=(const const_iterator& other) const {
return m_index >= other.m_index;
}
friend const_iterator operator+(difference_type n,
const const_iterator& it) {
return it + n;
}
};
template <class U> [[nodiscard]] Handle insert(U&& value) {
uint32_t id;
if (!m_free_list.empty()) {
id = m_free_list.back();
m_free_list.pop_back();
m_data[id].emplace(std::forward<U>(value));
m_dense_index[id] = m_dense.size();
} else {
id = m_data.size();
m_data.push_back(std::forward<U>(value));
m_generation.push_back(1);
m_dense_index.emplace_back(m_dense.size());
}
m_dense.push_back(id);
return {id, m_generation[id]};
}
template <typename... Args> Handle emplace(Args&&... args) {
return insert(T(std::forward<Args>(args)...));
}
void erase(Handle h) {
if (!exists(h)) {
return;
}
uint32_t id = h.index;
m_free_list.push_back(id);
uint32_t index = m_dense_index[id];
uint32_t last_id = m_dense.back();
m_dense[index] = last_id;
m_dense_index[last_id] = index;
m_dense.pop_back();
m_data[id].reset();
++m_generation[id];
}
T& operator[](Handle h) {
assert(exists(h));
return m_data[h.index].value();
}
const T& operator[](Handle h) const {
assert(exists(h));
return m_data[h.index].value();
}
bool exists(Handle h) const {
return h.index < m_generation.size() &&
m_generation[h.index] == h.generation;
}
void reserve(size_t n) {
m_data.reserve(n);
m_generation.reserve(n);
m_dense.reserve(n);
m_dense_index.reserve(n);
}
size_t size() const { return m_dense.size(); }
bool empty() const { return m_dense.empty(); }
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, m_dense.size()); }
const_iterator begin() const { return const_iterator(this, 0); }
const_iterator end() const { return const_iterator(this, m_dense.size()); }
const_iterator cbegin() const { return begin(); }
const_iterator cend() const { return end(); }
};
} // namespace Cubed

View File

@@ -29,6 +29,12 @@ std::optional<T> safe_get_value(const toml::table& table, std::string_view key,
return value;
}
inline std::optional<std::string>
safe_get_value(const toml::table& table, std::string_view key,
std::string_view default_value) {
return safe_get_value(table, key, std::string(default_value));
}
} // namespace TOML
} // namespace Cubed

View File

@@ -12,4 +12,7 @@ enum class Anchor {
BOTTOM_LEFT,
BOTTOM_CENTER,
BOTTOM_RIGHT,
FOLLOW_MOUSE
};

View File

@@ -12,9 +12,6 @@ public:
ColumnLayout& operator=(ColumnLayout&&) = delete;
ColumnLayout(Widget* parent);
~ColumnLayout();
void update(float dt) override;
float width() const override;
float height() const override;
@@ -29,5 +26,7 @@ private:
float m_content_height = 0.0f;
float m_content_width = 0.0f;
ColumnLayoutAnchor m_layout_anchor = ColumnLayoutAnchor::CENTER;
void on_update(float dt) override;
};
} // namespace Cubed

View File

@@ -13,13 +13,19 @@ public:
Image& operator=(const Image&) = delete;
Image& operator=(Image&&) = delete;
Image(Widget* parent);
Image& set_image(const std::string& path, TextureManager& texture_manager);
// If you need to set width and height to the material's size, pass true,
// If you set fill parent, please pass false.
Image& set_image(const std::string& path, TextureManager& texture_manager,
bool change_size);
// If you need to set width and height to the material's size, pass true
// If you set fill parent, please pass false.
Image& set_texture(const Texture* texture, bool change_size);
float width() const override;
float height() const override;
const Texture* texture() const;
Image& set_scale(float scale);
float scale() const;
bool has_texture() const;
private:
void on_render(Renderer& renderer) override;

View File

@@ -0,0 +1,30 @@
#pragma once
#include "Cubed/ui/item_slot.hpp"
#include "Cubed/ui/label.hpp"
#include "Cubed/ui/ui_manager.hpp"
namespace Cubed {
class WorldScene;
class InventoryUI : public UIManager {
public:
InventoryUI(WorldScene& m_scene);
void init() override;
void on_re_enter();
void update(float dt) override;
private:
WorldScene& m_scene;
std::vector<ItemSlot*> m_slots;
std::vector<ItemSlot*> m_hotbar;
Label* m_item_info = nullptr;
Image* m_selected_image = nullptr;
BlockType m_selected_block = 0;
void update_item_info();
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
ItemSlot* get_hovered_slot();
std::pair<ItemSlot*, size_t> get_hovered_hotbar_slot();
};
} // namespace Cubed

View File

@@ -0,0 +1,33 @@
#pragma once
#include "Cubed/gameplay/block.hpp"
#include "Cubed/ui/image.hpp"
#include "Cubed/ui/widget.hpp"
namespace Cubed {
class TextureManager;
class ItemSlot : public Widget {
public:
static constexpr float DEFAULT_WIDTH = 16.0f;
static constexpr float DEFAULT_HEIGHT = 16.0f;
ItemSlot(Widget* parent);
ItemSlot& set_default_background(TextureManager& m_texture_manager);
ItemSlot& set_scale(float m_scale);
ItemSlot& set_item(BlockType id, const Texture* texture);
float width() const override;
float height() const override;
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
BlockType block() const;
bool hovered() const;
private:
static constexpr const char* DEFAULT_SLOT_PATH = "texture/ui/slot.png";
void on_render(Renderer& renderer) override;
void on_update(float dt) override;
std::unique_ptr<Image> m_background;
std::unique_ptr<Image> m_foreground;
BlockType m_block_type;
float m_scale = 1.0f;
bool m_hovered = false;
};
} // namespace Cubed

View File

@@ -21,6 +21,7 @@ public:
Rect& set_alpha(float alpha);
Color color() const;
virtual bool supports_border() const override;
private:
void on_update(float dt) override;

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Cubed/ui/widget.hpp"
namespace Cubed {
class RowLayout : public Widget {
public:
RowLayout(Widget* parent);
~RowLayout();
float width() const override;
float height() const override;
RowLayout& set_spacing(int spacing);
void layout();
private:
int m_spacing = 0;
float m_content_height = 0.0f;
float m_content_width = 0.0f;
void on_update(float dt) override;
};
} // namespace Cubed

View File

@@ -2,9 +2,11 @@
#include "Cubed/input/event.hpp"
#include "Cubed/ui/anchor.hpp"
#include <array>
#include <glm/glm.hpp>
#include <memory>
#include <vector>
namespace Cubed {
class Renderer;
@@ -31,7 +33,7 @@ public:
virtual void render(Renderer& renderer);
virtual Widget& set_anchor(Anchor anchor);
virtual Widget& set_offset(glm::ivec2 offset);
virtual Widget& set_window_size(int width, int height);
static void set_window_size(int width, int height);
virtual Widget& set_visible(bool visible);
// Returns the final display size
@@ -43,7 +45,9 @@ public:
virtual Widget& set_fill_parent(bool fill);
virtual Widget& set_fill_width(bool fill);
virtual Widget& set_fill_height(bool fill);
virtual Widget& set_border_size(int size);
virtual Widget& set_border_visale(bool visable);
virtual bool supports_border() const;
virtual glm::vec2 pos() const;
virtual bool handle_key_event(const KeyEvent& e);
@@ -63,16 +67,17 @@ public:
protected:
Widget* m_parent = nullptr;
float m_window_height = 0.0f;
float m_window_width = 0.0f;
float m_width = 0.0f;
float m_height = 0.0f;
static inline float m_window_height = 0.0f;
static inline float m_window_width = 0.0f;
// Center is at the top-left corner, position is at the top-left corner
Anchor m_anchor = Anchor::TOP_LEFT;
bool m_visible = true;
bool m_fill_parent = false;
bool m_fill_height = false;
bool m_fill_width = false;
bool m_show_border = false;
int m_border_size = 5;
glm::ivec2 m_offset{0, 0};
std::vector<std::unique_ptr<Widget>>& children();
@@ -82,9 +87,22 @@ protected:
virtual void on_update(float dt);
virtual void on_render(Renderer& renderer);
virtual glm::vec2 compute_position() const;
virtual void update_border();
void set_width_internal(float width);
void set_height_internal(float height);
private:
std::vector<std::unique_ptr<Widget>> m_children;
/*
0 - up
1 - down
2 - left
3 - right
*/
std::array<std::unique_ptr<Widget>, 4> m_border;
TraversalOrder m_order = TraversalOrder::BACK_TO_FRONT;
glm::vec2 m_mouse_pos;
float m_width = 0.0f;
float m_height = 0.0f;
};
} // namespace Cubed

View File

@@ -2,6 +2,8 @@
#include "Cubed/ui/chat_box.hpp"
#include "Cubed/ui/image.hpp"
#include "Cubed/ui/item_slot.hpp"
#include "Cubed/ui/row_layout.hpp"
#include "Cubed/ui/ui_manager.hpp"
#include "Cubed/ui/widget.hpp"
@@ -26,10 +28,14 @@ public:
private:
bool handle_key_event(const KeyEvent& e) override;
void update_hotbar();
WorldScene& m_scene;
ChatBox* m_chat_box = nullptr;
Image* m_mircophone = nullptr;
Label* m_disbable_voice = nullptr;
RowLayout* m_hotbar = nullptr;
std::vector<ItemSlot*> m_hotbar_slot;
};
} // namespace Cubed

View File

@@ -0,0 +1,134 @@
#ifndef ENTT_CONFIG_CONFIG_H
#define ENTT_CONFIG_CONFIG_H
#if __has_include(<entt/ext/config.h>)
# include <entt/ext/config.h>
#endif
#include <version>
#include "version.h"
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#ifdef ENTT_USE_STL
# define ENTT_FORCE_STL
#endif
#if defined(__cpp_exceptions) && !defined(ENTT_NO_EXCEPTION)
# define ENTT_THROW throw
# define ENTT_TRY try
# define ENTT_CATCH catch(...)
#else
# define ENTT_THROW
# define ENTT_TRY if(true)
# define ENTT_CATCH if(false)
#endif
#if defined(__cpp_consteval)
# define ENTT_CONSTEVAL consteval
#else
# define ENTT_CONSTEVAL constexpr
#endif
#ifdef ENTT_USE_ATOMIC
# include "../stl/atomic.hpp"
# define ENTT_MAYBE_ATOMIC(Type) stl::atomic<Type>
#else
# define ENTT_MAYBE_ATOMIC(Type) Type
#endif
#ifndef ENTT_ID_TYPE
# include "../stl/cstdint.hpp"
# define ENTT_ID_TYPE stl::uint32_t
#else
# include "../stl/cstdint.hpp" // provides coverage for types in the std namespace
#endif
#ifndef ENTT_SPARSE_PAGE
# define ENTT_SPARSE_PAGE 4096
#endif
#ifndef ENTT_PACKED_PAGE
# define ENTT_PACKED_PAGE 1024
#endif
#ifdef ENTT_DISABLE_ASSERT
# undef ENTT_ASSERT
# define ENTT_ASSERT(condition, msg) (void(0))
#elif !defined ENTT_ASSERT
# include <cassert>
# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg)))
#endif
#ifdef ENTT_DISABLE_ASSERT
# undef ENTT_ASSERT_CONSTEXPR
# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0))
#elif !defined ENTT_ASSERT_CONSTEXPR
# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg)
#endif
#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg);
#ifdef ENTT_NO_ETO
# define ENTT_ETO_TYPE(Type) void
#else
# define ENTT_ETO_TYPE(Type) Type
#endif
#ifdef ENTT_NO_MIXIN
# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__
#else
# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__>
#endif
#ifdef ENTT_STANDARD_CPP
# define ENTT_NONSTD false
#else
# define ENTT_NONSTD true
# if defined __clang__ || defined __GNUC__
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
# define ENTT_PRETTY_FUNCTION_PREFIX '='
# define ENTT_PRETTY_FUNCTION_SUFFIX ']'
# elif defined _MSC_VER
# define ENTT_PRETTY_FUNCTION __FUNCSIG__
# define ENTT_PRETTY_FUNCTION_PREFIX '<'
# define ENTT_PRETTY_FUNCTION_SUFFIX '>'
# endif
#endif
#ifndef ENTT_EXPORT
# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
# define ENTT_EXPORT __declspec(dllexport)
# define ENTT_IMPORT __declspec(dllimport)
# define ENTT_HIDDEN
# elif defined __GNUC__ && __GNUC__ >= 4
# define ENTT_EXPORT __attribute__((visibility("default")))
# define ENTT_IMPORT __attribute__((visibility("default")))
# define ENTT_HIDDEN __attribute__((visibility("hidden")))
# else /* Unsupported compiler */
# define ENTT_EXPORT
# define ENTT_IMPORT
# define ENTT_HIDDEN
# endif
#endif
#ifndef ENTT_API
# if defined ENTT_API_EXPORT
# define ENTT_API ENTT_EXPORT
# elif defined ENTT_API_IMPORT
# define ENTT_API ENTT_IMPORT
# else /* No API */
# define ENTT_API
# endif
#endif
#if defined _MSC_VER
# pragma detect_mismatch("entt.version", ENTT_VERSION)
# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
#endif
// NOLINTEND(cppcoreguidelines-macro-usage)
#endif

View File

@@ -0,0 +1,11 @@
#ifndef ENTT_CONFIG_MACRO_H
#define ENTT_CONFIG_MACRO_H
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#define ENTT_STR(arg) #arg
#define ENTT_XSTR(arg) ENTT_STR(arg)
// NOLINTEND(cppcoreguidelines-macro-usage)
#endif

View File

@@ -0,0 +1,18 @@
#ifndef ENTT_CONFIG_VERSION_H
#define ENTT_CONFIG_VERSION_H
#include "macro.h"
// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*)
#define ENTT_VERSION_MAJOR 4
#define ENTT_VERSION_MINOR 0
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
"." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*)
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,890 @@
#ifndef ENTT_CONTAINER_DENSE_SET_HPP
#define ENTT_CONTAINER_DENSE_SET_HPP
#include <compare>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/type_traits.hpp"
#include "../stl/bit.hpp"
#include "../stl/cmath.hpp"
#include "../stl/concepts.hpp"
#include "../stl/cstddef.hpp"
#include "../stl/functional.hpp"
#include "../stl/iterator.hpp"
#include "../stl/limits.hpp"
#include "../stl/memory.hpp"
#include "../stl/tuple.hpp"
#include "../stl/type_traits.hpp"
#include "../stl/utility.hpp"
#include "../stl/vector.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond ENTT_INTERNAL */
namespace internal {
static constexpr stl::size_t dense_set_placeholder_position = (stl::numeric_limits<stl::size_t>::max)();
template<typename It>
class dense_set_iterator final {
template<typename>
friend class dense_set_iterator;
static_assert(stl::is_pointer_v<It>, "Not a pointer type");
public:
using value_type = stl::remove_const_t<stl::remove_pointer_t<It>>::second_type;
using pointer = const value_type *;
using reference = const value_type &;
using difference_type = stl::ptrdiff_t;
using iterator_category = stl::random_access_iterator_tag;
constexpr dense_set_iterator() noexcept
: it{} {}
constexpr dense_set_iterator(const It iter) noexcept
: it{iter} {}
template<typename Other>
requires (!stl::same_as<It, Other> && stl::constructible_from<It, Other>)
constexpr dense_set_iterator(const dense_set_iterator<Other> &other) noexcept
: it{other.it} {}
constexpr dense_set_iterator &operator++() noexcept {
return ++it, *this;
}
constexpr dense_set_iterator operator++(int) noexcept {
const dense_set_iterator orig = *this;
return ++(*this), orig;
}
constexpr dense_set_iterator &operator--() noexcept {
return --it, *this;
}
constexpr dense_set_iterator operator--(int) noexcept {
const dense_set_iterator orig = *this;
return operator--(), orig;
}
constexpr dense_set_iterator &operator+=(const difference_type value) noexcept {
it += value;
return *this;
}
constexpr dense_set_iterator operator+(const difference_type value) const noexcept {
dense_set_iterator copy = *this;
return (copy += value);
}
constexpr dense_set_iterator &operator-=(const difference_type value) noexcept {
return (*this += -value);
}
constexpr dense_set_iterator operator-(const difference_type value) const noexcept {
return (*this + -value);
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
return it[value].second;
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return stl::addressof(operator[](0));
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return operator[](0);
}
template<typename Other>
[[nodiscard]] constexpr stl::ptrdiff_t operator-(const dense_set_iterator<Other> &other) const noexcept {
return it - other.it;
}
template<typename Other>
[[nodiscard]] constexpr bool operator==(const dense_set_iterator<Other> &other) const noexcept {
return it == other.it;
}
template<typename Other>
[[nodiscard]] constexpr auto operator<=>(const dense_set_iterator<Other> &other) const noexcept {
return it <=> other.it;
}
private:
It it;
};
template<typename It>
class dense_set_local_iterator final {
template<typename>
friend class dense_set_local_iterator;
static_assert(stl::is_pointer_v<It>, "Not a pointer type");
public:
using value_type = stl::remove_const_t<stl::remove_pointer_t<It>>::second_type;
using pointer = const value_type *;
using reference = const value_type &;
using difference_type = stl::ptrdiff_t;
using iterator_category = stl::forward_iterator_tag;
constexpr dense_set_local_iterator() noexcept = default;
constexpr dense_set_local_iterator(It iter, const stl::size_t pos) noexcept
: it{iter},
offset{pos} {}
template<typename Other>
requires (!stl::same_as<It, Other> && stl::constructible_from<It, Other>)
constexpr dense_set_local_iterator(const dense_set_local_iterator<Other> &other) noexcept
: it{other.it},
offset{other.offset} {}
constexpr dense_set_local_iterator &operator++() noexcept {
return offset = it[static_cast<difference_type>(offset)].first, *this;
}
constexpr dense_set_local_iterator operator++(int) noexcept {
const dense_set_local_iterator orig = *this;
return ++(*this), orig;
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return stl::addressof(it[static_cast<difference_type>(offset)].second);
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return *operator->();
}
template<typename Other>
[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Other> &other) const noexcept {
return offset == other.offset;
}
[[nodiscard]] constexpr stl::size_t index() const noexcept {
return offset;
}
private:
It it{};
stl::size_t offset{dense_set_placeholder_position};
};
} // namespace internal
/*! @endcond */
/**
* @brief Associative container for unique objects of a given type.
*
* Internally, elements are organized into buckets. Which bucket an element is
* placed into depends entirely on its hash. Elements with the same hash code
* appear in the same bucket.
*
* @tparam Type Value type of the associative container.
* @tparam Hash Type of function to use to hash the values.
* @tparam KeyEqual Type of function to use to compare the values for equality.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
class dense_set {
static constexpr float default_threshold = 0.875f;
static constexpr stl::size_t minimum_capacity = 8u;
static constexpr stl::size_t placeholder_position = internal::dense_set_placeholder_position;
using node_type = stl::pair<stl::size_t, Type>;
using alloc_traits = stl::allocator_traits<Allocator>;
static_assert(stl::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using sparse_container_type = stl::vector<stl::size_t, typename alloc_traits::template rebind_alloc<stl::size_t>>;
using packed_container_type = stl::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
[[nodiscard]] stl::size_t value_to_bucket(const auto &value) const noexcept {
return fast_mod(static_cast<size_type>(sparse.second()(value)), bucket_count());
}
[[nodiscard]] auto constrained_find(const auto &value, const stl::size_t bucket) {
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
if(packed.second()(packed.first()[offset].second, value)) {
return begin() + static_cast<iterator::difference_type>(offset);
}
}
return end();
}
[[nodiscard]] auto constrained_find(const auto &value, const stl::size_t bucket) const {
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
if(packed.second()(packed.first()[offset].second, value)) {
return cbegin() + static_cast<const_iterator::difference_type>(offset);
}
}
return cend();
}
template<typename Other>
[[nodiscard]] auto insert_or_do_nothing(Other &&value) {
const auto index = value_to_bucket(value);
if(auto it = constrained_find(value, index); it != end()) {
return stl::make_pair(it, false);
}
packed.first().emplace_back(sparse.first()[index], stl::forward<Other>(value));
sparse.first()[index] = packed.first().size() - 1u;
rehash_if_required();
return stl::make_pair(--end(), true);
}
void move_and_pop(const stl::size_t pos) {
if(const auto last = size() - 1u; pos != last) {
size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)];
packed.first()[pos] = stl::move(packed.first().back());
for(; *curr != last; curr = &packed.first()[*curr].first) {}
*curr = pos;
}
packed.first().pop_back();
}
void rehash_if_required() {
if(const auto bc = bucket_count(); size() > static_cast<size_type>(static_cast<float>(bc) * max_load_factor())) {
rehash(bc * 2u);
}
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Key type of the container. */
using key_type = Type;
/*! @brief Value type of the container. */
using value_type = Type;
/*! @brief Unsigned integer type. */
using size_type = stl::size_t;
/*! @brief Signed integer type. */
using difference_type = stl::ptrdiff_t;
/*! @brief Type of function to use to hash the elements. */
using hasher = Hash;
/*! @brief Type of function to use to compare the elements for equality. */
using key_equal = KeyEqual;
/*! @brief Random access iterator type. */
using iterator = internal::dense_set_iterator<typename packed_container_type::pointer>;
/*! @brief Constant random access iterator type. */
using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_pointer>;
/*! @brief Reverse iterator type. */
using reverse_iterator = stl::reverse_iterator<iterator>;
/*! @brief Constant reverse iterator type. */
using const_reverse_iterator = stl::reverse_iterator<const_iterator>;
/*! @brief Forward iterator type. */
using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::pointer>;
/*! @brief Constant forward iterator type. */
using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_pointer>;
/*! @brief Default constructor. */
dense_set()
: dense_set{minimum_capacity} {}
/**
* @brief Constructs an empty container with a given allocator.
* @param allocator The allocator to use.
*/
explicit dense_set(const allocator_type &allocator)
: dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
/**
* @brief Constructs an empty container with a given allocator and user
* supplied minimal number of buckets.
* @param cnt Minimal number of buckets.
* @param allocator The allocator to use.
*/
dense_set(const size_type cnt, const allocator_type &allocator)
: dense_set{cnt, hasher{}, key_equal{}, allocator} {}
/**
* @brief Constructs an empty container with a given allocator, hash
* function and user supplied minimal number of buckets.
* @param cnt Minimal number of buckets.
* @param hash Hash function to use.
* @param allocator The allocator to use.
*/
dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator)
: dense_set{cnt, hash, key_equal{}, allocator} {}
/**
* @brief Constructs an empty container with a given allocator, hash
* function, compare function and user supplied minimal number of buckets.
* @param cnt Minimal number of buckets.
* @param hash Hash function to use.
* @param equal Compare function to use.
* @param allocator The allocator to use.
*/
explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
: sparse{allocator, hash},
packed{allocator, equal} {
rehash(cnt);
}
/*! @brief Default copy constructor. */
dense_set(const dense_set &) = default;
/**
* @brief Allocator-extended copy constructor.
* @param other The instance to copy from.
* @param allocator The allocator to use.
*/
dense_set(const dense_set &other, const allocator_type &allocator)
: sparse{stl::piecewise_construct, stl::forward_as_tuple(other.sparse.first(), allocator), stl::forward_as_tuple(other.sparse.second())},
packed{stl::piecewise_construct, stl::forward_as_tuple(other.packed.first(), allocator), stl::forward_as_tuple(other.packed.second())},
threshold{other.threshold} {}
/*! @brief Default move constructor. */
dense_set(dense_set &&) noexcept = default;
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
dense_set(dense_set &&other, const allocator_type &allocator)
: sparse{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.sparse.first()), allocator), stl::forward_as_tuple(stl::move(other.sparse.second()))},
packed{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.packed.first()), allocator), stl::forward_as_tuple(stl::move(other.packed.second()))},
threshold{other.threshold} {}
/*! @brief Default destructor. */
~dense_set() = default;
/**
* @brief Default copy assignment operator.
* @return This container.
*/
dense_set &operator=(const dense_set &) = default;
/**
* @brief Default move assignment operator.
* @return This container.
*/
dense_set &operator=(dense_set &&) noexcept = default;
/**
* @brief Exchanges the contents with those of a given container.
* @param other Container to exchange the content with.
*/
void swap(dense_set &other) noexcept {
using stl::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
swap(threshold, other.threshold);
}
/**
* @brief Returns the associated allocator.
* @return The associated allocator.
*/
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
return sparse.first().get_allocator();
}
/**
* @brief Returns an iterator to the beginning.
*
* If the array is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first instance of the internal array.
*/
[[nodiscard]] const_iterator cbegin() const noexcept {
return packed.first().data();
}
/*! @copydoc cbegin */
[[nodiscard]] const_iterator begin() const noexcept {
return cbegin();
}
/*! @copydoc begin */
[[nodiscard]] iterator begin() noexcept {
return packed.first().data();
}
/**
* @brief Returns an iterator to the end.
* @return An iterator to the element following the last instance of the
* internal array.
*/
[[nodiscard]] const_iterator cend() const noexcept {
return packed.first().data() + packed.first().size();
}
/*! @copydoc cend */
[[nodiscard]] const_iterator end() const noexcept {
return cend();
}
/*! @copydoc end */
[[nodiscard]] iterator end() noexcept {
return packed.first().data() + packed.first().size();
}
/**
* @brief Returns a reverse iterator to the beginning.
*
* If the array is empty, the returned iterator will be equal to `rend()`.
*
* @return An iterator to the first instance of the reversed internal array.
*/
[[nodiscard]] const_reverse_iterator crbegin() const noexcept {
return stl::make_reverse_iterator(cend());
}
/*! @copydoc crbegin */
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
return crbegin();
}
/*! @copydoc rbegin */
[[nodiscard]] reverse_iterator rbegin() noexcept {
return stl::make_reverse_iterator(end());
}
/**
* @brief Returns a reverse iterator to the end.
* @return An iterator to the element following the last instance of the
* reversed internal array.
*/
[[nodiscard]] const_reverse_iterator crend() const noexcept {
return stl::make_reverse_iterator(cbegin());
}
/*! @copydoc crend */
[[nodiscard]] const_reverse_iterator rend() const noexcept {
return crend();
}
/*! @copydoc rend */
[[nodiscard]] reverse_iterator rend() noexcept {
return stl::make_reverse_iterator(begin());
}
/**
* @brief Checks whether a container is empty.
* @return True if the container is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return packed.first().empty();
}
/**
* @brief Returns the number of elements in a container.
* @return Number of elements in a container.
*/
[[nodiscard]] size_type size() const noexcept {
return packed.first().size();
}
/**
* @brief Returns the maximum possible number of elements.
* @return Maximum possible number of elements.
*/
[[nodiscard]] size_type max_size() const noexcept {
return packed.first().max_size();
}
/*! @brief Clears the container. */
void clear() noexcept {
sparse.first().clear();
packed.first().clear();
rehash(0u);
}
/**
* @brief Inserts an element into the container, if it does not exist.
* @param value An element to insert into the container.
* @return A pair consisting of an iterator to the inserted element (or to
* the element that prevented the insertion) and a bool denoting whether the
* insertion took place.
*/
stl::pair<iterator, bool> insert(const value_type &value) {
return insert_or_do_nothing(value);
}
/*! @copydoc insert */
stl::pair<iterator, bool> insert(value_type &&value) {
return insert_or_do_nothing(stl::move(value));
}
/**
* @brief Inserts elements into the container, if they do not exist.
* @param first An iterator to the first element of the range of elements.
* @param last An iterator past the last element of the range of elements.
*/
void insert(stl::input_iterator auto first, stl::input_iterator auto last) {
for(; first != last; ++first) {
insert(*first);
}
}
/**
* @brief Constructs an element in-place, if it does not exist.
*
* The element is also constructed when the container already has the key,
* in which case the newly constructed object is destroyed immediately.
*
* @tparam Args Types of arguments to forward to the constructor of the
* element.
* @param args Arguments to forward to the constructor of the element.
* @return A pair consisting of an iterator to the inserted element (or to
* the element that prevented the insertion) and a bool denoting whether the
* insertion took place.
*/
template<typename... Args>
stl::pair<iterator, bool> emplace(Args &&...args) {
if constexpr(((sizeof...(Args) == 1u) && ... && stl::is_same_v<stl::decay_t<Args>, value_type>)) {
return insert_or_do_nothing(stl::forward<Args>(args)...);
} else {
auto &node = packed.first().emplace_back(stl::piecewise_construct, stl::make_tuple(packed.first().size()), stl::forward_as_tuple(stl::forward<Args>(args)...));
const auto index = value_to_bucket(node.second);
if(auto it = constrained_find(node.second, index); it != end()) {
packed.first().pop_back();
return stl::make_pair(it, false);
}
stl::swap(node.first, sparse.first()[index]);
rehash_if_required();
return stl::make_pair(--end(), true);
}
}
/**
* @brief Removes an element from a given position.
* @param pos An iterator to the element to remove.
* @return An iterator following the removed element.
*/
iterator erase(const_iterator pos) {
const auto diff = pos - cbegin();
erase(*pos);
return begin() + diff;
}
/**
* @brief Removes the given elements from a container.
* @param first An iterator to the first element of the range of elements.
* @param last An iterator past the last element of the range of elements.
* @return An iterator following the last removed element.
*/
iterator erase(const_iterator first, const_iterator last) {
const auto dist = first - cbegin();
for(auto from = last - cbegin(); from != dist; --from) {
erase(packed.first()[static_cast<size_type>(from) - 1u].second);
}
return (begin() + dist);
}
/**
* @brief Removes the element associated with a given value.
* @param value Value of an element to remove.
* @return Number of elements removed (either 0 or 1).
*/
size_type erase(const value_type &value) {
for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != placeholder_position; curr = &packed.first()[*curr].first) {
if(packed.second()(packed.first()[*curr].second, value)) {
const auto index = *curr;
*curr = packed.first()[*curr].first;
move_and_pop(index);
return 1u;
}
}
return 0u;
}
/**
* @brief Returns the number of elements matching a value (either 1 or 0).
* @param key Key value of an element to search for.
* @return Number of elements matching the key (either 1 or 0).
*/
[[nodiscard]] size_type count(const value_type &key) const {
return find(key) != end();
}
/**
* @brief Returns the number of elements matching a key (either 1 or 0).
* @param key Key value of an element to search for.
* @return Number of elements matching the key (either 1 or 0).
*/
[[nodiscard]] size_type count(const auto &key) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
return find(key) != end();
}
/**
* @brief Finds an element with a given value.
* @param value Value of an element to search for.
* @return An iterator to an element with the given value. If no such
* element is found, a past-the-end iterator is returned.
*/
[[nodiscard]] iterator find(const value_type &value) {
return constrained_find(value, value_to_bucket(value));
}
/*! @copydoc find */
[[nodiscard]] const_iterator find(const value_type &value) const {
return constrained_find(value, value_to_bucket(value));
}
/**
* @brief Finds an element that compares _equivalent_ to a given value.
* @param value Value of an element to search for.
* @return An iterator to an element with the given value. If no such
* element is found, a past-the-end iterator is returned.
*/
[[nodiscard]] iterator find(const auto &value)
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
return constrained_find(value, value_to_bucket(value));
}
/*! @copydoc find */
[[nodiscard]] const_iterator find(const auto &value) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
return constrained_find(value, value_to_bucket(value));
}
/**
* @brief Returns a range containing all elements with a given value.
* @param value Value of an element to search for.
* @return A pair of iterators pointing to the first element and past the
* last element of the range.
*/
[[nodiscard]] stl::pair<iterator, iterator> equal_range(const value_type &value) {
const auto it = find(value);
return {it, it + !(it == end())};
}
/*! @copydoc equal_range */
[[nodiscard]] stl::pair<const_iterator, const_iterator> equal_range(const value_type &value) const {
const auto it = find(value);
return {it, it + !(it == cend())};
}
/**
* @brief Returns a range containing all elements that compare _equivalent_
* to a given value.
* @param value Value of an element to search for.
* @return A pair of iterators pointing to the first element and past the
* last element of the range.
*/
[[nodiscard]] stl::pair<iterator, iterator> equal_range(const auto &value)
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
const auto it = find(value);
return {it, it + !(it == end())};
}
/*! @copydoc equal_range */
[[nodiscard]] stl::pair<const_iterator, const_iterator> equal_range(const auto &value) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
const auto it = find(value);
return {it, it + !(it == cend())};
}
/**
* @brief Checks if the container contains an element with a given value.
* @param value Value of an element to search for.
* @return True if there is such an element, false otherwise.
*/
[[nodiscard]] bool contains(const value_type &value) const {
return (find(value) != cend());
}
/**
* @brief Checks if the container contains an element that compares
* _equivalent_ to a given value.
* @param value Value of an element to search for.
* @return True if there is such an element, false otherwise.
*/
[[nodiscard]] bool contains(const auto &value) const
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
return (find(value) != cend());
}
/**
* @brief Returns an iterator to the beginning of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the beginning of the given bucket.
*/
[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
return {packed.first().data(), sparse.first()[index]};
}
/**
* @brief Returns an iterator to the beginning of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the beginning of the given bucket.
*/
[[nodiscard]] const_local_iterator begin(const size_type index) const {
return cbegin(index);
}
/**
* @brief Returns an iterator to the beginning of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the beginning of the given bucket.
*/
[[nodiscard]] local_iterator begin(const size_type index) {
return {packed.first().data(), sparse.first()[index]};
}
/**
* @brief Returns an iterator to the end of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
return {};
}
/**
* @brief Returns an iterator to the end of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] const_local_iterator end(const size_type index) const {
return cend(index);
}
/**
* @brief Returns an iterator to the end of a given bucket.
* @param index An index of a bucket to access.
* @return An iterator to the end of the given bucket.
*/
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
return {};
}
/**
* @brief Returns the number of buckets.
* @return The number of buckets.
*/
[[nodiscard]] size_type bucket_count() const {
return sparse.first().size();
}
/**
* @brief Returns the maximum number of buckets.
* @return The maximum number of buckets.
*/
[[nodiscard]] size_type max_bucket_count() const {
return sparse.first().max_size();
}
/**
* @brief Returns the number of elements in a given bucket.
* @param index The index of the bucket to examine.
* @return The number of elements in the given bucket.
*/
[[nodiscard]] size_type bucket_size(const size_type index) const {
return static_cast<size_type>(stl::distance(begin(index), end(index)));
}
/**
* @brief Returns the bucket for a given element.
* @param value The value of the element to examine.
* @return The bucket for the given element.
*/
[[nodiscard]] size_type bucket(const value_type &value) const {
return value_to_bucket(value);
}
/**
* @brief Returns the average number of elements per bucket.
* @return The average number of elements per bucket.
*/
[[nodiscard]] float load_factor() const {
return static_cast<float>(size()) / static_cast<float>(bucket_count());
}
/**
* @brief Returns the maximum average number of elements per bucket.
* @return The maximum average number of elements per bucket.
*/
[[nodiscard]] float max_load_factor() const {
return threshold;
}
/**
* @brief Sets the desired maximum average number of elements per bucket.
* @param value A desired maximum average number of elements per bucket.
*/
void max_load_factor(const float value) {
ENTT_ASSERT(value > 0.f, "Invalid load factor");
threshold = value;
rehash(0u);
}
/**
* @brief Reserves at least the specified number of buckets and regenerates
* the hash table.
* @param cnt New number of buckets.
*/
void rehash(const size_type cnt) {
auto value = cnt > minimum_capacity ? cnt : minimum_capacity;
const auto cap = static_cast<size_type>(static_cast<float>(size()) / max_load_factor());
value = value > cap ? value : cap;
if(const auto sz = stl::bit_ceil(value); sz != bucket_count()) {
sparse.first().resize(sz);
for(auto &&elem: sparse.first()) {
elem = placeholder_position;
}
for(size_type pos{}, last = size(); pos < last; ++pos) {
const auto index = value_to_bucket(packed.first()[pos].second);
packed.first()[pos].first = stl::exchange(sparse.first()[index], pos);
}
}
}
/**
* @brief Reserves space for at least the specified number of elements and
* regenerates the hash table.
* @param cnt New number of elements.
*/
void reserve(const size_type cnt) {
packed.first().reserve(cnt);
rehash(static_cast<size_type>(stl::ceil(static_cast<float>(cnt) / max_load_factor())));
}
/**
* @brief Returns the function used to hash the elements.
* @return The function used to hash the elements.
*/
[[nodiscard]] hasher hash_function() const {
return sparse.second();
}
/**
* @brief Returns the function used to compare elements for equality.
* @return The function used to compare elements for equality.
*/
[[nodiscard]] key_equal key_eq() const {
return packed.second();
}
private:
compressed_pair<sparse_container_type, hasher> sparse;
compressed_pair<packed_container_type, key_equal> packed;
float threshold{default_threshold};
};
} // namespace entt
#endif

View File

@@ -0,0 +1,38 @@
#ifndef ENTT_CONTAINER_FWD_HPP
#define ENTT_CONTAINER_FWD_HPP
#include "../stl/functional.hpp"
#include "../stl/memory.hpp"
#include "../stl/utility.hpp"
#include "../stl/vector.hpp"
namespace entt {
template<
typename Key,
typename Type,
typename = stl::hash<Key>,
typename = stl::equal_to<>,
typename = stl::allocator<stl::pair<const Key, Type>>>
class dense_map;
template<
typename Type,
typename = stl::hash<Type>,
typename = stl::equal_to<>,
typename = stl::allocator<Type>>
class dense_set;
template<typename...>
class basic_table;
/**
* @brief Alias declaration for the most common use case.
* @tparam Type Element types.
*/
template<typename... Type>
using table = basic_table<stl::vector<Type>...>;
} // namespace entt
#endif

Some files were not shown because too many files have changed in this diff Show More