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_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 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_DESTORY_REQUEST = 3010,
|
||||
S2C_ENTITY_UPDATE = 3011,
|
||||
S2C_ENTITY_UPDATE_BATCH = 3012,
|
||||
|
||||
CHAT_MSG = 4001,
|
||||
VOICE_MSG = 4002,
|
||||
@@ -153,6 +154,9 @@ template <> constexpr uint16_t get_packet_id<VoiceMsg>() {
|
||||
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>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/entity.hpp"
|
||||
#include "Cubed/gameplay/gait.hpp"
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
@@ -26,6 +27,14 @@ private:
|
||||
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;
|
||||
@@ -47,9 +56,8 @@ private:
|
||||
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<std::shared_ptr<Session>>& sessions);
|
||||
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();
|
||||
|
||||
@@ -87,7 +87,7 @@ void ClientEntityManager::receive_entity_destory(EntityID id) {
|
||||
m_tasks.emplace(Command::DESTORY, id);
|
||||
}
|
||||
|
||||
void ClientEntityManager::receive_entity_update(S2CEntityUpdate& msg) {
|
||||
void ClientEntityManager::receive_entity_update(const S2CEntityUpdate& msg) {
|
||||
UpdateInfo e;
|
||||
e.id = msg.id();
|
||||
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));
|
||||
}
|
||||
|
||||
void ClientEntityManager::receive_entity_update(S2CEntityUpdateBatch& msg) {
|
||||
for (auto& u : msg.updates()) {
|
||||
receive_entity_update(u);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientEntityManager::destory(EntityID id) {
|
||||
auto client = m_world.get_client();
|
||||
Arena arena;
|
||||
|
||||
@@ -162,6 +162,12 @@ asio::awaitable<void> NetworkClient::read_loop() {
|
||||
m_world.entity_manager().receive_entity_update(*msg);
|
||||
}
|
||||
} 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) {
|
||||
|
||||
@@ -45,11 +45,12 @@ void ServerEntityManager::update() {
|
||||
for (auto e : view) {
|
||||
entities.push_back(e);
|
||||
}
|
||||
tbb::concurrent_vector<EntitySendData> send_data;
|
||||
// parallel block touches disjoint entities only;
|
||||
// structural registry changes stay on the server thread via m_tasks.
|
||||
parallel_do(
|
||||
*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);
|
||||
if (!m_world.get_chunk_ref_count(c.transform.position.value)) {
|
||||
const auto& entity = m_registry.get<Entity>(e);
|
||||
@@ -58,8 +59,23 @@ void ServerEntityManager::update() {
|
||||
}
|
||||
update_ai(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) {
|
||||
@@ -74,8 +90,7 @@ void ServerEntityManager::update_move(entt::entity e) {
|
||||
}
|
||||
|
||||
void ServerEntityManager::update_send(
|
||||
entt::entity e,
|
||||
tbb::concurrent_vector<std::shared_ptr<Session>>& sessions) {
|
||||
entt::entity e, tbb::concurrent_vector<EntitySendData>& send_data) {
|
||||
|
||||
if (!m_registry.all_of<Entity, BaseServerCreature>(e)) {
|
||||
return;
|
||||
@@ -83,23 +98,17 @@ void ServerEntityManager::update_send(
|
||||
|
||||
const auto [entity, creature] =
|
||||
m_registry.get<Entity, BaseServerCreature>(e);
|
||||
Arena arena;
|
||||
auto* p = Arena::Create<S2CEntityUpdate>(&arena);
|
||||
p->set_id(entity.id);
|
||||
Tools::set_net_pos(p, creature.transform.position.value);
|
||||
|
||||
Tools::set_net_vec3(p->mutable_direction(),
|
||||
creature.transform.direction.value);
|
||||
EntitySendData data;
|
||||
data.id = entity.id;
|
||||
data.pos = creature.transform.position.value;
|
||||
data.dir = creature.transform.direction.value;
|
||||
const auto& v = creature.velocity.value;
|
||||
if (v.x * v.x + v.z * v.z > 1e-4f) {
|
||||
p->set_gait(get_gait_id(Gait::WALK));
|
||||
data.gait = Gait::WALK;
|
||||
} else {
|
||||
p->set_gait(get_gait_id(Gait::STOP));
|
||||
}
|
||||
|
||||
for (auto& s : sessions) {
|
||||
s->send(make_packet(p));
|
||||
data.gait = Gait::STOP;
|
||||
}
|
||||
send_data.emplace_back(std::move(data));
|
||||
}
|
||||
|
||||
void ServerEntityManager::handle_task() {
|
||||
@@ -177,9 +186,9 @@ void ServerEntityManager::handle_entity_create(EntityID id,
|
||||
s2c->set_id(id);
|
||||
s2c->set_name(name);
|
||||
Tools::set_net_pos(s2c, pos);
|
||||
|
||||
auto packet = make_packet(*s2c);
|
||||
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;
|
||||
auto* s2c = Arena::Create<S2CEntityDestory>(&arena);
|
||||
s2c->set_id(id);
|
||||
auto packet = make_packet(*s2c);
|
||||
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_game_tick(m_game_ticks);
|
||||
{
|
||||
std::shared_lock lock(m_players_mutex);
|
||||
for (auto& [uuid, player] : m_players) {
|
||||
player.get_session()->send(make_packet(*rsp), 3);
|
||||
}
|
||||
auto sessions = get_all_session();
|
||||
auto packet = make_packet(*rsp);
|
||||
for (auto& s : sessions) {
|
||||
s->send(packet, 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -608,7 +607,7 @@ void ServerWorld::sync_player_pos(const C2S_PlayerInfo& prsp) {
|
||||
|
||||
name = it->second.get_name();
|
||||
}
|
||||
ChunkPos pos = get_chunk_pos(x, z);
|
||||
ChunkPos c_pos = get_chunk_pos(x, z);
|
||||
// update other player pos;
|
||||
std::vector<std::shared_ptr<Session>> other;
|
||||
{
|
||||
@@ -617,16 +616,12 @@ void ServerWorld::sync_player_pos(const C2S_PlayerInfo& prsp) {
|
||||
if (o_uuid == uuid) {
|
||||
continue;
|
||||
}
|
||||
if (player.has_player(pos)) {
|
||||
if (player.has_player(c_pos)) {
|
||||
other.emplace_back(player.get_session());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& session : other) {
|
||||
if (!session) {
|
||||
continue;
|
||||
}
|
||||
Arena arena;
|
||||
auto* rsp = Arena::Create<PlayerInfoRsp>(&arena);
|
||||
rsp->set_uuid(uuid);
|
||||
@@ -638,7 +633,14 @@ void ServerWorld::sync_player_pos(const C2S_PlayerInfo& prsp) {
|
||||
rsp->set_yaw(yaw);
|
||||
rsp->set_pitch(pitch);
|
||||
rsp->set_gait(prsp.gait());
|
||||
session->send(make_packet(*rsp), 0);
|
||||
auto packet = make_packet(*rsp);
|
||||
|
||||
for (auto& session : other) {
|
||||
if (!session) {
|
||||
continue;
|
||||
}
|
||||
|
||||
session->send(packet, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,13 +675,13 @@ void ServerWorld::sync_player_water_sound(const PlayerWaterSound& rsp) {
|
||||
p->set_x(x);
|
||||
p->set_y(y);
|
||||
p->set_z(z);
|
||||
|
||||
auto packet = make_packet(*r);
|
||||
for (auto& session : other) {
|
||||
if (!session) {
|
||||
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);
|
||||
exit_session->send(make_packet(*rsp), 0);
|
||||
|
||||
std::vector<std::shared_ptr<Session>> sessions;
|
||||
{
|
||||
std::shared_lock lock(m_players_mutex);
|
||||
for (auto& [uuid, player] : m_players) {
|
||||
sessions.emplace_back(player.get_session());
|
||||
}
|
||||
}
|
||||
auto sessions = get_all_session();
|
||||
|
||||
auto packet = make_packet(*rsp);
|
||||
for (auto& s : sessions) {
|
||||
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_y(p.y);
|
||||
pos->set_z(p.z);
|
||||
|
||||
auto packet = make_packet(*msg);
|
||||
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) {
|
||||
if (x) {
|
||||
x->send(make_packet(*rsp), 1);
|
||||
x->send(packet, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -971,9 +968,10 @@ void ServerWorld::send_server_stop() {
|
||||
Arena arena;
|
||||
auto* rsp = Arena::Create<LogoutRsp>(&arena);
|
||||
rsp->set_server_stop(true);
|
||||
std::shared_lock lock(m_players_mutex);
|
||||
for (auto& [uuid, player] : m_players) {
|
||||
player.get_session()->send(make_packet(*rsp), 0);
|
||||
auto sessions = get_all_session();
|
||||
auto packet = make_packet(*rsp);
|
||||
for (auto& s : sessions) {
|
||||
s->send(packet, 0);
|
||||
}
|
||||
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_system_msg(system_msg);
|
||||
|
||||
auto packet = make_packet(*msg);
|
||||
for (auto& s : m_session) {
|
||||
s->send(make_packet(*msg));
|
||||
s->send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,3 +29,7 @@ message S2CEntityUpdate {
|
||||
Vec3 direction = 3;
|
||||
int32 gait = 4;
|
||||
}
|
||||
|
||||
message S2CEntityUpdateBatch {
|
||||
repeated S2CEntityUpdate updates = 1;
|
||||
}
|
||||
Reference in New Issue
Block a user