mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 17:57:02 +08:00
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.
This commit is contained in:
@@ -19,8 +19,8 @@ public:
|
|||||||
|
|
||||||
void receive_entity_create(S2CEntityCreate& msg);
|
void receive_entity_create(S2CEntityCreate& msg);
|
||||||
void receive_entity_destory(EntityID id);
|
void receive_entity_destory(EntityID id);
|
||||||
void receive_entity_update(S2CEntityUpdate& msg);
|
void receive_entity_update(const S2CEntityUpdate& msg);
|
||||||
|
void receive_entity_update(S2CEntityUpdateBatch& msg);
|
||||||
void destory(EntityID id);
|
void destory(EntityID id);
|
||||||
void create(std::string_view name, const glm::vec3& pos);
|
void create(std::string_view name, const glm::vec3& pos);
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ enum class PacketEnum : uint16_t {
|
|||||||
C2S_ENTITY_CREATE_REQUEST = 3009,
|
C2S_ENTITY_CREATE_REQUEST = 3009,
|
||||||
C2S_ENTITY_DESTORY_REQUEST = 3010,
|
C2S_ENTITY_DESTORY_REQUEST = 3010,
|
||||||
S2C_ENTITY_UPDATE = 3011,
|
S2C_ENTITY_UPDATE = 3011,
|
||||||
|
S2C_ENTITY_UPDATE_BATCH = 3012,
|
||||||
|
|
||||||
CHAT_MSG = 4001,
|
CHAT_MSG = 4001,
|
||||||
VOICE_MSG = 4002,
|
VOICE_MSG = 4002,
|
||||||
@@ -153,6 +154,9 @@ template <> constexpr uint16_t get_packet_id<VoiceMsg>() {
|
|||||||
template <> constexpr uint16_t get_packet_id<S2CEntityUpdate>() {
|
template <> constexpr uint16_t get_packet_id<S2CEntityUpdate>() {
|
||||||
return std::to_underlying(PacketEnum::S2C_ENTITY_UPDATE);
|
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>
|
template <typename T>
|
||||||
requires std::derived_from<T, google::protobuf::Message>
|
requires std::derived_from<T, google::protobuf::Message>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "Cubed/gameplay/ecs/entity.hpp"
|
#include "Cubed/gameplay/ecs/entity.hpp"
|
||||||
|
#include "Cubed/gameplay/gait.hpp"
|
||||||
#include "glm/ext/vector_float3.hpp"
|
#include "glm/ext/vector_float3.hpp"
|
||||||
|
|
||||||
#include <entt/entt.hpp>
|
#include <entt/entt.hpp>
|
||||||
@@ -26,6 +27,14 @@ private:
|
|||||||
std::string name;
|
std::string name;
|
||||||
glm::vec3 pos;
|
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 EntityMap = tbb::concurrent_hash_map<EntityID, entt::entity>;
|
||||||
using acc = EntityMap::accessor;
|
using acc = EntityMap::accessor;
|
||||||
using cacc = EntityMap::const_accessor;
|
using cacc = EntityMap::const_accessor;
|
||||||
@@ -47,9 +56,8 @@ private:
|
|||||||
void send_all_entities(std::shared_ptr<Session>& session);
|
void send_all_entities(std::shared_ptr<Session>& session);
|
||||||
void update_ai(entt::entity e);
|
void update_ai(entt::entity e);
|
||||||
void update_move(entt::entity e);
|
void update_move(entt::entity e);
|
||||||
void
|
void update_send(entt::entity e,
|
||||||
update_send(entt::entity e,
|
tbb::concurrent_vector<EntitySendData>& sessions);
|
||||||
tbb::concurrent_vector<std::shared_ptr<Session>>& sessions);
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
EntityID create_entity_in_factory(Args&&... args) {
|
EntityID create_entity_in_factory(Args&&... args) {
|
||||||
auto entity = m_registry.create();
|
auto entity = m_registry.create();
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ void ClientEntityManager::receive_entity_destory(EntityID id) {
|
|||||||
m_tasks.emplace(Command::DESTORY, id);
|
m_tasks.emplace(Command::DESTORY, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientEntityManager::receive_entity_update(S2CEntityUpdate& msg) {
|
void ClientEntityManager::receive_entity_update(const S2CEntityUpdate& msg) {
|
||||||
UpdateInfo e;
|
UpdateInfo e;
|
||||||
e.id = msg.id();
|
e.id = msg.id();
|
||||||
e.pos = Tools::get_net_vec3(msg.pos());
|
e.pos = Tools::get_net_vec3(msg.pos());
|
||||||
@@ -96,6 +96,12 @@ void ClientEntityManager::receive_entity_update(S2CEntityUpdate& msg) {
|
|||||||
m_tasks.emplace(Command::UPDATE, std::move(e));
|
m_tasks.emplace(Command::UPDATE, std::move(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientEntityManager::receive_entity_update(S2CEntityUpdateBatch& msg) {
|
||||||
|
for (auto& u : msg.updates()) {
|
||||||
|
receive_entity_update(u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ClientEntityManager::destory(EntityID id) {
|
void ClientEntityManager::destory(EntityID id) {
|
||||||
auto client = m_world.get_client();
|
auto client = m_world.get_client();
|
||||||
Arena arena;
|
Arena arena;
|
||||||
|
|||||||
@@ -162,6 +162,12 @@ asio::awaitable<void> NetworkClient::read_loop() {
|
|||||||
m_world.entity_manager().receive_entity_update(*msg);
|
m_world.entity_manager().receive_entity_update(*msg);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
case std::to_underlying(PacketEnum::S2C_ENTITY_UPDATE_BATCH): {
|
||||||
|
auto* msg = Arena::Create<S2CEntityUpdateBatch>(&arena);
|
||||||
|
if (decode_packet(*msg, body_data, header)) {
|
||||||
|
m_world.entity_manager().receive_entity_update(*msg);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (const asio::system_error& e) {
|
} catch (const asio::system_error& e) {
|
||||||
|
|||||||
@@ -45,11 +45,12 @@ void ServerEntityManager::update() {
|
|||||||
for (auto e : view) {
|
for (auto e : view) {
|
||||||
entities.push_back(e);
|
entities.push_back(e);
|
||||||
}
|
}
|
||||||
|
tbb::concurrent_vector<EntitySendData> send_data;
|
||||||
// parallel block touches disjoint entities only;
|
// parallel block touches disjoint entities only;
|
||||||
// structural registry changes stay on the server thread via m_tasks.
|
// structural registry changes stay on the server thread via m_tasks.
|
||||||
parallel_do(
|
parallel_do(
|
||||||
*pool, entities.begin(), entities.end(), pool->thread_sum(),
|
*pool, entities.begin(), entities.end(), pool->thread_sum(),
|
||||||
[this, &sessions](entt::entity e) {
|
[this, &send_data](entt::entity e) {
|
||||||
const auto& c = m_registry.get<BaseServerCreature>(e);
|
const auto& c = m_registry.get<BaseServerCreature>(e);
|
||||||
if (!m_world.get_chunk_ref_count(c.transform.position.value)) {
|
if (!m_world.get_chunk_ref_count(c.transform.position.value)) {
|
||||||
const auto& entity = m_registry.get<Entity>(e);
|
const auto& entity = m_registry.get<Entity>(e);
|
||||||
@@ -58,8 +59,23 @@ void ServerEntityManager::update() {
|
|||||||
}
|
}
|
||||||
update_ai(e);
|
update_ai(e);
|
||||||
update_move(e);
|
update_move(e);
|
||||||
update_send(e, sessions);
|
update_send(e, send_data);
|
||||||
});
|
});
|
||||||
|
if (!send_data.empty()) {
|
||||||
|
Arena arena;
|
||||||
|
auto* msg = Arena::Create<S2CEntityUpdateBatch>(&arena);
|
||||||
|
for (auto& data : send_data) {
|
||||||
|
auto* u = msg->add_updates();
|
||||||
|
u->set_id(data.id);
|
||||||
|
Tools::set_net_pos(u, data.pos);
|
||||||
|
Tools::set_net_vec3(u->mutable_direction(), data.dir);
|
||||||
|
u->set_gait(get_gait_id(data.gait));
|
||||||
|
}
|
||||||
|
auto packet = make_packet(msg);
|
||||||
|
for (auto& player : sessions) {
|
||||||
|
player->send(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerEntityManager::update_ai(entt::entity e) {
|
void ServerEntityManager::update_ai(entt::entity e) {
|
||||||
@@ -74,8 +90,7 @@ void ServerEntityManager::update_move(entt::entity e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServerEntityManager::update_send(
|
void ServerEntityManager::update_send(
|
||||||
entt::entity e,
|
entt::entity e, tbb::concurrent_vector<EntitySendData>& send_data) {
|
||||||
tbb::concurrent_vector<std::shared_ptr<Session>>& sessions) {
|
|
||||||
|
|
||||||
if (!m_registry.all_of<Entity, BaseServerCreature>(e)) {
|
if (!m_registry.all_of<Entity, BaseServerCreature>(e)) {
|
||||||
return;
|
return;
|
||||||
@@ -83,23 +98,17 @@ void ServerEntityManager::update_send(
|
|||||||
|
|
||||||
const auto [entity, creature] =
|
const auto [entity, creature] =
|
||||||
m_registry.get<Entity, BaseServerCreature>(e);
|
m_registry.get<Entity, BaseServerCreature>(e);
|
||||||
Arena arena;
|
EntitySendData data;
|
||||||
auto* p = Arena::Create<S2CEntityUpdate>(&arena);
|
data.id = entity.id;
|
||||||
p->set_id(entity.id);
|
data.pos = creature.transform.position.value;
|
||||||
Tools::set_net_pos(p, creature.transform.position.value);
|
data.dir = creature.transform.direction.value;
|
||||||
|
|
||||||
Tools::set_net_vec3(p->mutable_direction(),
|
|
||||||
creature.transform.direction.value);
|
|
||||||
const auto& v = creature.velocity.value;
|
const auto& v = creature.velocity.value;
|
||||||
if (v.x * v.x + v.z * v.z > 1e-4f) {
|
if (v.x * v.x + v.z * v.z > 1e-4f) {
|
||||||
p->set_gait(get_gait_id(Gait::WALK));
|
data.gait = Gait::WALK;
|
||||||
} else {
|
} else {
|
||||||
p->set_gait(get_gait_id(Gait::STOP));
|
data.gait = Gait::STOP;
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& s : sessions) {
|
|
||||||
s->send(make_packet(p));
|
|
||||||
}
|
}
|
||||||
|
send_data.emplace_back(std::move(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerEntityManager::handle_task() {
|
void ServerEntityManager::handle_task() {
|
||||||
@@ -177,9 +186,9 @@ void ServerEntityManager::handle_entity_create(EntityID id,
|
|||||||
s2c->set_id(id);
|
s2c->set_id(id);
|
||||||
s2c->set_name(name);
|
s2c->set_name(name);
|
||||||
Tools::set_net_pos(s2c, pos);
|
Tools::set_net_pos(s2c, pos);
|
||||||
|
auto packet = make_packet(*s2c);
|
||||||
for (auto& s : sessions) {
|
for (auto& s : sessions) {
|
||||||
s->send(make_packet(*s2c));
|
s->send(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,8 +203,9 @@ void ServerEntityManager::handle_entity_destory(EntityID id) {
|
|||||||
Arena arena;
|
Arena arena;
|
||||||
auto* s2c = Arena::Create<S2CEntityDestory>(&arena);
|
auto* s2c = Arena::Create<S2CEntityDestory>(&arena);
|
||||||
s2c->set_id(id);
|
s2c->set_id(id);
|
||||||
|
auto packet = make_packet(*s2c);
|
||||||
for (auto& s : sessions) {
|
for (auto& s : sessions) {
|
||||||
s->send(make_packet(*s2c));
|
s->send(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,11 +89,10 @@ void ServerWorld::send_time() {
|
|||||||
|
|
||||||
rsp->set_day_tick(m_day_tick);
|
rsp->set_day_tick(m_day_tick);
|
||||||
rsp->set_game_tick(m_game_ticks);
|
rsp->set_game_tick(m_game_ticks);
|
||||||
{
|
auto sessions = get_all_session();
|
||||||
std::shared_lock lock(m_players_mutex);
|
auto packet = make_packet(*rsp);
|
||||||
for (auto& [uuid, player] : m_players) {
|
for (auto& s : sessions) {
|
||||||
player.get_session()->send(make_packet(*rsp), 3);
|
s->send(packet, 3);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,7 +607,7 @@ void ServerWorld::sync_player_pos(const C2S_PlayerInfo& prsp) {
|
|||||||
|
|
||||||
name = it->second.get_name();
|
name = it->second.get_name();
|
||||||
}
|
}
|
||||||
ChunkPos pos = get_chunk_pos(x, z);
|
ChunkPos c_pos = get_chunk_pos(x, z);
|
||||||
// update other player pos;
|
// update other player pos;
|
||||||
std::vector<std::shared_ptr<Session>> other;
|
std::vector<std::shared_ptr<Session>> other;
|
||||||
{
|
{
|
||||||
@@ -617,28 +616,31 @@ void ServerWorld::sync_player_pos(const C2S_PlayerInfo& prsp) {
|
|||||||
if (o_uuid == uuid) {
|
if (o_uuid == uuid) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (player.has_player(pos)) {
|
if (player.has_player(c_pos)) {
|
||||||
other.emplace_back(player.get_session());
|
other.emplace_back(player.get_session());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Arena arena;
|
||||||
|
auto* rsp = Arena::Create<PlayerInfoRsp>(&arena);
|
||||||
|
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);
|
||||||
|
rsp->set_yaw(yaw);
|
||||||
|
rsp->set_pitch(pitch);
|
||||||
|
rsp->set_gait(prsp.gait());
|
||||||
|
auto packet = make_packet(*rsp);
|
||||||
|
|
||||||
for (auto& session : other) {
|
for (auto& session : other) {
|
||||||
if (!session) {
|
if (!session) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Arena arena;
|
|
||||||
auto* rsp = Arena::Create<PlayerInfoRsp>(&arena);
|
session->send(packet, 0);
|
||||||
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);
|
|
||||||
rsp->set_yaw(yaw);
|
|
||||||
rsp->set_pitch(pitch);
|
|
||||||
rsp->set_gait(prsp.gait());
|
|
||||||
session->send(make_packet(*rsp), 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,13 +675,13 @@ void ServerWorld::sync_player_water_sound(const PlayerWaterSound& rsp) {
|
|||||||
p->set_x(x);
|
p->set_x(x);
|
||||||
p->set_y(y);
|
p->set_y(y);
|
||||||
p->set_z(z);
|
p->set_z(z);
|
||||||
|
auto packet = make_packet(*r);
|
||||||
for (auto& session : other) {
|
for (auto& session : other) {
|
||||||
if (!session) {
|
if (!session) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
session->send(make_packet(*r), 5);
|
session->send(packet, 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -765,17 +767,12 @@ void ServerWorld::handle_player_exit(const std::string& uuid) {
|
|||||||
rsp->set_server_stop(false);
|
rsp->set_server_stop(false);
|
||||||
exit_session->send(make_packet(*rsp), 0);
|
exit_session->send(make_packet(*rsp), 0);
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Session>> sessions;
|
auto sessions = get_all_session();
|
||||||
{
|
|
||||||
std::shared_lock lock(m_players_mutex);
|
|
||||||
for (auto& [uuid, player] : m_players) {
|
|
||||||
sessions.emplace_back(player.get_session());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
auto packet = make_packet(*rsp);
|
||||||
for (auto& s : sessions) {
|
for (auto& s : sessions) {
|
||||||
if (s) {
|
if (s) {
|
||||||
s->send(make_packet(*rsp), 0);
|
s->send(packet, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -856,9 +853,9 @@ void ServerWorld::handle_voice_message(VoiceMsg& msg) {
|
|||||||
pos->set_x(p.x);
|
pos->set_x(p.x);
|
||||||
pos->set_y(p.y);
|
pos->set_y(p.y);
|
||||||
pos->set_z(p.z);
|
pos->set_z(p.z);
|
||||||
|
auto packet = make_packet(*msg);
|
||||||
for (auto& s : session) {
|
for (auto& s : session) {
|
||||||
s->send(make_packet(*msg), 5);
|
s->send(packet, 5);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -889,10 +886,10 @@ void ServerWorld::handle_block_change(const BlockChangeReq& req) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
auto packet = make_packet(*rsp);
|
||||||
for (auto& x : sessions) {
|
for (auto& x : sessions) {
|
||||||
if (x) {
|
if (x) {
|
||||||
x->send(make_packet(*rsp), 1);
|
x->send(packet, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -971,9 +968,10 @@ void ServerWorld::send_server_stop() {
|
|||||||
Arena arena;
|
Arena arena;
|
||||||
auto* rsp = Arena::Create<LogoutRsp>(&arena);
|
auto* rsp = Arena::Create<LogoutRsp>(&arena);
|
||||||
rsp->set_server_stop(true);
|
rsp->set_server_stop(true);
|
||||||
std::shared_lock lock(m_players_mutex);
|
auto sessions = get_all_session();
|
||||||
for (auto& [uuid, player] : m_players) {
|
auto packet = make_packet(*rsp);
|
||||||
player.get_session()->send(make_packet(*rsp), 0);
|
for (auto& s : sessions) {
|
||||||
|
s->send(packet, 0);
|
||||||
}
|
}
|
||||||
Logger::info("Send Server Mesaage Success");
|
Logger::info("Send Server Mesaage Success");
|
||||||
}
|
}
|
||||||
@@ -1002,9 +1000,9 @@ void ServerWorld::boardcast_message(const std::string& name,
|
|||||||
|
|
||||||
msg->set_color(std::to_underlying(color));
|
msg->set_color(std::to_underlying(color));
|
||||||
msg->set_system_msg(system_msg);
|
msg->set_system_msg(system_msg);
|
||||||
|
auto packet = make_packet(*msg);
|
||||||
for (auto& s : m_session) {
|
for (auto& s : m_session) {
|
||||||
s->send(make_packet(*msg));
|
s->send(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,4 +28,8 @@ message S2CEntityUpdate {
|
|||||||
Vec3 pos = 2;
|
Vec3 pos = 2;
|
||||||
Vec3 direction = 3;
|
Vec3 direction = 3;
|
||||||
int32 gait = 4;
|
int32 gait = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message S2CEntityUpdateBatch {
|
||||||
|
repeated S2CEntityUpdate updates = 1;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user