mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 09:47:01 +08:00
feature: creature (#38)
* build(deps): add assimp library as dependency * feat(render): add model loading and rendering pipeline * refactor(render): rename depth player shaders to depth model and clean up * build: add EnTT library * feat(entity): add ECS-based entity rendering * feat(render): add shadow pass for entity models * refactor(render): unify model and player rendering with single shader pipeline * refactor(collision): convert AABB to center-half representation and migrate player data to ECS * refactor(gameplay): add base classes Chunk and World for shared logic * 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 * fix(gameplay): correct hitbox insertion in HitboxManager::load * build: remove entt library * 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. * 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 * refactor(gameplay): extract movement, gravity, orientation, and walk pose into structs * 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. * feat(player): split max speed into horizontal/vertical, set spawn pos * refactor: move movement logic into SpeedSystem and Entity components * 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. * 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]] * build: add entt library * refactor(gameplay): rename ClientPlayer to LocalPlayer * refactor(gameplay): separate player logic into manager and ECS components * feat(gameplay): add entity managers and refactor to ECS components * refactor(gameplay): replace client thread with timer-based system and add entity manager * feat(gameplay): implement entity system with concurrent task handling and fix model loading * feat(gameplay): add entity destruction support * 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. * feat(gameplay): implement entity update packets Add S2CEntityUpdate to sync entity positions from server to client, including update handling in the client entity manager and server-side AI updates. * feat(ai): add wander AI system with move boost Add AIBase and WanderAITag components, a WanderAISystem, and a MoveBoost component to control wandering behavior. Replace the old MoveState with MoveBoost and include a horizontal random direction helper. * refactor(ecs): move entity physics to tick-based systems Update PhysicalSystem and SpeedSystem to operate on entt::registry instead of individual components. Add TickVelocity for server creatures so movement is calculated per tick without frame delta time. LocalPlayer now implements its own client-side physics with collision detection. * feat(gameplay): add server-side entity movement and AI Refactor LocalPlayer::update_physical to operate on a passed position, and run speed, physical, and wander AI systems in ServerEntityManager. * feat(gameplay): add pig wander AI and refine speed physics * feat(server): send entity position updates to all sessions * fix: correct velocity clamping and hitbox loading Preserve sign when clamping velocity; negative velocities now clamp to zero instead of flipping direction. Support loading hitbox definitions from JSON arrays and objects. Tune pig entity movement, spawn height, and wander probabilities. * feat(creatures): refine pig movement physics Add movement constants for pigs and switch deceleration to per-axis friction so creatures stop naturally. Extend wander boost duration for smoother behavior. * feat(physics): implement step-up for horizontal collisions * feat: sync entity direction and rotate models accordingly Move direction into the transform component and include it in server-to-client entity updates. Client now sets transform direction from the network message and uses it to compute yaw for model rotation, so entities visually face their movement direction. Refactor net_utils to support arbitrary Vec3 fields. * feat(render): interpolate entity transforms for rendering Add RenderTransform component to smooth position/direction updates and use it in entity and shadow passes. * fix(server_world): send time before entity updates * build: replace nlohmann json with rapidjson * refactor(json): migrate from nlohmann to rapidjson Replace nlohmann::json with rapidjson across localization, hitbox manager, server world, and sensitive filter. Add json_utils helper for converting rapidjson documents to maps, and improve parse error handling. * feat(item): add item manager with JSON asset loading Implement ItemManager to load and query item definitions from assets/item JSON files. Add ItemData struct, item asset metadata, CMake source registration, and initialize the manager during app startup. Also add AGENTS.md repository guidelines. * refactor(item): decouple items from block types The item system now supports an ItemKind and property, allowing item JSON to declare a type instead of assuming every item is a block. BlockManager was moved to its own header, redesigned around concurrent hash maps, and exposes id_from_name(). Texture and UI code now key items by ItemID, while block placement resolves the block type from the item registry. * fix(gameplay): use inline const for static EMPTY members Use inline const for static EMPTY members to avoid ODR and linker issues. Add missing block_manager.hpp includes in gameplay sources. * fix: stabilize block item registration and display Store item names as owned strings in ItemManager, set block type property for block items, validate item kind before placement, and handle items without textures gracefully in inventory UI. * feat: add pig spawn egg item Add pig spawn egg asset and texture, parse spawn egg item kind and creature property, and spawn the configured entity when used on an empty block. Also fix item texture loading to use actual image dimensions and update selected item UI sizing. * refactor(gameplay): update systems per entity with chunk check Refactor server entity update flow to process entities individually, skipping those whose chunk is not loaded. System update methods now accept a single entity instead of iterating the full registry view, and `ServerWorld::get_chunk_ref_count` is added to determine if an entity's chunk is active. * perf(render): skip entities outside loaded chunks * feat: add --direct-enter option to skip to world scene Support a new CLI flag that launches the app directly into the world scene. If no IP is provided, it starts a local server on the specified port and connects to 127.0.0.1; otherwise it connects to the given IP. With this flag enabled, a port must be specified via -p. Also expose SceneManager::push as public so scenes can be pushed immediately when bypassing the normal menu flow. * refactor(localization): switch block translations to item naming Remove the `name_key` field from block definitions and use item-based localization keys (`item.*.name`) for inventory display. Item data now stores the localized name at load time. * feat(gameplay): spawn creatures during chunk generation Add creature spawning to chunk generation with a configurable SpawnConfig, including a default pig spawn. Spawning occurs in the final generation phase and registers entities through the server world's entity manager. Also destroy entities when their chunk is unloaded to prevent orphaned entities and expose the entity manager from ServerWorld. * feat(gameplay): add run mode based thread pool config Introduce RunMode enum and thread pool sizing helpers for client, server, and hybrid modes. Add compute pool to server world and pass mode through server/client initialization. * perf(server): parallelize entity update loop Use parallel_do to process entities concurrently via the compute pool. Switch get_all_session to a thread-safe tbb::concurrent_vector and make ChunkEntity's ref_count atomic to avoid data races. Also remove the debug pig spawn from world init. * feat(creatures): animate creature walk cycles Add a Gait component synchronized over the network and procedural animation for model nodes. Load animation parameters from assets/model/creature/pig/animation.json and apply leg swing, body bob, and head motion based on gait. Refactor Gait into its own header for reuse. * fix(gameplay): make entity interpolation frame-rate independent * perf(render): batch model rendering with instancing Replaces per-entity draw calls with instanced rendering for both main and shadow passes. Each model's node hierarchy is flattened once and instance matrices are updated per frame, reducing draw calls and CPU overhead. * refactor(render): build entity instance buffers once per frame Move instance matrix upload into build_vertices and use a precomputed instance data map for both shadow and color passes, avoiding duplicate GPU buffer updates. * perf(gameplay): batch entity updates into single packet Aggregate per-entity update messages into S2CEntityUpdateBatch and broadcast once per tick. Reuse serialized packets in several broadcast loops to avoid repeated make_packet calls. * fix: correct thread count and shadow projection uniform - Add reserved client thread count to client threads in server generation calculation. - Set projection matrix uniform only for non-shadow rendering. * feat(ecs): add entity type separation and creature limits Replace generic entity creation with typed creatures and items. Rename `add_entity` to `add_creature`, enforce per-player creature cap, and track entity/creature totals for metrics. Expose run mode to dev panel and show new counts. * perf(render): frustum cull entities before instance building Add frustum culling to entity instance data collection using AABB vs camera frustum planes. Pass renderer into get_instances_data_map to compute MVP and extract planes. Also add "Rendered Entities" debug counter for visibility. * feat(audio): add ambient pig sounds Add pig call audio with randomized timing, triggered by proximity to the player as 3D positional sound. * feat(client-entity): use snapshot history for entity interpolation Replace exponential smoothing with a buffered snapshot system. Each entity keeps a deque of position/direction snapshots with timestamps; the render transform is interpolated at a fixed 100 ms render delay to hide network jitter. Snapshots are capped to 16 entries. * refactor(hitbox): move player hitbox definition to JSON asset * feat(client_player): use snapshot interpolation for remote players Replace exponential position smoothing with snapshot interpolation. Remote player transforms are rendered from a delayed snapshot history, using linear interpolation for position and shortest-path interpolation for yaw/pitch. A 100 ms render delay compensates for network tick rate.
This commit is contained in:
251
AGENTS.md
Normal file
251
AGENTS.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# Code Modification Guidelines
|
||||
|
||||
## 1. Git Branch Protection
|
||||
|
||||
- Never modify code directly on the `main` branch.
|
||||
- Before editing, check the current branch:
|
||||
|
||||
```bash
|
||||
git branch --show-current
|
||||
````
|
||||
|
||||
* If the result is `main`, create or switch to a development branch first:
|
||||
|
||||
```bash
|
||||
git checkout -b feature/xxx
|
||||
```
|
||||
|
||||
* Do not bypass branch protection by:
|
||||
|
||||
* Committing directly to `main`.
|
||||
* Running code generation, formatting, or large refactors on `main`.
|
||||
* Rewriting or damaging `main` history.
|
||||
|
||||
---
|
||||
|
||||
## 2. Code Comments
|
||||
|
||||
* All new comments must be written in English.
|
||||
* Comments must be:
|
||||
|
||||
* Short and clear.
|
||||
* Explain the purpose, not repeat the code.
|
||||
* Avoid unnecessary details.
|
||||
|
||||
Recommended:
|
||||
|
||||
```cpp
|
||||
// AI-generated: Prevent stale handle access.
|
||||
```
|
||||
|
||||
Avoid:
|
||||
|
||||
```cpp
|
||||
// AI-generated: This checks if the handle is invalid because...
|
||||
```
|
||||
|
||||
* Important AI-added or modified logic should include:
|
||||
|
||||
```cpp
|
||||
// AI-generated
|
||||
```
|
||||
|
||||
* Do not add comments for trivial changes such as:
|
||||
|
||||
* One or two line logic fixes.
|
||||
* Spelling corrections.
|
||||
* Simple variable renames.
|
||||
|
||||
### 2.1 Do not write excessive or granular comments
|
||||
|
||||
AI must avoid sprinkling small explanatory comments throughout code.
|
||||
Specifically:
|
||||
|
||||
* **No line-by-line narration.** Do not add a comment above (or beside)
|
||||
every field, signal, slot, branch, or block just to restate what it is.
|
||||
If the name is self-explanatory, no comment is needed.
|
||||
* **No multi-part explanations on a single marker.** A line like
|
||||
`// AI-generated: abort the in-flight download; finished handler clears state.`
|
||||
is too much. Either drop it, or shorten to a single short clause
|
||||
(e.g. `// AI-generated: abort in-flight download.`).
|
||||
* **Prefer zero or one comment per file/section**, not many. A short
|
||||
header comment explaining the file's purpose is acceptable; dozens of
|
||||
inline labels are not.
|
||||
* **Do not comment obvious QML/C++ bindings, layout splits, or default
|
||||
values** (e.g. `// AI-generated: index 0 -> zh_CN, index 1 -> en.`).
|
||||
The code already says that.
|
||||
* **When in doubt, omit the comment.** A missing comment is fine; a
|
||||
redundant one is noise that future maintainers must clean up.
|
||||
|
||||
Rule of thumb: if removing the comment would not lose useful
|
||||
information, remove it.
|
||||
|
||||
---
|
||||
|
||||
## 3. Project Style
|
||||
|
||||
Follow the existing project style strictly:
|
||||
|
||||
* Naming conventions.
|
||||
* File organization.
|
||||
* Formatting rules.
|
||||
* Include order.
|
||||
* Existing architecture.
|
||||
|
||||
Do not:
|
||||
|
||||
* Introduce unrelated coding styles.
|
||||
* Add unnecessary design patterns.
|
||||
* Create duplicate systems.
|
||||
* Add abstractions without need.
|
||||
* Change module responsibilities.
|
||||
|
||||
---
|
||||
|
||||
## 4. Modification Scope
|
||||
|
||||
Only modify the minimum code required.
|
||||
|
||||
Do not:
|
||||
|
||||
* Format unrelated files.
|
||||
* Remove existing comments.
|
||||
* Rename large numbers of symbols.
|
||||
* Change public APIs.
|
||||
* Perform large refactors without confirmation.
|
||||
|
||||
If large changes are required, explain first:
|
||||
|
||||
1. Current problem.
|
||||
2. Required changes.
|
||||
3. Affected modules.
|
||||
4. Expected impact.
|
||||
|
||||
---
|
||||
|
||||
## 5. Build and Test
|
||||
|
||||
After modifications:
|
||||
|
||||
* Verify compilation.
|
||||
* Check for new warnings.
|
||||
* Ensure existing features still work.
|
||||
|
||||
Do not:
|
||||
|
||||
* Commit without verification.
|
||||
* Ignore compiler errors.
|
||||
* Hide problems with temporary hacks.
|
||||
|
||||
---
|
||||
|
||||
## 6. Compatibility
|
||||
|
||||
Consider:
|
||||
|
||||
* Existing callers.
|
||||
* API/ABI compatibility.
|
||||
* Serialization formats.
|
||||
* Network protocols.
|
||||
* Save data.
|
||||
* Threading impact.
|
||||
|
||||
Do not:
|
||||
|
||||
* Change network structures without compatibility handling.
|
||||
* Break old data formats.
|
||||
* Change public data layouts carelessly.
|
||||
|
||||
---
|
||||
|
||||
## 7. Multi-threading Safety
|
||||
|
||||
For multi-threaded code, check:
|
||||
|
||||
* Data races.
|
||||
* Object lifetime.
|
||||
* Lock contention.
|
||||
* Atomic correctness.
|
||||
* Cross-thread resource access.
|
||||
|
||||
Do not:
|
||||
|
||||
* Assume single-threaded execution.
|
||||
* Modify shared data without protection.
|
||||
* Add hidden locks that hurt performance.
|
||||
|
||||
---
|
||||
|
||||
## 8. Memory Safety
|
||||
|
||||
Consider:
|
||||
|
||||
* Ownership.
|
||||
* Lifetime.
|
||||
* RAII.
|
||||
* Memory leaks.
|
||||
* Dangling references.
|
||||
* Iterator invalidation.
|
||||
|
||||
Do not:
|
||||
|
||||
* Return references to local variables.
|
||||
* Store pointers to temporary objects.
|
||||
* Use uninitialized data.
|
||||
* Introduce undefined behavior.
|
||||
|
||||
---
|
||||
|
||||
## 9. Third-party Libraries
|
||||
|
||||
Before adding or changing dependencies:
|
||||
|
||||
Check:
|
||||
|
||||
* Existing project dependencies.
|
||||
* Build system impact.
|
||||
* Maintenance cost.
|
||||
|
||||
Do not:
|
||||
|
||||
* Add large libraries for simple features.
|
||||
* Duplicate existing library functionality.
|
||||
* Modify third-party source code.
|
||||
|
||||
---
|
||||
|
||||
## 10. AI Behavior Rules
|
||||
|
||||
AI-generated code must:
|
||||
|
||||
* Reuse existing code first.
|
||||
* Preserve current architecture.
|
||||
* Prefer simple solutions.
|
||||
* Minimize risk and code changes.
|
||||
|
||||
Do not:
|
||||
|
||||
* Guess requirements.
|
||||
* Add unrequested features.
|
||||
* Redesign architecture.
|
||||
* Add unnecessary abstraction layers.
|
||||
|
||||
Choose the implementation with:
|
||||
|
||||
> The smallest change, lowest risk, and best compatibility with existing code.
|
||||
|
||||
---
|
||||
|
||||
## 11. Pre-commit Checklist
|
||||
|
||||
Before committing:
|
||||
|
||||
* [ ] Not on `main`.
|
||||
* [ ] Changes match the requested task.
|
||||
* [ ] New comments are in English.
|
||||
* [ ] AI changes are marked when needed.
|
||||
* [ ] No unrelated formatting changes.
|
||||
* [ ] Build passes.
|
||||
* [ ] No obvious performance/security/thread issues.
|
||||
* [ ] No API, protocol, or data format breakage.
|
||||
|
||||
@@ -148,6 +148,7 @@ target_link_libraries(${PROJECT_NAME}
|
||||
OpenAL::OpenAL
|
||||
harfbuzz::harfbuzz
|
||||
Opus::Opus
|
||||
assimp::assimp
|
||||
$<$<PLATFORM_ID:Windows>:ws2_32>
|
||||
|
||||
)
|
||||
|
||||
@@ -8,5 +8,4 @@ is_passable = true
|
||||
is_transitional = false
|
||||
is_transparent = true
|
||||
name = 'air'
|
||||
name_key = 'block.air'
|
||||
roughness = 1.0
|
||||
@@ -8,5 +8,4 @@ is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'dirt'
|
||||
name_key = 'block.dirt'
|
||||
roughness = 1.0
|
||||
@@ -8,5 +8,4 @@ is_passable = true
|
||||
is_transitional = false
|
||||
is_transparent = true
|
||||
name = 'grass'
|
||||
name_key = 'block.grass'
|
||||
roughness = 0.90000000000000002
|
||||
@@ -8,5 +8,4 @@ is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'grass_block'
|
||||
name_key = 'block.grass_block'
|
||||
roughness = 0.90000000000000002
|
||||
@@ -8,5 +8,4 @@ is_passable = false
|
||||
is_transitional = false
|
||||
is_transparent = true
|
||||
name = 'leaf'
|
||||
name_key = 'block.leaf'
|
||||
roughness = 0.69999999999999996
|
||||
@@ -8,5 +8,4 @@ is_passable = false
|
||||
is_transitional = false
|
||||
is_transparent = false
|
||||
name = 'log'
|
||||
name_key = 'block.log'
|
||||
roughness = 0.69999999999999996
|
||||
@@ -8,5 +8,4 @@ is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'sand'
|
||||
name_key = 'block.sand'
|
||||
roughness = 0.80000000000000004
|
||||
@@ -8,5 +8,4 @@ is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'snowy_grass_block'
|
||||
name_key = 'block.snowy_grass_block'
|
||||
roughness = 0.90000000000000002
|
||||
@@ -8,5 +8,4 @@ is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'stone'
|
||||
name_key = 'block.stone'
|
||||
roughness = 0.75
|
||||
@@ -9,4 +9,3 @@ is_discard = false
|
||||
is_blend = false
|
||||
is_transitional = false
|
||||
roughness = 1.0
|
||||
name_key = "block.template"
|
||||
|
||||
@@ -8,5 +8,4 @@ is_passable = true
|
||||
is_transitional = false
|
||||
is_transparent = true
|
||||
name = 'water'
|
||||
name_key = 'block.water'
|
||||
roughness = 0.02
|
||||
6
assets/item/air.json
Normal file
6
assets/item/air.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"id": 0,
|
||||
"name": "air",
|
||||
"description": "",
|
||||
"type": "block"
|
||||
}
|
||||
7
assets/item/dirt.json
Normal file
7
assets/item/dirt.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": 2,
|
||||
"name": "dirt",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/dirt.png",
|
||||
"type": "block"
|
||||
}
|
||||
7
assets/item/grass.json
Normal file
7
assets/item/grass.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": 9,
|
||||
"name": "grass",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/grass.png",
|
||||
"type": "block"
|
||||
}
|
||||
7
assets/item/grass_block.json
Normal file
7
assets/item/grass_block.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": 1,
|
||||
"name": "grass_block",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/grass_block.png",
|
||||
"type": "block"
|
||||
}
|
||||
7
assets/item/leaf.json
Normal file
7
assets/item/leaf.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": 6,
|
||||
"name": "leaf",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/leaf.png",
|
||||
"type": "block"
|
||||
}
|
||||
7
assets/item/log.json
Normal file
7
assets/item/log.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": 5,
|
||||
"name": "log",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/log.png",
|
||||
"type": "block"
|
||||
}
|
||||
8
assets/item/pig_spawn_egg.json
Normal file
8
assets/item/pig_spawn_egg.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": 10,
|
||||
"name": "pig_spawn_egg",
|
||||
"description": "",
|
||||
"texture": "texture/item/spawn_egg/pig_spawn_egg.png",
|
||||
"type": "spawn_egg",
|
||||
"creature": "cubed:pig"
|
||||
}
|
||||
7
assets/item/sand.json
Normal file
7
assets/item/sand.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": 4,
|
||||
"name": "sand",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/sand.png",
|
||||
"type": "block"
|
||||
}
|
||||
7
assets/item/snowy_grass_block.json
Normal file
7
assets/item/snowy_grass_block.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": 8,
|
||||
"name": "snowy_grass_block",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/snowy_grass_block.png",
|
||||
"type": "block"
|
||||
}
|
||||
7
assets/item/stone.json
Normal file
7
assets/item/stone.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": 3,
|
||||
"name": "stone",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/stone.png",
|
||||
"type": "block"
|
||||
}
|
||||
7
assets/item/water.json
Normal file
7
assets/item/water.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": 7,
|
||||
"name": "water",
|
||||
"description": "",
|
||||
"texture": "texture/item/block/water.png",
|
||||
"type": "block"
|
||||
}
|
||||
@@ -36,14 +36,15 @@
|
||||
"joingame.server_ip": "Server Ip",
|
||||
"joingame.join_world": "Join World",
|
||||
"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"
|
||||
"item.air.name": "air",
|
||||
"item.dirt.name": "dirt",
|
||||
"item.grass_block.name": "grass block",
|
||||
"item.grass.name": "grass",
|
||||
"item.leaf.name": "leaf",
|
||||
"item.log.name": "log",
|
||||
"item.sand.name": "sand",
|
||||
"item.snowy_grass_block.name": "snowy grass block",
|
||||
"item.stone.name": "stone",
|
||||
"item.water.name": "water",
|
||||
"item.pig_spawn_egg.name": "pig spawn egg"
|
||||
}
|
||||
@@ -36,14 +36,15 @@
|
||||
"joingame.server_ip": "服务器IP",
|
||||
"joingame.join_world": "加入世界",
|
||||
"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": "水"
|
||||
"item.air.name": "空气",
|
||||
"item.dirt.name": "泥土",
|
||||
"item.grass_block.name": "草方块",
|
||||
"item.grass.name": "草",
|
||||
"item.leaf.name": "树叶",
|
||||
"item.log.name": "原木",
|
||||
"item.sand.name": "沙子",
|
||||
"item.snowy_grass_block.name": "覆雪草方块",
|
||||
"item.stone.name": "石头",
|
||||
"item.water.name": "水",
|
||||
"item.pig_spawn_egg.name": "猪猪刷怪蛋"
|
||||
}
|
||||
33
assets/model/creature/pig/animation.json
Normal file
33
assets/model/creature/pig/animation.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"walk": {
|
||||
"speed": 6.0,
|
||||
"amplitude": 25.0
|
||||
},
|
||||
"run": {
|
||||
"speed": 12.0,
|
||||
"amplitude": 40.0
|
||||
},
|
||||
"body_bob": 0.03,
|
||||
"head": {
|
||||
"node": "Head",
|
||||
"amplitude": 4.0
|
||||
},
|
||||
"legs": [
|
||||
{
|
||||
"node": "Foot_FL",
|
||||
"phase": 0.0
|
||||
},
|
||||
{
|
||||
"node": "Foot_FR",
|
||||
"phase": 3.14159
|
||||
},
|
||||
{
|
||||
"node": "Foot_HL",
|
||||
"phase": 3.14159
|
||||
},
|
||||
{
|
||||
"node": "Foot_HR",
|
||||
"phase": 0.0
|
||||
}
|
||||
]
|
||||
}
|
||||
16
assets/model/creature/pig/collision.json
Normal file
16
assets/model/creature/pig/collision.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"boxes": [
|
||||
{
|
||||
"center": [
|
||||
0,
|
||||
0.78,
|
||||
0
|
||||
],
|
||||
"half": [
|
||||
0.53,
|
||||
0.78,
|
||||
0.406
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/model/creature/pig/pig.glb
Normal file
BIN
assets/model/creature/pig/pig.glb
Normal file
Binary file not shown.
16
assets/model/creature/player/collision.json
Normal file
16
assets/model/creature/player/collision.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"boxes": [
|
||||
{
|
||||
"center": [
|
||||
0,
|
||||
0.9,
|
||||
0
|
||||
],
|
||||
"half": [
|
||||
0.3,
|
||||
0.9,
|
||||
0.3
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/model/creature/player/player.glb
Normal file
BIN
assets/model/creature/player/player.glb
Normal file
Binary file not shown.
13
assets/shaders/depth_model_instance_vert.glsl
Normal file
13
assets/shaders/depth_model_instance_vert.glsl
Normal file
@@ -0,0 +1,13 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec3 pos;
|
||||
layout (location = 1) in vec2 texCoord;
|
||||
layout (location = 3) in mat4 modelMatrix;
|
||||
uniform mat4 lightSpaceMatrix;
|
||||
|
||||
out vec2 tc;
|
||||
|
||||
void main() {
|
||||
tc = texCoord;
|
||||
gl_Position = lightSpaceMatrix * modelMatrix * vec4(pos, 1.0);
|
||||
}
|
||||
@@ -7,7 +7,6 @@ uniform mat4 lightSpaceMatrix;
|
||||
uniform mat4 modelMatrix;
|
||||
|
||||
out vec2 tc;
|
||||
flat out int tex_layer;
|
||||
|
||||
void main() {
|
||||
tc = texCoord;
|
||||
14
assets/shaders/depth_model_vert.glsl
Normal file
14
assets/shaders/depth_model_vert.glsl
Normal file
@@ -0,0 +1,14 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec3 pos;
|
||||
layout (location = 1) in vec2 texCoord;
|
||||
|
||||
uniform mat4 lightSpaceMatrix;
|
||||
uniform mat4 modelMatrix;
|
||||
|
||||
out vec2 tc;
|
||||
|
||||
void main() {
|
||||
tc = texCoord;
|
||||
gl_Position = lightSpaceMatrix * modelMatrix * vec4(pos, 1.0);
|
||||
}
|
||||
26
assets/shaders/model_instance_vert.glsl
Normal file
26
assets/shaders/model_instance_vert.glsl
Normal file
@@ -0,0 +1,26 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec3 pos;
|
||||
layout (location = 1) in vec2 texCoord;
|
||||
layout (location = 2) in vec3 aNormal;
|
||||
layout (location = 3) in mat4 modelMatrix;
|
||||
uniform mat4 view_matrix;
|
||||
uniform mat4 proj_matrix;
|
||||
uniform mat4 lightSpaceMatrix;
|
||||
out vec4 FragPosLightSpace;
|
||||
out vec3 normal;
|
||||
out vec2 tc;
|
||||
out vec3 vert_pos;
|
||||
|
||||
|
||||
void main() {
|
||||
vec4 worldPos = modelMatrix * vec4(pos, 1.0);
|
||||
FragPosLightSpace = lightSpaceMatrix * worldPos;
|
||||
mat4 mv_matrix= view_matrix * modelMatrix;
|
||||
mat4 norm_matrix = transpose(inverse(mv_matrix));
|
||||
vec4 viewPos = mv_matrix * vec4(pos, 1.0);
|
||||
tc = texCoord;
|
||||
vert_pos = pos;
|
||||
normal = normalize(mat3(norm_matrix) * aNormal);
|
||||
gl_Position = proj_matrix * viewPos;
|
||||
}
|
||||
@@ -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;
|
||||
BIN
assets/sound/creature/pig/call.mp3
Normal file
BIN
assets/sound/creature/pig/call.mp3
Normal file
Binary file not shown.
BIN
assets/texture/item/spawn_egg/pig_spawn_egg.png
Normal file
BIN
assets/texture/item/spawn_egg/pig_spawn_egg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 174 B |
@@ -10,7 +10,7 @@ find_package(harfbuzz REQUIRED)
|
||||
find_package(Freetype REQUIRED)
|
||||
find_package(SDL3 REQUIRED)
|
||||
find_package(Opus REQUIRED)
|
||||
|
||||
find_package(assimp REQUIRED)
|
||||
# Third-party libraries
|
||||
|
||||
if (WIN32)
|
||||
|
||||
@@ -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
|
||||
@@ -49,7 +49,6 @@ private:
|
||||
Window m_window;
|
||||
|
||||
TextureManager m_texture_manager;
|
||||
|
||||
AudioEngine m_audio;
|
||||
|
||||
Renderer m_renderer;
|
||||
|
||||
@@ -13,5 +13,6 @@ struct Argument {
|
||||
std::optional<int> log_level;
|
||||
std::optional<bool> enable_filelog;
|
||||
std::optional<bool> enable_consolelog;
|
||||
std::optional<bool> direct_enter;
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -42,7 +42,6 @@ struct LookBlock {
|
||||
|
||||
struct BlockData {
|
||||
std::string name;
|
||||
std::string name_key;
|
||||
BlockType id = 0;
|
||||
|
||||
bool is_liquid = false;
|
||||
@@ -56,42 +55,14 @@ struct BlockData {
|
||||
bool is_blend = false;
|
||||
bool is_transitional = false;
|
||||
float roughness = 1.0f;
|
||||
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),
|
||||
BlockData(std::string_view b_name, 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), 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) {}
|
||||
};
|
||||
|
||||
class BlockManager {
|
||||
|
||||
public:
|
||||
static const std::vector<BlockData>& datas();
|
||||
static void init();
|
||||
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);
|
||||
|
||||
static bool is_cross_plane(BlockType id);
|
||||
static bool is_transparent(BlockType id);
|
||||
static bool is_passable(BlockType id);
|
||||
|
||||
static bool is_discard(BlockType id);
|
||||
static bool is_blend(BlockType id);
|
||||
static bool is_transitional(BlockType id);
|
||||
static float roughness(BlockType id);
|
||||
static BlockType cross_plane_index(BlockType id);
|
||||
|
||||
private:
|
||||
static void set_up_cross_plane_map();
|
||||
static inline std::vector<BlockData> m_datas;
|
||||
static inline bool is_init = false;
|
||||
static inline std::unordered_map<BlockType, BlockType> m_cross_plane_map;
|
||||
BlockData() { name = ""; }
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
45
include/Cubed/gameplay/block_manager.hpp
Normal file
45
include/Cubed/gameplay/block_manager.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
namespace Cubed {
|
||||
class BlockManager {
|
||||
|
||||
public:
|
||||
static void init();
|
||||
static unsigned sums();
|
||||
static unsigned cross_plane_sum();
|
||||
static const std::string& name_form_id(BlockType id);
|
||||
static bool is_gas(BlockType id);
|
||||
static bool is_liquid(BlockType id);
|
||||
|
||||
static bool is_cross_plane(BlockType id);
|
||||
static bool is_transparent(BlockType id);
|
||||
static bool is_passable(BlockType id);
|
||||
|
||||
static bool is_discard(BlockType id);
|
||||
static bool is_blend(BlockType id);
|
||||
static bool is_transitional(BlockType id);
|
||||
static float roughness(BlockType id);
|
||||
static BlockType cross_plane_index(BlockType id);
|
||||
|
||||
static BlockType id_from_name(const std::string& name);
|
||||
|
||||
private:
|
||||
using BlockMap = tbb::concurrent_hash_map<BlockType, BlockData>;
|
||||
using acc = BlockMap::accessor;
|
||||
using cacc = BlockMap::const_accessor;
|
||||
using IDMap = tbb::concurrent_hash_map<std::string, BlockType>;
|
||||
using CrossPlaneMap = tbb::concurrent_hash_map<BlockType, BlockType>;
|
||||
|
||||
static inline const BlockData EMPTY;
|
||||
|
||||
static inline BlockMap m_datas;
|
||||
static inline IDMap m_id_map;
|
||||
static inline bool is_init = false;
|
||||
static inline CrossPlaneMap m_cross_plane_map;
|
||||
static void set_up_cross_plane_map(
|
||||
const std::vector<std::pair<bool, BlockType>>& types);
|
||||
};
|
||||
} // namespace Cubed
|
||||
27
include/Cubed/gameplay/chunk.hpp
Normal file
27
include/Cubed/gameplay/chunk.hpp
Normal 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
|
||||
@@ -50,6 +50,8 @@ public:
|
||||
void generate_cave();
|
||||
void generate_river();
|
||||
|
||||
void spawn_creature();
|
||||
|
||||
private:
|
||||
static inline std::atomic<bool> is_init{false};
|
||||
static inline unsigned m_generator_seed{0};
|
||||
|
||||
@@ -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;
|
||||
|
||||
82
include/Cubed/gameplay/client_entity_manager.hpp
Normal file
82
include/Cubed/gameplay/client_entity_manager.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/entity.hpp"
|
||||
#include "Cubed/gameplay/gait.hpp"
|
||||
#include "Cubed/tools/cubed_random.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, UPDATE };
|
||||
|
||||
ClientEntityManager(ClientWorld& world);
|
||||
void update(float dt);
|
||||
void init();
|
||||
|
||||
void receive_entity_create(S2CEntityCreate& msg);
|
||||
void receive_entity_destory(EntityID id);
|
||||
void receive_entity_update(const S2CEntityUpdate& msg);
|
||||
void receive_entity_update(S2CEntityUpdateBatch& msg);
|
||||
void destory(EntityID id);
|
||||
void create(std::string_view name, const glm::vec3& pos);
|
||||
|
||||
const entt::registry& get_registry() const;
|
||||
|
||||
void player_sound(float dt);
|
||||
|
||||
private:
|
||||
struct EntityCreateElement {
|
||||
EntityID id;
|
||||
std::string name;
|
||||
glm::vec3 pos;
|
||||
};
|
||||
|
||||
struct UpdateInfo {
|
||||
EntityID id;
|
||||
glm::vec3 pos;
|
||||
glm::vec3 direction;
|
||||
Gait gait;
|
||||
};
|
||||
|
||||
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, UpdateInfo>;
|
||||
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;
|
||||
Random m_random;
|
||||
void handle_task(float dt);
|
||||
void handle_entity_destory(EntityID id);
|
||||
// not thread safe
|
||||
void handle_entity_create(EntityID id, std::string_view name,
|
||||
const glm::vec3& pos);
|
||||
void handle_entity_update(UpdateInfo& info, float dt);
|
||||
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
|
||||
@@ -1,167 +1,36 @@
|
||||
#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/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>
|
||||
#include "Cubed/gameplay/ecs/animation.hpp"
|
||||
#include "Cubed/gameplay/ecs/identity.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
|
||||
#include <deque>
|
||||
namespace Cubed {
|
||||
|
||||
class ClientWorld;
|
||||
class ClientPlayer {
|
||||
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>;
|
||||
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 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();
|
||||
|
||||
const ItemStack& get_current_itemstack() 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);
|
||||
|
||||
int selected_hotbar() const;
|
||||
void set_hotbar(int pos, const ItemStack& item);
|
||||
std::span<const ItemStack, HOTBAR_SUM> get_hotbar() 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_acceleration = DEFAULT_ACCELERATION;
|
||||
float m_deceleration = DEFAULT_DECELERATION;
|
||||
float m_g = DEFAULT_G;
|
||||
static constexpr float MAX_SPACE_ON_TIME = 0.3f;
|
||||
static constexpr 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;
|
||||
std::array<ItemStack, HOTBAR_SUM> m_hotbar;
|
||||
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;
|
||||
|
||||
int m_selected_hotbar = 0;
|
||||
|
||||
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 ClientPlayerSnapshot {
|
||||
double time_ms = 0.0f;
|
||||
glm::vec3 pos{0.0f};
|
||||
float yaw = 0.0f;
|
||||
float pitch = 0.0f;
|
||||
};
|
||||
struct ClientPlayerState {
|
||||
std::deque<ClientPlayerSnapshot> value;
|
||||
};
|
||||
struct ClientPlayer {
|
||||
Position pos{};
|
||||
EntityInfo entity{};
|
||||
WalkPose walk{};
|
||||
Orientation angle{};
|
||||
Position render_pos{};
|
||||
Orientation render_angle{};
|
||||
ClientPlayerState history{};
|
||||
};
|
||||
|
||||
struct PlayerRenderData {
|
||||
EntityInfo info{};
|
||||
Position render_pos{};
|
||||
Orientation angle{};
|
||||
Gait gait{};
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
45
include/Cubed/gameplay/client_player_manager.hpp
Normal file
45
include/Cubed/gameplay/client_player_manager.hpp
Normal 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
|
||||
@@ -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);
|
||||
std::shared_ptr<NetworkClient> client, RunMode mode);
|
||||
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,28 @@ 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;
|
||||
|
||||
bool is_render(const glm::vec3& pos) const;
|
||||
|
||||
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)),
|
||||
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)));
|
||||
}
|
||||
|
||||
@@ -136,7 +122,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;
|
||||
|
||||
@@ -147,16 +132,14 @@ private:
|
||||
|
||||
static constexpr int WORLD_EXIT_TIMEOUT = 200;
|
||||
static constexpr int MAX_UPLOAD_CHUNK_SUM = 16;
|
||||
ClientPlayer m_player;
|
||||
OtherPlayerHashMap m_player_info;
|
||||
std::atomic<RunMode> m_runmode = RunMode::HYBRID;
|
||||
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;
|
||||
@@ -166,9 +149,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};
|
||||
@@ -176,6 +157,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};
|
||||
@@ -187,10 +169,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);
|
||||
|
||||
13
include/Cubed/gameplay/creatures/pig.hpp
Normal file
13
include/Cubed/gameplay/creatures/pig.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
namespace Cubed {
|
||||
struct PigTag {};
|
||||
|
||||
namespace PigDefaults {
|
||||
constexpr float MAX_SPEED = 0.1f; // tiles/tick → 2 tiles/sec
|
||||
constexpr float ACCELERATION = 0.01f; // ~10 ticks (0.5s) to reach full speed
|
||||
constexpr float DECELERATION = 0.02f; // ~5 ticks (0.25s) to stop
|
||||
constexpr float GRAVITY = 1.0f; // ≈20 tiles/s², close to the player
|
||||
} // namespace PigDefaults
|
||||
|
||||
} // namespace Cubed
|
||||
20
include/Cubed/gameplay/creatures/spawn.hpp
Normal file
20
include/Cubed/gameplay/creatures/spawn.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/biome.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
namespace Cubed {
|
||||
struct SpawnConfig {
|
||||
std::string_view name; // factory key, e.g. "cubed:pig"
|
||||
std::span<const BiomeType> biomes; // allowed biomes
|
||||
float probability = 0.0f; // per chunk spawn probability
|
||||
unsigned max_spawn_count = 0; // per chunk_max_spawn_sum
|
||||
};
|
||||
|
||||
namespace SpawnDefaults {
|
||||
constexpr std::array<BiomeType, 2> PIG_BIOMES{BiomeType::PLAIN,
|
||||
BiomeType::FOREST};
|
||||
constexpr SpawnConfig PIG{"cubed:pig", PIG_BIOMES, 0.02f, 3};
|
||||
} // namespace SpawnDefaults
|
||||
} // namespace Cubed
|
||||
17
include/Cubed/gameplay/ecs/ai_struct.hpp
Normal file
17
include/Cubed/gameplay/ecs/ai_struct.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/game_time.hpp"
|
||||
namespace Cubed {
|
||||
struct AIBase {
|
||||
TickType interval = 1;
|
||||
TickType count = 0;
|
||||
};
|
||||
|
||||
struct WanderAITag {};
|
||||
|
||||
struct MoveBoost {
|
||||
TickType duration = 0;
|
||||
TickType count = 0;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
14
include/Cubed/gameplay/ecs/animation.hpp
Normal file
14
include/Cubed/gameplay/ecs/animation.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/gait.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
|
||||
31
include/Cubed/gameplay/ecs/client_entity.hpp
Normal file
31
include/Cubed/gameplay/ecs/client_entity.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/animation.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
#include "Cubed/gameplay/model.hpp"
|
||||
|
||||
#include <deque>
|
||||
namespace Cubed {
|
||||
struct BaseClientCreature {
|
||||
|
||||
Transform transform;
|
||||
|
||||
WalkPose pose;
|
||||
|
||||
ModelID model;
|
||||
};
|
||||
|
||||
struct SoundTime {
|
||||
float next_call_time;
|
||||
};
|
||||
|
||||
struct ClientEntitySnapshot {
|
||||
double time_ms = 0.0;
|
||||
glm::vec3 pos{0.0f};
|
||||
glm::vec3 dir{0.0f};
|
||||
};
|
||||
|
||||
struct ClientEntityState {
|
||||
std::deque<ClientEntitySnapshot> history;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
14
include/Cubed/gameplay/ecs/entity.hpp
Normal file
14
include/Cubed/gameplay/ecs/entity.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
namespace Cubed {
|
||||
using EntityID = uint64_t;
|
||||
|
||||
enum class EntityType { CREATURE, ITEM };
|
||||
|
||||
struct Entity {
|
||||
EntityID id;
|
||||
EntityType type;
|
||||
Entity(EntityID id, EntityType type) : id(id), type(type) {}
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
10
include/Cubed/gameplay/ecs/health.hpp
Normal file
10
include/Cubed/gameplay/ecs/health.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
struct Health {
|
||||
float hp = 20;
|
||||
float max_hp = 20;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
10
include/Cubed/gameplay/ecs/identity.hpp
Normal file
10
include/Cubed/gameplay/ecs/identity.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Cubed {
|
||||
struct EntityInfo {
|
||||
std::string name;
|
||||
std::string uuid;
|
||||
};
|
||||
} // namespace Cubed
|
||||
34
include/Cubed/gameplay/ecs/movement.hpp
Normal file
34
include/Cubed/gameplay/ecs/movement.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include "Cubed/constants.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
namespace Cubed {
|
||||
struct TickVelocity {
|
||||
|
||||
glm::vec3 value{0.0f};
|
||||
// blocks/tick!!! -1 for in
|
||||
glm::vec3 max{1.0f, -1.0f, 1.0f};
|
||||
};
|
||||
|
||||
struct Velocity {
|
||||
|
||||
glm::vec3 value{0.0f};
|
||||
// blocks/second!!!
|
||||
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
|
||||
22
include/Cubed/gameplay/ecs/server_entity.hpp
Normal file
22
include/Cubed/gameplay/ecs/server_entity.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/health.hpp"
|
||||
#include "Cubed/gameplay/ecs/movement.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
#include "Cubed/gameplay/hitbox.hpp"
|
||||
namespace Cubed {
|
||||
|
||||
struct BaseServerCreature {
|
||||
Transform transform{};
|
||||
|
||||
TickVelocity velocity{};
|
||||
|
||||
Movement movement{};
|
||||
|
||||
Gravity gravity{};
|
||||
|
||||
Health health{};
|
||||
|
||||
HitboxID hitbox{};
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
18
include/Cubed/gameplay/ecs/state.hpp
Normal file
18
include/Cubed/gameplay/ecs/state.hpp
Normal 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
|
||||
30
include/Cubed/gameplay/ecs/transform.hpp
Normal file
30
include/Cubed/gameplay/ecs/transform.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#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{};
|
||||
};
|
||||
|
||||
struct RenderTransform {
|
||||
Position position{};
|
||||
Orientation orientation{};
|
||||
Direction direction{};
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
namespace Cubed {
|
||||
enum class Gait { STOP = 0, WALK = 1, RUN = 2 };
|
||||
constexpr int get_gait_id(Gait gait) { return std::to_underlying(gait); }
|
||||
@@ -17,5 +18,4 @@ inline Gait get_gait_from_id(int id) {
|
||||
throw std::runtime_error("Unknown Gait");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
26
include/Cubed/gameplay/hitbox.hpp
Normal file
26
include/Cubed/gameplay/hitbox.hpp
Normal 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
|
||||
37
include/Cubed/gameplay/hitbox_manager.hpp
Normal file
37
include/Cubed/gameplay/hitbox_manager.hpp
Normal 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
|
||||
39
include/Cubed/gameplay/item.hpp
Normal file
39
include/Cubed/gameplay/item.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
namespace Cubed {
|
||||
using ItemID = uint16_t;
|
||||
|
||||
enum class ItemKind {
|
||||
NONE,
|
||||
BLOCK,
|
||||
SPAWN_EGG
|
||||
|
||||
};
|
||||
|
||||
using ItemProperty = std::variant<BlockType, std::string>;
|
||||
|
||||
struct ItemData {
|
||||
ItemID id = 0;
|
||||
std::string name;
|
||||
std::string local_name;
|
||||
std::string description;
|
||||
std::string path;
|
||||
ItemKind kind = ItemKind::NONE;
|
||||
ItemProperty property;
|
||||
};
|
||||
|
||||
inline constexpr ItemKind get_item_kind(std::string_view kind) {
|
||||
if (kind == "block") {
|
||||
return ItemKind::BLOCK;
|
||||
}
|
||||
if (kind == "spawn_egg") {
|
||||
return ItemKind::SPAWN_EGG;
|
||||
}
|
||||
return ItemKind::NONE;
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
39
include/Cubed/gameplay/item_manager.hpp
Normal file
39
include/Cubed/gameplay/item_manager.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/item.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
namespace Cubed {
|
||||
class ItemManager {
|
||||
public:
|
||||
ItemManager();
|
||||
void init();
|
||||
static ItemManager& instance();
|
||||
|
||||
static const ItemData& get(std::string_view key);
|
||||
static const ItemData& get(ItemID id);
|
||||
|
||||
static ItemID size();
|
||||
|
||||
const ItemData& get_item_data(std::string_view key) const;
|
||||
const ItemData& get_item_data(ItemID id) const;
|
||||
|
||||
private:
|
||||
using ItemMap = tbb::concurrent_hash_map<ItemID, ItemData>;
|
||||
using acc = ItemMap::accessor;
|
||||
using cacc = ItemMap::const_accessor;
|
||||
|
||||
using IDMap = tbb::concurrent_hash_map<std::string, ItemID>;
|
||||
using BlockToIDMap = tbb::concurrent_hash_map<BlockType, ItemID>;
|
||||
void add(const std::filesystem::path& path);
|
||||
|
||||
ItemMap m_map;
|
||||
|
||||
IDMap m_id_map;
|
||||
BlockToIDMap m_block_to_id_map;
|
||||
|
||||
static inline const ItemData EMPTY;
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/item.hpp"
|
||||
namespace Cubed {
|
||||
|
||||
struct ItemStack {
|
||||
BlockType type = 0;
|
||||
ItemID id = 0;
|
||||
size_t sum = 0;
|
||||
};
|
||||
} // namespace Cubed
|
||||
|
||||
161
include/Cubed/gameplay/local_player.hpp
Normal file
161
include/Cubed/gameplay/local_player.hpp
Normal file
@@ -0,0 +1,161 @@
|
||||
#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/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 dt);
|
||||
void update_player_chunk();
|
||||
|
||||
void play_walk_sound(float dt);
|
||||
Gait compute_gait() const;
|
||||
|
||||
void update_speed(float dt);
|
||||
std::tuple<bool, bool, bool> update_physical(float dt, glm::vec3& pos);
|
||||
glm::vec3 get_move_distance(float dt);
|
||||
};
|
||||
} // namespace Cubed
|
||||
7
include/Cubed/gameplay/model.hpp
Normal file
7
include/Cubed/gameplay/model.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
using ModelID = uint32_t;
|
||||
struct Model {
|
||||
ModelID id;
|
||||
};
|
||||
@@ -14,8 +14,7 @@ public:
|
||||
void stop();
|
||||
|
||||
// Run in another thread after initialization is complete
|
||||
void start_server(int port);
|
||||
void start_server();
|
||||
void start_server(int port, RunMode mode);
|
||||
int port() const;
|
||||
ServerWorld& server_world();
|
||||
|
||||
|
||||
@@ -51,18 +51,28 @@ enum class PacketEnum : uint16_t {
|
||||
LOGIN_RSP = 1002,
|
||||
LOGOUT_REQ = 1003,
|
||||
LOGOUT_RSP = 1004,
|
||||
|
||||
PLAYER_INFO = 2001,
|
||||
C2S_PLAYER_INFO = 2002,
|
||||
PLAYER_INFO_RSP = 2003,
|
||||
PLAYER_WATER_SOUND = 2004,
|
||||
|
||||
CHUNK_DATA_REQ = 3001,
|
||||
CHUNK_DATA_RSP = 3002,
|
||||
BLOCK_CHANGE_REQ = 3003,
|
||||
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,
|
||||
S2C_ENTITY_UPDATE = 3011,
|
||||
S2C_ENTITY_UPDATE_BATCH = 3012,
|
||||
|
||||
CHAT_MSG = 4001,
|
||||
VOICE_MSG = 4002,
|
||||
|
||||
PING = 9001,
|
||||
PONG = 9002
|
||||
|
||||
@@ -111,6 +121,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);
|
||||
}
|
||||
@@ -129,6 +151,12 @@ template <> constexpr uint16_t get_packet_id<ChatMsg>() {
|
||||
template <> constexpr uint16_t get_packet_id<VoiceMsg>() {
|
||||
return std::to_underlying(PacketEnum::VOICE_MSG);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<S2CEntityUpdate>() {
|
||||
return std::to_underlying(PacketEnum::S2C_ENTITY_UPDATE);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<S2CEntityUpdateBatch>() {
|
||||
return std::to_underlying(PacketEnum::S2C_ENTITY_UPDATE_BATCH);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::derived_from<T, google::protobuf::Message>
|
||||
@@ -180,6 +208,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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
80
include/Cubed/gameplay/server_entity_manager.hpp
Normal file
80
include/Cubed/gameplay/server_entity_manager.hpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/entity.hpp"
|
||||
#include "Cubed/gameplay/gait.hpp"
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/concurrent_queue.h>
|
||||
#include <tbb/concurrent_vector.h>
|
||||
namespace Cubed {
|
||||
class ServerWorld;
|
||||
class Session;
|
||||
class ServerEntityManager {
|
||||
public:
|
||||
static constexpr size_t PER_CREATURE_LIMITS = 100;
|
||||
|
||||
ServerEntityManager(ServerWorld& world);
|
||||
|
||||
void init();
|
||||
void update();
|
||||
void add_creature(std::string_view name, const glm::vec3& world_pos);
|
||||
void destory(EntityID id);
|
||||
void handle_player_login(std::shared_ptr<Session> session);
|
||||
|
||||
size_t max_creature_sum() const;
|
||||
size_t creature_sum() const;
|
||||
size_t entity_sum() const;
|
||||
|
||||
private:
|
||||
enum class Command { CREATE, SEND_ALL_ENTITIES, DESTORY };
|
||||
struct EntityCreateElement {
|
||||
std::string name;
|
||||
glm::vec3 pos;
|
||||
};
|
||||
|
||||
struct EntitySendData {
|
||||
EntityID id;
|
||||
glm::vec3 pos;
|
||||
glm::vec3 dir;
|
||||
Gait gait;
|
||||
};
|
||||
|
||||
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;
|
||||
std::atomic<size_t> m_creature_sum{0};
|
||||
std::atomic<size_t> m_entity_sum{0};
|
||||
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);
|
||||
void update_ai(entt::entity e);
|
||||
void update_move(entt::entity e);
|
||||
void update_send(entt::entity e,
|
||||
tbb::concurrent_vector<EntitySendData>& sessions);
|
||||
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
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
#include "Cubed/gameplay/gait.hpp"
|
||||
#include "Cubed/gameplay/game_time.hpp"
|
||||
#include "Cubed/gameplay/player.hpp"
|
||||
|
||||
#include <absl/container/flat_hash_set.h>
|
||||
#include <atomic>
|
||||
|
||||
@@ -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"
|
||||
@@ -20,19 +22,20 @@
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/concurrent_queue.h>
|
||||
#include <tbb/concurrent_unordered_map.h>
|
||||
#include <tbb/concurrent_vector.h>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
class Session;
|
||||
class ServerWorld {
|
||||
class ServerWorld : public World {
|
||||
public:
|
||||
enum class ThreadPoolKind { NET, GEN };
|
||||
ServerWorld(Config& config);
|
||||
~ServerWorld();
|
||||
void stop();
|
||||
void handle_player_exit(const std::string& uuid);
|
||||
void init_world();
|
||||
void init_world(RunMode mode);
|
||||
void need_gen(std::string uuid);
|
||||
void update();
|
||||
void hot_reload();
|
||||
@@ -84,7 +87,24 @@ 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;
|
||||
|
||||
tbb::concurrent_vector<std::shared_ptr<Session>> get_all_session() const;
|
||||
|
||||
uint32_t get_chunk_ref_count(const glm::vec3& pos) const;
|
||||
ServerEntityManager& entity_manager();
|
||||
std::shared_ptr<ThreadPool> get_compute_pool();
|
||||
size_t player_sum() 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,
|
||||
@@ -97,7 +117,27 @@ private:
|
||||
struct ChunkEntity {
|
||||
ChunkState state;
|
||||
std::shared_ptr<ServerChunk> chunk;
|
||||
uint32_t ref_count = 0;
|
||||
std::atomic<uint32_t> ref_count = 0;
|
||||
ChunkEntity() = default;
|
||||
ChunkEntity(ChunkState s, std::shared_ptr<ServerChunk> c = {})
|
||||
: state(s), chunk(std::move(c)) {}
|
||||
|
||||
ChunkEntity& operator=(ChunkEntity&& o) noexcept {
|
||||
if (this == &o) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
state = std::exchange(o.state, ServerWorld::ChunkState::NONE);
|
||||
chunk = std::move(o.chunk);
|
||||
ref_count = o.ref_count.exchange(0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ChunkEntity(ChunkEntity&& o) noexcept
|
||||
: state(std::exchange(o.state, ServerWorld::ChunkState::NONE)),
|
||||
chunk(std::move(o.chunk)), ref_count(o.ref_count.exchange(0)) {}
|
||||
ChunkEntity(const ChunkEntity&) = delete;
|
||||
ChunkEntity& operator=(const ChunkEntity&) = delete;
|
||||
};
|
||||
|
||||
enum class ChunkLoadStyle { RANDOM, CENTER };
|
||||
@@ -119,13 +159,14 @@ 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;
|
||||
|
||||
std::atomic<RunMode> m_runmode{RunMode::HYBRID};
|
||||
ServerEntityManager m_entity_manager;
|
||||
// key = uuid
|
||||
PlayerHashMap m_players;
|
||||
ChunkHashMap m_chunks;
|
||||
@@ -146,8 +187,9 @@ private:
|
||||
std::atomic<bool> m_init{false};
|
||||
std::atomic<bool> m_stopped{false};
|
||||
std::atomic<int> m_rendering_distance{24};
|
||||
std::atomic<int> m_gen_pool_threads{0};
|
||||
std::atomic<int> m_net_pool_threads{0};
|
||||
std::atomic<int> m_gen_threads{0};
|
||||
std::atomic<int> m_net_threads{0};
|
||||
std::atomic<int> m_compute_threads{0};
|
||||
std::atomic<int> m_max_threads{1};
|
||||
std::atomic<size_t> m_player_sum{0};
|
||||
std::atomic<TickType> m_game_ticks{0};
|
||||
@@ -155,7 +197,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;
|
||||
|
||||
@@ -163,6 +205,7 @@ private:
|
||||
|
||||
std::atomic<std::shared_ptr<PriorityThreadPool>> m_gen_thread_pool;
|
||||
std::atomic<std::shared_ptr<ThreadPool>> m_net_thread_pool;
|
||||
std::atomic<std::shared_ptr<ThreadPool>> m_compute_thread_pool;
|
||||
|
||||
std::atomic<ChunkLoadStyle> m_chunk_load_style{ChunkLoadStyle::CENTER};
|
||||
|
||||
|
||||
16
include/Cubed/gameplay/systems/physical_system.hpp
Normal file
16
include/Cubed/gameplay/systems/physical_system.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/movement.hpp"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
namespace Cubed {
|
||||
class ServerWorld;
|
||||
class PhysicalSystem {
|
||||
public:
|
||||
static glm::vec3 get_move_distance(const TickVelocity& v);
|
||||
|
||||
static void update(ServerWorld& world, entt::registry& registry,
|
||||
entt::entity e);
|
||||
|
||||
private:
|
||||
};
|
||||
} // namespace Cubed
|
||||
11
include/Cubed/gameplay/systems/speed_system.hpp
Normal file
11
include/Cubed/gameplay/systems/speed_system.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
namespace Cubed {
|
||||
class SpeedSystem {
|
||||
public:
|
||||
static void update(float dt, entt::registry& registry, entt::entity e);
|
||||
|
||||
private:
|
||||
};
|
||||
} // namespace Cubed
|
||||
14
include/Cubed/gameplay/systems/wander_ai_system.hpp
Normal file
14
include/Cubed/gameplay/systems/wander_ai_system.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/ai_struct.hpp"
|
||||
#include "Cubed/gameplay/ecs/server_entity.hpp"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
namespace Cubed {
|
||||
class WanderAISystem {
|
||||
public:
|
||||
static void update(entt::registry& registry, entt::entity e);
|
||||
|
||||
private:
|
||||
static void do_ai(BaseServerCreature& creature, MoveBoost& move_boost);
|
||||
};
|
||||
} // namespace Cubed
|
||||
32
include/Cubed/gameplay/world.hpp
Normal file
32
include/Cubed/gameplay/world.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#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}};
|
||||
}
|
||||
};
|
||||
|
||||
enum class RunMode { CLIENT_ONLY, SERVER_ONLY, HYBRID };
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -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;
|
||||
|
||||
46
include/Cubed/render/model_manager.hpp
Normal file
46
include/Cubed/render/model_manager.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#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);
|
||||
void load_anim_config(ModelNode& node, const std::string& path);
|
||||
};
|
||||
} // namespace Cubed
|
||||
79
include/Cubed/render/model_node.hpp
Normal file
79
include/Cubed/render/model_node.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#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>
|
||||
#include <string_view>
|
||||
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 NodeAnimRule {
|
||||
enum class Role { NONE, LEG, HEAD };
|
||||
std::string node;
|
||||
Role role = Role::NONE;
|
||||
float phase = 0.0f;
|
||||
};
|
||||
|
||||
struct ModelAnimConfig {
|
||||
float walk_speed = 6.0f;
|
||||
float walk_amp = 25.0f;
|
||||
float run_speed = 12.0f;
|
||||
float run_amp = 40.0f;
|
||||
float body_bob = 0.05f;
|
||||
float head_amp = 4.0f;
|
||||
std::vector<NodeAnimRule> nodes;
|
||||
|
||||
const NodeAnimRule* rule_for(std::string_view name) const {
|
||||
for (const auto& r : nodes) {
|
||||
if (r.node == name) {
|
||||
return &r;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool has_role(NodeAnimRule::Role role) const {
|
||||
for (const auto& r : nodes) {
|
||||
if (r.role == role) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
struct ModelNode {
|
||||
std::string name;
|
||||
glm::mat4 transform{1.0f};
|
||||
|
||||
std::vector<Mesh> meshes;
|
||||
std::vector<ModelNode> children;
|
||||
ModelAnimConfig anim;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
55
include/Cubed/render/model_renderer.hpp
Normal file
55
include/Cubed/render/model_renderer.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/ecs/animation.hpp"
|
||||
#include "Cubed/gameplay/model.hpp"
|
||||
#include "Cubed/render/model_node.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
class Renderer;
|
||||
class Camera;
|
||||
class ModelRender {
|
||||
public:
|
||||
struct InstanceData {
|
||||
glm::vec3 pos{0.0f};
|
||||
float yaw = 0.0f;
|
||||
WalkPose pose;
|
||||
};
|
||||
|
||||
struct DrawEntry {
|
||||
const Mesh* mesh = nullptr;
|
||||
size_t node_slot = 0;
|
||||
};
|
||||
|
||||
struct ModelBatch {
|
||||
size_t node_count = 0;
|
||||
size_t capacity = 0;
|
||||
std::vector<DrawEntry> entries;
|
||||
std::vector<glm::mat4> instance_matrices;
|
||||
std::unique_ptr<VertexBuffer> instance_vbo;
|
||||
};
|
||||
|
||||
ModelRender(Renderer& renderer);
|
||||
|
||||
void render_instance(ModelID id, size_t sum, const Camera& camera,
|
||||
bool shadow);
|
||||
void build_vertices(ModelID id, std::span<const InstanceData> instances);
|
||||
|
||||
private:
|
||||
Renderer& m_renderer;
|
||||
|
||||
std::unordered_map<ModelID, ModelBatch> m_batches;
|
||||
|
||||
size_t collect_matrices(const ModelNode& node, const glm::mat4& parent,
|
||||
const WalkPose& pose, const ModelAnimConfig& cfg,
|
||||
std::vector<glm::mat4>& out, size_t slot);
|
||||
glm::mat4 pose_node(const ModelNode& node, const ModelAnimConfig& cfg,
|
||||
const WalkPose& pose);
|
||||
|
||||
ModelBatch& get_batch(ModelID id, const ModelNode& root);
|
||||
size_t flatten_nodes(const ModelNode& node, size_t slot, ModelBatch& batch);
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -19,6 +19,7 @@ public:
|
||||
|
||||
void attribute(GLuint index, GLint size, GLenum type, GLsizei stride,
|
||||
const void* ptr, bool normalized = false) const;
|
||||
void divisor(GLuint index, GLuint divisor = 1);
|
||||
|
||||
private:
|
||||
GLuint m_vao = 0;
|
||||
|
||||
@@ -26,6 +26,8 @@ public:
|
||||
GLuint id() const;
|
||||
void buffer_data(const void* data, GLsizeiptr size,
|
||||
BufferUsage usage = BufferUsage::STATIC_DRAW) const;
|
||||
void buffer_sub_data(const void* data, GLsizeiptr size,
|
||||
GLintptr offset) const;
|
||||
|
||||
private:
|
||||
GLuint m_vbo = 0;
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/model.hpp"
|
||||
#include "Cubed/render/frame_buffer.hpp"
|
||||
#include "Cubed/render/model_renderer.hpp"
|
||||
#include "Cubed/render/player_renderer.hpp"
|
||||
#include "Cubed/render/texture.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
class Renderer;
|
||||
class ClientWorld;
|
||||
@@ -12,6 +16,8 @@ class TextureManager;
|
||||
class Camera;
|
||||
class WorldRenderer {
|
||||
public:
|
||||
using InstanceDataMap =
|
||||
std::unordered_map<ModelID, std::vector<ModelRender::InstanceData>>;
|
||||
struct ParallelLight {
|
||||
glm::vec3 sundir; // direction from sun to vertex
|
||||
glm::vec3 lightdir;
|
||||
@@ -128,11 +134,13 @@ private:
|
||||
|
||||
void render_world(ClientWorld& world);
|
||||
|
||||
void shadow_map_generate(ClientWorld& world);
|
||||
void shadow_map_generate(ClientWorld& world, const InstanceDataMap& map);
|
||||
|
||||
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,
|
||||
const InstanceDataMap& map);
|
||||
void render_entity(ClientWorld& world, const InstanceDataMap& map);
|
||||
|
||||
void render_normal_block(const glm::mat4& model_mat,
|
||||
const glm::mat4& mv_mat, const glm::mat4& norm_mat,
|
||||
@@ -146,5 +154,6 @@ private:
|
||||
float angle_step_deg) const;
|
||||
glm::vec3 get_smoothed_shadow_lightdir(const glm::vec3& raw_shadow_sundir,
|
||||
float dt);
|
||||
InstanceDataMap entity_build(ClientWorld& world);
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
|
||||
App& app();
|
||||
WorldSceneParam& world_scene_param();
|
||||
void push(SceneType type);
|
||||
|
||||
private:
|
||||
enum class OperationType { PUSH, POP, CHANGE };
|
||||
@@ -51,7 +52,7 @@ private:
|
||||
std::stack<std::unique_ptr<Scene>> m_scenes;
|
||||
void process_operation();
|
||||
void change(SceneType type);
|
||||
void push(SceneType type);
|
||||
|
||||
void pop(bool re_enter = true);
|
||||
|
||||
std::unique_ptr<Scene> create_scene(SceneType);
|
||||
|
||||
@@ -42,6 +42,8 @@ public:
|
||||
|
||||
bool is_recording() const;
|
||||
|
||||
RunMode runmode() const;
|
||||
|
||||
private:
|
||||
enum class PauseUI { PAUSE_MENU, INVENTORY };
|
||||
SceneManager& m_scene_manager;
|
||||
@@ -61,6 +63,7 @@ private:
|
||||
ErrorUI m_error_ui;
|
||||
const Argument& m_argument;
|
||||
VoiceInputType m_input_type;
|
||||
RunMode m_runmode = RunMode::HYBRID;
|
||||
bool handle_mouse_move_event(const MouseMoveEvent& e) override;
|
||||
bool handle_mouse_button_event(const MouseButtonEvent& e) override;
|
||||
bool handle_window_resize_event(const WindowResizeEvent& e) override;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "Cubed/config.hpp"
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/item.hpp"
|
||||
#include "Cubed/input/event.hpp"
|
||||
#include "Cubed/render/texture.hpp"
|
||||
|
||||
@@ -10,6 +11,26 @@
|
||||
namespace Cubed {
|
||||
|
||||
class TextureManager {
|
||||
public:
|
||||
using ItemTextureMap = std::unordered_map<ItemID, std::shared_ptr<Texture>>;
|
||||
TextureManager(Config& config);
|
||||
~TextureManager();
|
||||
|
||||
void delete_texture();
|
||||
const Texture* get_block_status_array() const;
|
||||
const Texture* get_texture_array() const;
|
||||
const Texture* get_cross_plane_array() const;
|
||||
const Texture* get_image_texture(const std::string& path);
|
||||
const Texture* get_pbr_texture() const;
|
||||
const ItemTextureMap& get_item_textures() const;
|
||||
const Texture* get_skin() const;
|
||||
void init_texture();
|
||||
|
||||
void need_reload();
|
||||
void update();
|
||||
int max_aniso() const;
|
||||
bool handle_event(const Event& e);
|
||||
|
||||
private:
|
||||
bool m_need_reload = false;
|
||||
bool m_init = false;
|
||||
@@ -18,7 +39,7 @@ private:
|
||||
std::unique_ptr<Texture> m_cross_plane_array;
|
||||
std::unique_ptr<Texture> m_normal_texture_array;
|
||||
std::unique_ptr<Texture> m_skin;
|
||||
std::vector<std::unique_ptr<Texture>> m_item_textures;
|
||||
ItemTextureMap m_item_textures;
|
||||
std::unordered_map<std::string, std::unique_ptr<Texture>> m_ui_map;
|
||||
GLfloat m_max_aniso = 0.0f;
|
||||
Config& m_config;
|
||||
@@ -26,7 +47,7 @@ private:
|
||||
|
||||
void load_block_status(unsigned status_id);
|
||||
void load_block_texture(unsigned block_id);
|
||||
void load_block_item_texture(unsigned id);
|
||||
void init_item_texture();
|
||||
void load_cross_plane_texture(unsigned id);
|
||||
const Texture* load_image_texture(const std::string& path);
|
||||
void load_pbr_texture(unsigned id);
|
||||
@@ -37,25 +58,6 @@ private:
|
||||
void init_skin();
|
||||
void hot_reload();
|
||||
bool handle_key_event(const KeyEvent& e);
|
||||
|
||||
public:
|
||||
TextureManager(Config& config);
|
||||
~TextureManager();
|
||||
|
||||
void delete_texture();
|
||||
const Texture* get_block_status_array() const;
|
||||
const Texture* get_texture_array() const;
|
||||
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>>& get_item_textures() const;
|
||||
const Texture* get_skin() const;
|
||||
void init_texture();
|
||||
|
||||
void need_reload();
|
||||
void update();
|
||||
int max_aniso() const;
|
||||
bool handle_event(const Event& e);
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
7
include/Cubed/tools/cubed_concepts.hpp
Normal file
7
include/Cubed/tools/cubed_concepts.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
namespace Cubed {
|
||||
template <typename T>
|
||||
concept Ptr = std::is_pointer_v<T>;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
|
||||
#include <random>
|
||||
namespace Cubed {
|
||||
|
||||
@@ -14,6 +16,8 @@ public:
|
||||
int random_int(int min, int max);
|
||||
float random_float(float min, float max);
|
||||
|
||||
glm::vec3 random_direction_horizontal();
|
||||
|
||||
private:
|
||||
unsigned int m_seed = 0;
|
||||
std::mt19937 m_engine;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user