From 48300b5fe2b64e8ea42b7162b25b511128f55b23 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Fri, 26 Jun 2026 15:52:22 +0800 Subject: [PATCH] feat(multiplayer): add rendering of other players as colored cubes --- assets/shaders/player_f_shader.glsl | 8 ++++ assets/shaders/player_v_shader.glsl | 15 ++++++++ include/Cubed/gameplay/client_world.hpp | 22 ++++++++++- include/Cubed/gameplay/packet.hpp | 3 ++ include/Cubed/primitive_data.hpp | 49 +++++++++++++++++++++++++ include/Cubed/renderer.hpp | 5 ++- src/gameplay/client_world.cpp | 24 +++++++++++- src/gameplay/network_client.cpp | 10 ++++- src/gameplay/server_world.cpp | 39 +++++++++++++++++--- src/gameplay/session.cpp | 2 +- src/proto/player/player.proto | 6 +++ src/renderer.cpp | 37 ++++++++++++++++++- 12 files changed, 204 insertions(+), 16 deletions(-) create mode 100644 assets/shaders/player_f_shader.glsl create mode 100644 assets/shaders/player_v_shader.glsl diff --git a/assets/shaders/player_f_shader.glsl b/assets/shaders/player_f_shader.glsl new file mode 100644 index 0000000..68c9da0 --- /dev/null +++ b/assets/shaders/player_f_shader.glsl @@ -0,0 +1,8 @@ +#version 460 + +out vec4 color; +in vec2 tc; + +void main() { + color = vec4(0.0, 0.0, 1.0, 1.0); +} \ No newline at end of file diff --git a/assets/shaders/player_v_shader.glsl b/assets/shaders/player_v_shader.glsl new file mode 100644 index 0000000..2bf71d5 --- /dev/null +++ b/assets/shaders/player_v_shader.glsl @@ -0,0 +1,15 @@ +#version 460 + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoord; + +uniform mat4 mv_matrix; +uniform mat4 proj_matrix; + +out vec2 tc; + +void main() { + vec4 viewPos = mv_matrix * vec4(pos, 1.0); + tc = texCoord; + gl_Position = proj_matrix * viewPos; +} \ No newline at end of file diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index 7e2b52a..eb2d9a2 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -10,6 +10,16 @@ #include namespace Cubed { +struct OtherPlayerInfo { + std::string name; + glm::vec3 pos; +}; + +struct RenderPlayerData { + std::string name; + glm::vec3 pos; +}; + class ClientWorld { public: ClientWorld(); @@ -34,6 +44,8 @@ public: void receive_block_change(const BlockChangeRsp& rsp); void receive_time(const UpdateTime& rsp); + void receive_other_player(const PlayerInfoRsp& rsp); + int rendering_distance() const; void rendering_distance(int rendering_distance); void start_client_thread(std::string_view uuid); @@ -42,7 +54,8 @@ public: void hot_reload(); void request_chunk(); std::vector& planes(); - std::vector& render_snapshots(); + const std::vector& render_snapshots() const; + const std::vector& render_player_data() const; glm::vec3 sunlight_dir() const; void receive_chunk(const ChunkDataRsp& data); template @@ -58,7 +71,9 @@ private: tbb::concurrent_unordered_map; using ChunkPosSet = std::unordered_set; using ChunkPosVector = std::vector; + using OtherPlayerHashMap = std::unordered_map; ClientPlayer m_player; + OtherPlayerHashMap m_other_players; ChunkHashMap m_chunks; std::vector m_planes; std::jthread m_client_thread; @@ -68,12 +83,17 @@ private: std::mutex m_delete_vao_mutex; std::mutex m_pending_upload_queue_mutex; std::mutex m_pending_chunk_data_queue_mutex; + std::mutex m_other_players_mutex; + std::deque m_pending_upload_queue; std::deque m_pending_chunk_data_queue; + std::vector m_pending_delete_vbo; std::vector m_pending_delete_vao; + std::deque m_dirty_queue; std::vector m_render_snapshots; + std::vector m_render_player_data; tbb::concurrent_unordered_map m_timers; std::atomic m_game_running{false}; std::atomic m_rendering_distance{24}; diff --git a/include/Cubed/gameplay/packet.hpp b/include/Cubed/gameplay/packet.hpp index 4a84f52..c6a044f 100644 --- a/include/Cubed/gameplay/packet.hpp +++ b/include/Cubed/gameplay/packet.hpp @@ -13,6 +13,7 @@ enum class PacketEnum { LOGIN_RSP = 1002, PLAYER_INFO = 2001, PLAYER_POS = 2002, + PLAYER_INFO_RSP = 2003, CHUNK_DATA_REQ = 3001, CHUNK_DATA_RSP = 3002, BLOCK_CHANGE_REQ = 3003, @@ -38,6 +39,8 @@ template constexpr uint16_t get_packet_id() { return to_num(PLAYER_INFO); } else if constexpr (is_same_v) { return to_num(PLAYER_POS); + } else if constexpr (is_same_v) { + return to_num(PLAYER_INFO_RSP); } else if constexpr (is_same_v) { return to_num(CHUNK_DATA_REQ); } else if constexpr (is_same_v) { diff --git a/include/Cubed/primitive_data.hpp b/include/Cubed/primitive_data.hpp index 3b8a79e..5bee0cc 100644 --- a/include/Cubed/primitive_data.hpp +++ b/include/Cubed/primitive_data.hpp @@ -272,6 +272,55 @@ constexpr float CROSS_TANGENTS[2][6][3] = { {-0.7071f, 0.0f, 0.7071f}}}; #pragma endregion + +#pragma region Player + +constexpr float VERTICES_PLAYER[6][6][3] = { + // ===== front (z = +1) ===== + {{0.0f, 0.0f, 1.0f}, // bottom left + {0.0f, 2.0f, 1.0f}, // top left + {1.0f, 2.0f, 1.0f}, // top right + {1.0f, 2.0f, 1.0f}, // top right + {1.0f, 0.0f, 1.0f}, // bottom right + {0.0f, 0.0f, 1.0f}}, // bottom left + // ===== right (x = +1) ===== + {{1.0f, 0.0f, 1.0f}, // bottom front + {1.0f, 0.0f, 0.0f}, // bottom back + {1.0f, 2.0f, 0.0f}, // top back + {1.0f, 2.0f, 0.0f}, // top back + {1.0f, 2.0f, 1.0f}, // top front + {1.0f, 0.0f, 1.0f}}, // bottom front + // ===== back (z = -1) ===== + {{0.0f, 0.0f, 0.0f}, // bottom left + {1.0f, 0.0f, 0.0f}, // bottom right + {1.0f, 2.0f, 0.0f}, // top right + {1.0f, 2.0f, 0.0f}, // top right + {0.0f, 2.0f, 0.0f}, // top left + {0.0f, 0.0f, 0.0f}}, // bottom left + // ===== left (x = -1) ===== + {{0.0f, 0.0f, 0.0f}, // bottom back + {0.0f, 0.0f, 1.0f}, // bottom front + {0.0f, 2.0f, 1.0f}, // top front + {0.0f, 2.0f, 1.0f}, // top front + {0.0f, 2.0f, 0.0f}, // top back + {0.0f, 0.0f, 0.0f}}, // bottom back + // ===== top (y = +2) ===== + {{0.0f, 2.0f, 0.0f}, // back left + {1.0f, 2.0f, 0.0f}, // back right + {1.0f, 2.0f, 1.0f}, // front right + {1.0f, 2.0f, 1.0f}, // front right + {0.0f, 2.0f, 1.0f}, // front left + {0.0f, 2.0f, 0.0f}}, // back left + // ===== bottom (y = -1) ===== + {{0.0f, 0.0f, 1.0f}, // front left + {1.0f, 0.0f, 1.0f}, // front right + {1.0f, 0.0f, 0.0f}, // back right + {1.0f, 0.0f, 0.0f}, // back right + {0.0f, 0.0f, 0.0f}, // back left + {0.0f, 0.0f, 1.0f}} // front left +}; +#pragma endregion + // [-1, 1] constexpr float QUAD_VERTICES[] = { // postion // texcoorlds diff --git a/include/Cubed/renderer.hpp b/include/Cubed/renderer.hpp index 4d92bce..91b1eb6 100644 --- a/include/Cubed/renderer.hpp +++ b/include/Cubed/renderer.hpp @@ -122,7 +122,7 @@ private: GLuint m_outline_indices_vbo = 0; GLuint m_outline_vbo = 0; GLuint m_ui_vbo = 0; - + GLuint m_player_vbo = 0; GLuint m_fbo = 0; GLuint m_screen_texture = 0; GLuint m_screen_depth_texture = 0; @@ -174,7 +174,7 @@ private: 2 - outline vao 3 - ui vao 4 - text vao - + 5 - player vao */ std::vector m_vao; std::vector m_ui; @@ -189,6 +189,7 @@ private: void render_text(); void render_ui(); void render_world(); + void render_player(); void render_underwater(); void render_dev_panel(); diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index eba82b6..a9ad17d 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -190,13 +190,23 @@ void ClientWorld::receive_time(const UpdateTime& rsp) { m_day_tick = rsp.day_tick(); } +void ClientWorld::receive_other_player(const PlayerInfoRsp& rsp) { + { + std::lock_guard lock(m_other_players_mutex); + m_other_players[rsp.uuid()].name = rsp.name(); + m_other_players[rsp.uuid()].pos = + glm::vec3{rsp.pos().x(), rsp.pos().y(), rsp.pos().z()}; + // Logger::info("Player {} pos Update", rsp.name()); + } +} + void ClientWorld::init(std::string_view player_name, std::shared_ptr client) { m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4); m_player.init(player_name); m_client = client; // timer - register_timer("player_pos", 2, [this]() { report_player_pos(); }); + register_timer("player_pos", 1, [this]() { report_player_pos(); }); LoginReq req; req.set_name(m_player.get_name()); while (!client->is_connected()) { @@ -450,6 +460,13 @@ void ClientWorld::update(float delta_time) { } } } + m_render_player_data.clear(); + { + std::lock_guard lock(m_other_players_mutex); + for (auto& [uuid, player] : m_other_players) { + m_render_player_data.emplace_back(player.name, player.pos); + } + } } glm::vec3 ClientWorld::sunlight_dir() const { @@ -476,8 +493,11 @@ int ClientWorld::rendering_distance() const { void ClientWorld::rendering_distance(int rendering_distance) { m_rendering_distance = rendering_distance; } -std::vector& ClientWorld::render_snapshots() { +const std::vector& ClientWorld::render_snapshots() const { return m_render_snapshots; }; +const std::vector& ClientWorld::render_player_data() const { + return m_render_player_data; +} std::vector& ClientWorld::planes() { return m_planes; } } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/network_client.cpp b/src/gameplay/network_client.cpp index 4cdc799..0442278 100644 --- a/src/gameplay/network_client.cpp +++ b/src/gameplay/network_client.cpp @@ -81,8 +81,8 @@ asio::awaitable NetworkClient::read_loop() { } break; case to_num(PacketEnum::CHUNK_DATA_RSP): { ChunkDataRsp rsp; - Logger::info("Client: Receive Chunk Data rsp, size {}mb", - body_data.size() / 1024.0f / 1024); + // Logger::info("Client: Receive Chunk Data rsp, size {}mb", + // body_data.size() / 1024.0f / 1024); if (rsp.ParseFromArray(body_data.data(), body_data.size())) { m_world.receive_chunk(rsp); } @@ -100,6 +100,12 @@ asio::awaitable NetworkClient::read_loop() { m_world.receive_time(rsp); } } + case to_num(PacketEnum::PLAYER_INFO_RSP): { + PlayerInfoRsp rsp; + if (rsp.ParseFromArray(body_data.data(), body_data.size())) { + m_world.receive_other_player(rsp); + } + } break; } } } catch (const asio::system_error& e) { diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index 3ec116e..597949a 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -400,13 +400,40 @@ void ServerWorld::update() { void ServerWorld::sync_player_pos(const std::string& uuid, float x, float y, float z) { - std::lock_guard lock(m_player_mutex); - auto it = m_players.find(uuid); - if (it == m_players.end()) { - Logger::warn("Player {} is not in this Server", uuid); - return; + std::string name; + { + std::lock_guard lock(m_player_mutex); + auto it = m_players.find(uuid); + if (it == m_players.end()) { + Logger::warn("Player {} is not in this Server", uuid); + return; + } + it->second.update_pos(x, y, z); + name = it->second.get_name(); + } + + // update other player pos; + std::vector> other; + { + std::shared_lock lock(m_player_mutex); + for (auto& [o_uuid, player] : m_players) { + if (o_uuid == uuid) { + continue; + } + other.emplace_back(player.get_session()); + } + } + + for (auto& session : other) { + PlayerInfoRsp rsp; + rsp.set_uuid(uuid); + rsp.set_name(name); + auto* pos = rsp.mutable_pos(); + pos->set_x(x); + pos->set_y(y); + pos->set_z(z); + session->send(make_packet(rsp)); } - it->second.update_pos(x, y, z); } void ServerWorld::handle_player_login(const std::string& name, diff --git a/src/gameplay/session.cpp b/src/gameplay/session.cpp index 11a4cae..b367d96 100644 --- a/src/gameplay/session.cpp +++ b/src/gameplay/session.cpp @@ -77,7 +77,7 @@ asio::awaitable Session::read_loop() { } if (cmd_id == to_num(PacketEnum::CHUNK_DATA_REQ)) { ChunkDataReq req; - Logger::info("Session: Receive Chunk Data req"); + // Logger::info("Session: Receive Chunk Data req"); if (req.ParseFromArray(body_data.data(), body_data.size())) { m_server_world.handle_chunk_req( req.uuid(), ChunkPos(req.pos().x(), req.pos().z())); diff --git a/src/proto/player/player.proto b/src/proto/player/player.proto index 7d3450f..dfbf483 100644 --- a/src/proto/player/player.proto +++ b/src/proto/player/player.proto @@ -5,4 +5,10 @@ import "common/vector3.proto"; message PlayerPos { string uuid = 1; Vec3 pos = 2; +} + +message PlayerInfoRsp { + string uuid = 1; + string name = 2; + Vec3 pos = 3; } \ No newline at end of file diff --git a/src/renderer.cpp b/src/renderer.cpp index fd2f146..d0a903a 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -34,6 +34,7 @@ Renderer::~Renderer() { glDeleteBuffers(1, &m_sky_vbo); glDeleteBuffers(1, &m_ui_vbo); glDeleteBuffers(1, &m_text_vbo); + glDeleteBuffers(1, &m_player_vbo); glBindVertexArray(0); glDeleteVertexArrays(NUM_VAO, m_vao.data()); glDeleteFramebuffers(1, &m_fbo); @@ -88,7 +89,9 @@ void Renderer::init() { "shaders/billboard_f_shader.glsl"}; Shader water_shader{"water", "shaders/water_v_shader.glsl", "shaders/water_f_shader.glsl"}; - + Shader player_shader{"player", "shaders/player_v_shader.glsl", + "shaders/player_f_shader.glsl"}; + m_shaders.insert({player_shader.hash(), std::move(player_shader)}); m_shaders.insert({world_shader.hash(), std::move(world_shader)}); m_shaders.insert({outline_shader.hash(), std::move(outline_shader)}); m_shaders.insert({sky_shdaer.hash(), std::move(sky_shdaer)}); @@ -102,6 +105,7 @@ void Renderer::init() { m_shaders.insert({depth_shader.hash(), std::move(depth_shader)}); m_shaders.insert({billboard.hash(), std::move(billboard)}); m_shaders.insert({water_shader.hash(), std::move(water_shader)}); + glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); @@ -167,6 +171,15 @@ void Renderer::init() { glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); + glBindVertexArray(m_vao[5]); + glGenBuffers(1, &m_player_vbo); + glBindBuffer(GL_ARRAY_BUFFER, m_player_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(VERTICES_PLAYER), VERTICES_PLAYER, + GL_STATIC_DRAW); + + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(0); + init_quad(); init_text(); hot_reload(); @@ -217,7 +230,7 @@ void Renderer::render() { render_sky(); render_world(); render_outline(); - + render_player(); glBindFramebuffer(GL_FRAMEBUFFER, 0); glEnable(GL_FRAMEBUFFER_SRGB); glDisable(GL_DEPTH_TEST); @@ -927,6 +940,26 @@ void Renderer::render_world() { DebugCollector::get().report( "rendered_chunk", "Rendered Chunk: " + std::to_string(rendered_sum)); } + +void Renderer::render_player() { + auto& shader = get_shader("player"); + shader.use(); + m_v_mat = m_camera.get_camera_lookat(); + + auto& players = m_world.render_player_data(); + + for (auto& player : players) { + m_m_mat = glm::translate(glm::mat4(1.0f), + player.pos + glm::vec3(-0.5f, 0.0f, -0.5f)); + m_mv_mat = m_v_mat * m_m_mat; + shader.set_loc("mv_matrix", m_mv_mat); + shader.set_loc("proj_matrix", m_p_mat); + glBindVertexArray(m_vao[5]); + glEnable(GL_DEPTH_TEST); + glDrawArrays(GL_TRIANGLES, 0, 36); + } +} + #pragma endregion void Renderer::render_dev_panel() { glDisable(GL_DEPTH_TEST);