Optimize entity updates

This commit is contained in:
tovjemam 2026-03-01 16:40:57 +01:00
parent 02e779fc70
commit 84a8d269de
19 changed files with 332 additions and 204 deletions

View File

@ -220,16 +220,15 @@ void game::Character::UpdateSyncState()
void game::Character::SendUpdateMsg() void game::Character::SendUpdateMsg()
{ {
auto msg = BeginEntMsg(net::EMSG_UPDATE); auto msg = BeginUpdateMsg();
auto fields_pos = msg.Reserve<CharacterSyncFieldFlags>(); auto fields_pos = msg.Reserve<CharacterSyncFieldFlags>();
auto fields = WriteState(msg, sync_[1 - sync_current_]); auto fields = WriteState(msg, sync_[1 - sync_current_]);
// TODO: allow this if (fields == 0)
// if (fields == 0) {
// { DiscardUpdateMsg();
// DiscardMsg(); return;
// return; }
// }
msg.WriteAt(fields_pos, fields); msg.WriteAt(fields_pos, fields);
} }

View File

@ -40,6 +40,12 @@ bool game::Entity::TryUpdate()
return true; return true;
} }
void game::Entity::FinalizeFrame()
{
ResetMsg();
DiscardUpdateMsg();
}
void game::Entity::SetNametag(const std::string& nametag) void game::Entity::SetNametag(const std::string& nametag)
{ {
nametag_ = nametag; nametag_ = nametag;
@ -89,3 +95,14 @@ net::OutMessage game::Entity::BeginEntMsg(net::EntMsgType type)
msg.Write(type); msg.Write(type);
return msg; return msg;
} }
net::OutMessage game::Entity::BeginUpdateMsg()
{
update_msg_buf_.clear(); // make sure
return net::OutMessage(update_msg_buf_);
}
void game::Entity::DiscardUpdateMsg()
{
update_msg_buf_.clear();
}

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <span>
#include "net/msg_producer.hpp" #include "net/msg_producer.hpp"
#include "transform_node.hpp" #include "transform_node.hpp"
@ -29,6 +30,10 @@ public:
bool TryUpdate(); // if not already updated bool TryUpdate(); // if not already updated
int64_t GetUpdateTime() const { return upd_time_; } int64_t GetUpdateTime() const { return upd_time_; }
std::span<const char> GetUpdateMsg() const { return update_msg_buf_; }
void FinalizeFrame();
void SetNametag(const std::string& nametag); void SetNametag(const std::string& nametag);
void Attach(net::EntNum parentnum); void Attach(net::EntNum parentnum);
@ -55,6 +60,8 @@ private:
protected: protected:
net::OutMessage BeginEntMsg(net::EntMsgType type); net::OutMessage BeginEntMsg(net::EntMsgType type);
net::OutMessage BeginUpdateMsg();
void DiscardUpdateMsg();
protected: protected:
World& world_; World& world_;
@ -70,6 +77,9 @@ protected:
private: private:
int64_t upd_time_ = -1; int64_t upd_time_ = -1;
std::vector<char> update_msg_buf_;
std::string nametag_; std::string nametag_;
net::EntNum parentnum_ = 0; net::EntNum parentnum_ = 0;
}; };

View File

@ -106,6 +106,10 @@ void game::Player::SyncEntities()
} }
} }
// list of entities to send update and messages of
static std::vector<const Entity*> upd_ents;
upd_ents.clear();
const auto& ents = world_->GetEntities(); const auto& ents = world_->GetEntities();
auto ent_it = ents.begin(); auto ent_it = ents.begin();
@ -116,36 +120,73 @@ void game::Player::SyncEntities()
const net::EntNum entnum = (ent_it != ents.end() ? ent_it->first : std::numeric_limits<net::EntNum>::max()); const net::EntNum entnum = (ent_it != ents.end() ? ent_it->first : std::numeric_limits<net::EntNum>::max());
const net::EntNum knownum = (know_it != known_ents_.end() ? *know_it : std::numeric_limits<net::EntNum>::max()); const net::EntNum knownum = (know_it != known_ents_.end() ? *know_it : std::numeric_limits<net::EntNum>::max());
if (entnum == knownum) // ----- entity exists and is currently known ----- if (entnum == knownum) // entity exists and is currently known
{ {
const Entity& e = *ent_it->second; const Entity& e = *ent_it->second;
if (ShouldSeeEntity(e)) // still visible? if (ShouldSeeEntity(e)) // still visible?
{ {
SendUpdateEntity(e); // 2) update upd_ents.push_back(&e);
++ent_it; ++ent_it;
++know_it; ++know_it;
} }
else // vanished for player else // not longed visible for player
{ {
SendDestroyEntity(knownum); // 3) destroy SendDestroyEntity(knownum);
know_it = known_ents_.erase(know_it); // remove from known know_it = known_ents_.erase(know_it);
++ent_it; ++ent_it;
} }
} }
else if (entnum < knownum) // ----- entity exists, player does NOT know it ----- else if (entnum < knownum) // entity exists, player does NOT know it
{ {
const Entity& e = *ent_it->second; const Entity& e = *ent_it->second;
if (ShouldSeeEntity(e)) // 1) become visible if (ShouldSeeEntity(e))
{ {
SendInitEntity(e); SendInitEntity(e);
known_ents_.insert(entnum); // add to known known_ents_.insert(entnum);
} // else: stays invisible, nothing to do }
++ent_it; ++ent_it;
} }
else // ----- player knows it, but it no longer exists ----- else // player knows it, but it no longer exists
{ {
SendDestroyEntity(knownum); // 3) destroy SendDestroyEntity(knownum);
know_it = known_ents_.erase(know_it); // remove from known know_it = known_ents_.erase(know_it);
}
}
// write update payload
{
auto msg = BeginMsg(net::MSG_UPDATEENTS);
size_t count_pos = msg.Reserve<net::EntCount>();
net::EntCount count = 0;
net::EntNum lastnum = 0;
for (auto ent : upd_ents)
{
auto ent_upd = ent->GetUpdateMsg();
if (!ent_upd.empty())
{
auto numdiff = ent->GetEntNum() - lastnum;
msg.WriteVarInt(numdiff);
msg.Write(ent_upd);
++count;
lastnum = ent->GetEntNum();
}
}
msg.WriteAt(count_pos, count);
}
// write other entity msgs
for (auto ent : upd_ents)
{
auto ent_msg = ent->GetMsg();
if (!ent_msg.empty())
{
auto msg = BeginMsg();
msg.Write(ent_msg);
} }
} }
} }
@ -171,13 +212,6 @@ void game::Player::SendInitEntity(const Entity& entity)
entity.SendInitData(*this, msg); entity.SendInitData(*this, msg);
} }
void game::Player::SendUpdateEntity(const Entity& entity)
{
MSGDEBUG(std::cout << "seding update ent " << entity.GetEntNum() << std::endl;)
auto msg = BeginMsg(); // no CMD here, these are already included in entity message payload!
msg.Write(entity.GetMsg());
}
void game::Player::SendDestroyEntity(net::EntNum entnum) void game::Player::SendDestroyEntity(net::EntNum entnum)
{ {
MSGDEBUG(std::cout << "seding ENTDESTROY " << entnum << std::endl;) MSGDEBUG(std::cout << "seding ENTDESTROY " << entnum << std::endl;)

View File

@ -45,7 +45,6 @@ private:
void SyncEntities(); void SyncEntities();
bool ShouldSeeEntity(const Entity& entity) const; bool ShouldSeeEntity(const Entity& entity) const;
void SendInitEntity(const Entity& entity); void SendInitEntity(const Entity& entity);
void SendUpdateEntity(const Entity& entity);
void SendDestroyEntity(net::EntNum entnum); void SendDestroyEntity(net::EntNum entnum);
// msg handlers // msg handlers

View File

@ -71,11 +71,18 @@ game::SimpleEntitySyncFieldFlags game::SimpleEntity::WriteState(net::OutMessage&
void game::SimpleEntity::SendUpdateMsg() void game::SimpleEntity::SendUpdateMsg()
{ {
auto msg = BeginEntMsg(net::EMSG_UPDATE); auto msg = BeginUpdateMsg();
// write state against previous // write state against previous
const SimpleEntitySyncState& prev = sync_[1 - sync_current_]; const SimpleEntitySyncState& prev = sync_[1 - sync_current_];
size_t fields_pos = msg.Reserve<SimpleEntitySyncFieldFlags>(); size_t fields_pos = msg.Reserve<SimpleEntitySyncFieldFlags>();
auto fields = WriteState(msg, prev); auto fields = WriteState(msg, prev);
if (fields == 0)
{
DiscardUpdateMsg();
return;
}
msg.WriteAt(fields_pos, fields); msg.WriteAt(fields_pos, fields);
} }

View File

@ -456,16 +456,15 @@ game::VehicleSyncFieldFlags game::Vehicle::WriteState(net::OutMessage& msg, cons
void game::Vehicle::SendUpdateMsg() void game::Vehicle::SendUpdateMsg()
{ {
auto msg = BeginEntMsg(net::EMSG_UPDATE); auto msg = BeginUpdateMsg();
auto fields_pos = msg.Reserve<VehicleSyncFieldFlags>(); auto fields_pos = msg.Reserve<VehicleSyncFieldFlags>();
auto fields = WriteState(msg, sync_[1 - sync_current_]); auto fields = WriteState(msg, sync_[1 - sync_current_]);
// TODO: allow this if (fields == 0)
// if (fields == 0) {
// { DiscardUpdateMsg();
// DiscardMsg(); return;
// return; }
// }
msg.WriteAt(fields_pos, fields); msg.WriteAt(fields_pos, fields);
} }

View File

@ -73,7 +73,7 @@ void game::World::FinishFrame()
// reset ent msgs // reset ent msgs
for (auto& [entnum, ent] : ents_) for (auto& [entnum, ent] : ents_)
{ {
ent->ResetMsg(); ent->FinalizeFrame();
} }
} }

View File

@ -30,11 +30,10 @@ game::view::CharacterView::CharacterView(WorldView& world, net::InMessage& msg)
UpdateSurfaceMask(); UpdateSurfaceMask();
// read initial state // read initial state
if (!ReadState(msg)) if (!ReadState(&msg))
throw EntityInitError(); throw EntityInitError();
states_[0] = states_[1]; // lerp from the read state to avoid jump OnAttach();
radius_ = 2.0f; radius_ = 2.0f;
} }
@ -43,18 +42,16 @@ bool game::view::CharacterView::ProcessMsg(net::EntMsgType type, net::InMessage&
{ {
switch (type) switch (type)
{ {
case net::EMSG_UPDATE:
return ProcessUpdateMsg(msg);
case net::EMSG_ATTACH:
skip_lerps_ = 1;
return Super::ProcessMsg(type, msg);
default: default:
return Super::ProcessMsg(type, msg); return Super::ProcessMsg(type, msg);
} }
} }
bool game::view::CharacterView::ProcessUpdateMsg(net::InMessage* msg)
{
return ReadState(msg);
}
void game::view::CharacterView::Update(const UpdateInfo& info) void game::view::CharacterView::Update(const UpdateInfo& info)
{ {
Super::Update(info); Super::Update(info);
@ -66,7 +63,12 @@ void game::view::CharacterView::Update(const UpdateInfo& info)
root_.local = Transform::Lerp(states_[0].trans, states_[1].trans, t); root_.local = Transform::Lerp(states_[0].trans, states_[1].trans, t);
animstate_.loco_blend = glm::mix(states_[0].loco_blend, states_[1].loco_blend, t); animstate_.loco_blend = glm::mix(states_[0].loco_blend, states_[1].loco_blend, t);
animstate_.loco_phase = glm::mod(glm::mix(states_[0].loco_phase, states_[1].loco_phase, t), 1.0f);
float loco_phase0 = states_[0].loco_phase;
float loco_phase1 = states_[1].loco_phase;
if (loco_phase0 > loco_phase1)
loco_phase0 -= 1.0f;
animstate_.loco_phase = glm::mod(glm::mix(loco_phase0, loco_phase1, t), 1.0f);
animstate_.ApplyToSkeleton(sk_); animstate_.ApplyToSkeleton(sk_);
@ -137,7 +139,17 @@ void game::view::CharacterView::Draw(const DrawArgs& args)
} }
} }
bool game::view::CharacterView::ReadState(net::InMessage& msg) void game::view::CharacterView::OnAttach()
{
states_[0] = states_[1]; // lerp from the read state to avoid jump
// init view state
root_.local = states_[0].trans;
animstate_.loco_blend = states_[0].loco_blend;
animstate_.loco_phase = states_[0].loco_phase;
}
bool game::view::CharacterView::ReadState(net::InMessage* msg)
{ {
update_time_ = world_.GetTime(); update_time_ = world_.GetTime();
@ -148,66 +160,55 @@ bool game::view::CharacterView::ReadState(net::InMessage& msg)
auto& new_state = states_[1]; auto& new_state = states_[1];
// parse state delta if (msg)
CharacterSyncFieldFlags fields;
if (!msg.Read(fields))
return false;
// transform
if (fields & CSF_TRANSFORM)
{ {
if (!net::ReadDelta(msg, sync_.pos.x) || !net::ReadDelta(msg, sync_.pos.y) || // parse state delta
!net::ReadDelta(msg, sync_.pos.z) || !net::ReadDelta(msg, sync_.yaw)) CharacterSyncFieldFlags fields;
if (!msg->Read(fields))
return false; return false;
net::DecodePosition(sync_.pos, new_state.trans.position); // transform
new_state.trans.rotation = glm::rotate(glm::quat(1.0f, 0.0f, 0.0f, 0.0f), if (fields & CSF_TRANSFORM)
sync_.yaw.Decode() + glm::pi<float>() * 0.5f, glm::vec3(0, 0, 1)); {
} if (!net::ReadDelta(*msg, sync_.pos.x) || !net::ReadDelta(*msg, sync_.pos.y) ||
!net::ReadDelta(*msg, sync_.pos.z) || !net::ReadDelta(*msg, sync_.yaw))
return false;
if (fields & CSF_IDLE_ANIM) net::DecodePosition(sync_.pos, new_state.trans.position);
{ new_state.trans.rotation = glm::rotate(glm::quat(1.0f, 0.0f, 0.0f, 0.0f),
if (!msg.Read(sync_.idle_anim)) sync_.yaw.Decode() + glm::pi<float>() * 0.5f, glm::vec3(0, 0, 1));
return false; }
animstate_.idle_anim_idx = sync_.idle_anim; if (fields & CSF_IDLE_ANIM)
} {
if (!msg->Read(sync_.idle_anim))
return false;
if (fields & CSF_LOCO_ANIMS) animstate_.idle_anim_idx = sync_.idle_anim;
{ }
if (!msg.Read(sync_.walk_anim) || !msg.Read(sync_.run_anim))
return false;
animstate_.walk_anim_idx = sync_.walk_anim; if (fields & CSF_LOCO_ANIMS)
animstate_.run_anim_idx = sync_.run_anim; {
} if (!msg->Read(sync_.walk_anim) || !msg->Read(sync_.run_anim))
return false;
if (fields & CSF_LOCO_VALS) animstate_.walk_anim_idx = sync_.walk_anim;
{ animstate_.run_anim_idx = sync_.run_anim;
if (!net::ReadDelta(msg, sync_.loco_blend) || !net::ReadDelta(msg, sync_.loco_phase)) }
return false;
new_state.loco_blend = sync_.loco_blend.Decode(); if (fields & CSF_LOCO_VALS)
new_state.loco_phase = sync_.loco_phase.Decode(); {
if (!net::ReadDelta(*msg, sync_.loco_blend) || !net::ReadDelta(*msg, sync_.loco_phase))
return false;
if (new_state.loco_phase < states_[0].loco_phase) new_state.loco_blend = sync_.loco_blend.Decode();
states_[0].loco_phase -= 1.0f; new_state.loco_phase = sync_.loco_phase.Decode();
} }
if (skip_lerps_ > 0)
{
states_[0] = states_[1];
skip_lerps_--;
} }
return true; return true;
} }
bool game::view::CharacterView::ProcessUpdateMsg(net::InMessage& msg)
{
return ReadState(msg);
}
game::view::CharacterView::SurfaceMask game::view::CharacterView::GetSurfaceMask(const std::string& name) game::view::CharacterView::SurfaceMask game::view::CharacterView::GetSurfaceMask(const std::string& name)
{ {
const auto& surface_names = basemodel_->GetMesh()->surface_names; const auto& surface_names = basemodel_->GetMesh()->surface_names;

View File

@ -34,12 +34,15 @@ public:
DELETE_COPY_MOVE(CharacterView) DELETE_COPY_MOVE(CharacterView)
virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg) override; virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg) override;
virtual bool ProcessUpdateMsg(net::InMessage* msg) override;
virtual void Update(const UpdateInfo& info) override; virtual void Update(const UpdateInfo& info) override;
virtual void Draw(const DrawArgs& args) override; virtual void Draw(const DrawArgs& args) override;
protected:
virtual void OnAttach() override;
private: private:
bool ReadState(net::InMessage& msg); bool ReadState(net::InMessage* msg);
bool ProcessUpdateMsg(net::InMessage& msg);
SurfaceMask GetSurfaceMask(const std::string& name); SurfaceMask GetSurfaceMask(const std::string& name);
void UpdateSurfaceMask(); void UpdateSurfaceMask();
@ -63,7 +66,6 @@ private:
CharacterSyncState sync_; CharacterSyncState sync_;
CharacterViewState states_[2]; CharacterViewState states_[2];
float update_time_ = 0.0f; float update_time_ = 0.0f;
size_t skip_lerps_ = 0;
}; };
} }

View File

@ -30,6 +30,11 @@ bool game::view::EntityView::ProcessMsg(net::EntMsgType type, net::InMessage& ms
} }
} }
bool game::view::EntityView::ProcessUpdateMsg(net::InMessage* msg)
{
return true;
}
bool game::view::EntityView::TryUpdate(const UpdateInfo& info) bool game::view::EntityView::TryUpdate(const UpdateInfo& info)
{ {
float time = world_.GetTime(); float time = world_.GetTime();
@ -81,7 +86,12 @@ bool game::view::EntityView::ReadNametag(net::InMessage& msg)
bool game::view::EntityView::ReadAttach(net::InMessage& msg) bool game::view::EntityView::ReadAttach(net::InMessage& msg)
{ {
return msg.Read(parentnum_); if (!msg.Read(parentnum_))
return false;
OnAttach();
return true;
} }
bool game::view::EntityView::ProcessPlaySoundMsg(net::InMessage& msg) bool game::view::EntityView::ProcessPlaySoundMsg(net::InMessage& msg)

View File

@ -36,6 +36,7 @@ public:
DELETE_COPY_MOVE(EntityView) DELETE_COPY_MOVE(EntityView)
virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg); virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg);
virtual bool ProcessUpdateMsg(net::InMessage* msg);
bool TryUpdate(const UpdateInfo& info); // if not updated already bool TryUpdate(const UpdateInfo& info); // if not updated already
virtual void Update(const UpdateInfo& info); virtual void Update(const UpdateInfo& info);
@ -47,6 +48,9 @@ public:
virtual ~EntityView() = default; virtual ~EntityView() = default;
protected:
virtual void OnAttach() {}
private: private:
bool ReadNametag(net::InMessage& msg); bool ReadNametag(net::InMessage& msg);
bool ReadAttach(net::InMessage& msg); bool ReadAttach(net::InMessage& msg);

View File

@ -17,10 +17,11 @@ game::view::SimpleEntityView::SimpleEntityView(WorldView& world, net::InMessage&
throw EntityInitError(); throw EntityInitError();
} }
if (!ReadState(msg)) if (!ReadState(&msg))
throw EntityInitError(); throw EntityInitError();
states_[0] = states_[1]; // lerp from the read state to avoid jump states_[0] = states_[1]; // lerp from the read state to avoid jump
root_.local = states_[0].trans;
radius_ = 20.0f; radius_ = 20.0f;
} }
@ -29,14 +30,16 @@ bool game::view::SimpleEntityView::ProcessMsg(net::EntMsgType type, net::InMessa
{ {
switch (type) switch (type)
{ {
case net::EMSG_UPDATE:
return ProcessUpdateMsg(msg);
default: default:
return Super::ProcessMsg(type, msg); return Super::ProcessMsg(type, msg);
} }
} }
bool game::view::SimpleEntityView::ProcessUpdateMsg(net::InMessage* msg)
{
return ReadState(msg);
}
void game::view::SimpleEntityView::Update(const UpdateInfo& info) void game::view::SimpleEntityView::Update(const UpdateInfo& info)
{ {
Super::Update(info); Super::Update(info);
@ -67,7 +70,7 @@ void game::view::SimpleEntityView::Draw(const DrawArgs& args)
} }
} }
bool game::view::SimpleEntityView::ReadState(net::InMessage& msg) bool game::view::SimpleEntityView::ReadState(net::InMessage* msg)
{ {
update_time_ = world_.GetTime(); update_time_ = world_.GetTime();
@ -76,33 +79,31 @@ bool game::view::SimpleEntityView::ReadState(net::InMessage& msg)
auto& new_state = states_[1]; auto& new_state = states_[1];
// parse state delta if (msg)
SimpleEntitySyncFieldFlags fields;
if (!msg.Read(fields))
return false;
// pos
if (fields & SESF_POSITION)
{ {
if (!net::ReadDelta(msg, sync_.pos.x) || !net::ReadDelta(msg, sync_.pos.y) || !net::ReadDelta(msg, sync_.pos.z)) // parse state delta
SimpleEntitySyncFieldFlags fields;
if (!msg->Read(fields))
return false; return false;
net::DecodePosition(sync_.pos, new_state.trans.position); // pos
} if (fields & SESF_POSITION)
{
if (!net::ReadDelta(*msg, sync_.pos.x) || !net::ReadDelta(*msg, sync_.pos.y) || !net::ReadDelta(*msg, sync_.pos.z))
return false;
// rot net::DecodePosition(sync_.pos, new_state.trans.position);
if (fields & SESF_ROTATION) }
{
if (!net::ReadDelta(msg, sync_.rot.x) || !net::ReadDelta(msg, sync_.rot.y) || !net::ReadDelta(msg, sync_.rot.z))
return false;
net::DecodeRotation(sync_.rot, new_state.trans.rotation); // rot
if (fields & SESF_ROTATION)
{
if (!net::ReadDelta(*msg, sync_.rot.x) || !net::ReadDelta(*msg, sync_.rot.y) || !net::ReadDelta(*msg, sync_.rot.z))
return false;
net::DecodeRotation(sync_.rot, new_state.trans.rotation);
}
} }
return true; return true;
} }
bool game::view::SimpleEntityView::ProcessUpdateMsg(net::InMessage& msg)
{
return ReadState(msg);
}

View File

@ -20,13 +20,12 @@ public:
SimpleEntityView(WorldView& world, net::InMessage& msg); SimpleEntityView(WorldView& world, net::InMessage& msg);
virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg) override; virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg) override;
virtual bool ProcessUpdateMsg(net::InMessage* msg) override;
virtual void Update(const UpdateInfo& info) override; virtual void Update(const UpdateInfo& info) override;
virtual void Draw(const DrawArgs& args) override; virtual void Draw(const DrawArgs& args) override;
private: private:
bool ReadState(net::InMessage& msg); bool ReadState(net::InMessage* msg);
bool ProcessUpdateMsg(net::InMessage& msg);
private: private:
std::shared_ptr<const assets::Model> model_; std::shared_ptr<const assets::Model> model_;

View File

@ -27,19 +27,15 @@ game::view::VehicleView::VehicleView(WorldView& world, net::InMessage& msg)
color_ = glm::vec4(color, 1.0f); color_ = glm::vec4(color, 1.0f);
if (!ReadState(msg)) if (!ReadState(&msg))
throw EntityInitError(); throw EntityInitError();
// init the other transform to identical // init the other transform to identical
root_trans_[0] = root_trans_[1]; root_trans_[0] = root_trans_[1];
root_.local = root_trans_[0];
snd_accel_ = assets::CacheManager::GetSound("data/auto.snd"); snd_accel_ = assets::CacheManager::GetSound("data/auto.snd");
// sync state
net::DecodePosition(sync_.pos, root_.local.position);
net::DecodeRotation(sync_.rot, root_.local.rotation);
radius_ = 3.0f; radius_ = 3.0f;
} }
@ -47,14 +43,16 @@ bool game::view::VehicleView::ProcessMsg(net::EntMsgType type, net::InMessage& m
{ {
switch (type) switch (type)
{ {
case net::EMSG_UPDATE:
return ProcessUpdateMsg(msg);
default: default:
return Super::ProcessMsg(type, msg); return Super::ProcessMsg(type, msg);
} }
} }
bool game::view::VehicleView::ProcessUpdateMsg(net::InMessage* msg)
{
return ReadState(msg);
}
void game::view::VehicleView::Update(const UpdateInfo& info) void game::view::VehicleView::Update(const UpdateInfo& info)
{ {
Super::Update(info); Super::Update(info);
@ -145,81 +143,78 @@ void game::view::VehicleView::Draw(const DrawArgs& args)
} }
} }
bool game::view::VehicleView::ReadState(net::InMessage& msg) bool game::view::VehicleView::ReadState(net::InMessage* msg)
{ {
root_trans_[0] = root_.local; root_trans_[0] = root_.local;
auto& root_trans = root_trans_[1]; auto& root_trans = root_trans_[1];
update_time_ = world_.GetTime(); update_time_ = world_.GetTime();
// parse state delta if (msg)
VehicleSyncFieldFlags fields;
if (!msg.Read(fields))
return false;
// flags
if (fields & VSF_FLAGS)
{ {
if (!msg.Read(flags_)) // parse state delta
return false; VehicleSyncFieldFlags fields;
} if (!msg->Read(fields))
// pos
if (fields & VSF_POSITION)
{
if (!net::ReadDelta(msg, sync_.pos.x) ||
!net::ReadDelta(msg, sync_.pos.y) ||
!net::ReadDelta(msg, sync_.pos.z))
return false; return false;
net::DecodePosition(sync_.pos, root_trans.position); // flags
} if (fields & VSF_FLAGS)
// rot
if (fields & VSF_ROTATION)
{
if (!net::ReadDelta(msg, sync_.rot.x) ||
!net::ReadDelta(msg, sync_.rot.y) ||
!net::ReadDelta(msg, sync_.rot.z))
return false;
net::DecodeRotation(sync_.rot, root_trans.rotation);
}
// steering
if (fields & VSF_STEERING)
{
if (!net::ReadDelta(msg, sync_.steering))
return false;
}
float steering = sync_.steering.Decode();
// wheels
if (fields & VSF_WHEELS)
{
for (size_t i = 0; i < wheels_.size(); ++i)
{ {
if (!net::ReadDelta(msg, sync_.wheels[i].z_offset) || if (!msg->Read(flags_))
!net::ReadDelta(msg, sync_.wheels[i].speed))
return false; return false;
} }
// pos
if (fields & VSF_POSITION)
{
if (!net::ReadDelta(*msg, sync_.pos.x) ||
!net::ReadDelta(*msg, sync_.pos.y) ||
!net::ReadDelta(*msg, sync_.pos.z))
return false;
net::DecodePosition(sync_.pos, root_trans.position);
}
// rot
if (fields & VSF_ROTATION)
{
if (!net::ReadDelta(*msg, sync_.rot.x) ||
!net::ReadDelta(*msg, sync_.rot.y) ||
!net::ReadDelta(*msg, sync_.rot.z))
return false;
net::DecodeRotation(sync_.rot, root_trans.rotation);
}
// steering
if (fields & VSF_STEERING)
{
if (!net::ReadDelta(*msg, sync_.steering))
return false;
}
float steering = sync_.steering.Decode();
// wheels
if (fields & VSF_WHEELS)
{
for (size_t i = 0; i < wheels_.size(); ++i)
{
if (!net::ReadDelta(*msg, sync_.wheels[i].z_offset) ||
!net::ReadDelta(*msg, sync_.wheels[i].speed))
return false;
}
}
const auto& wheels = model_->GetWheels();
for (size_t i = 0; i < wheels_.size(); ++i)
{
auto& wheel = wheels_[i];
wheel.z_offset = sync_.wheels[i].z_offset.Decode();
wheel.speed = sync_.wheels[i].speed.Decode();
wheel.steering = i < 2 ? steering : 0.0f;
}
} }
const auto& wheels = model_->GetWheels();
for (size_t i = 0; i < wheels_.size(); ++i)
{
auto& wheel = wheels_[i];
wheel.z_offset = sync_.wheels[i].z_offset.Decode();
wheel.speed = sync_.wheels[i].speed.Decode();
wheel.steering = i < 2 ? steering : 0.0f;
}
return true; return true;
} }
bool game::view::VehicleView::ProcessUpdateMsg(net::InMessage& msg)
{
return ReadState(msg);
}

View File

@ -27,12 +27,12 @@ public:
DELETE_COPY_MOVE(VehicleView) DELETE_COPY_MOVE(VehicleView)
virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg) override; virtual bool ProcessMsg(net::EntMsgType type, net::InMessage& msg) override;
virtual bool ProcessUpdateMsg(net::InMessage* msg) override;
virtual void Update(const UpdateInfo& info) override; virtual void Update(const UpdateInfo& info) override;
virtual void Draw(const DrawArgs& args) override; virtual void Draw(const DrawArgs& args) override;
private: private:
bool ReadState(net::InMessage& msg); bool ReadState(net::InMessage* msg);
bool ProcessUpdateMsg(net::InMessage& msg);
private: private:
std::shared_ptr<const assets::VehicleModel> model_; std::shared_ptr<const assets::VehicleModel> model_;

View File

@ -31,8 +31,9 @@ game::view::WorldView::WorldView(ClientSession& session, net::InMessage& msg) :
// cache common snds and stuff // cache common snds and stuff
Cache(assets::CacheManager::GetSound("data/breaksign.snd")); Cache(assets::CacheManager::GetSound("data/breaksign.snd"));
Cache(assets::CacheManager::GetSound("data/breakpatnik.snd")); Cache(assets::CacheManager::GetSound("data/breakpatnik.snd"));
Cache(assets::CacheManager::GetSound("data/breakwood.snd"));
Cache(assets::CacheManager::GetSound("data/cardoor.snd"));
Cache(assets::CacheManager::GetSound("data/crash.snd")); Cache(assets::CacheManager::GetSound("data/crash.snd"));
Cache(assets::CacheManager::GetSound("data/breakwindow.snd"));
} }
bool game::view::WorldView::ProcessMsg(net::MessageType type, net::InMessage& msg) bool game::view::WorldView::ProcessMsg(net::MessageType type, net::InMessage& msg)
@ -45,6 +46,9 @@ bool game::view::WorldView::ProcessMsg(net::MessageType type, net::InMessage& ms
case net::MSG_ENTMSG: case net::MSG_ENTMSG:
return ProcessEntMsgMsg(msg); return ProcessEntMsgMsg(msg);
case net::MSG_UPDATEENTS:
return ProcessUpdateEntsMsg(msg);
case net::MSG_ENTDESTROY: case net::MSG_ENTDESTROY:
return ProcessEntDestroyMsg(msg); return ProcessEntDestroyMsg(msg);
@ -172,6 +176,49 @@ bool game::view::WorldView::ProcessEntMsgMsg(net::InMessage& msg)
return ent_it->second->ProcessMsg(type, msg); return ent_it->second->ProcessMsg(type, msg);
} }
bool game::view::WorldView::ProcessUpdateEntsMsg(net::InMessage& msg)
{
net::EntCount count;
if (!msg.Read(count))
return false;
net::EntNum current = 0;
auto it = ents_.begin();
for (net::EntNum i = 0; i < count; ++i)
{
int64_t diff;
if (!msg.ReadVarInt(diff))
return false;
current += static_cast<net::EntNum>(diff);
while (it->first < current)
{
it->second->ProcessUpdateMsg(nullptr); // blank update
++it;
if (it == ents_.end())
return false;
}
if (it->first != current)
{
return false; // exact num wasnt found and it overrun
}
if (!it->second->ProcessUpdateMsg(&msg))
return false;
}
// process remaining
for (; it != ents_.end(); ++it)
{
it->second->ProcessUpdateMsg(nullptr);
}
return true;
}
bool game::view::WorldView::ProcessEntDestroyMsg(net::InMessage& msg) bool game::view::WorldView::ProcessEntDestroyMsg(net::InMessage& msg)
{ {
net::EntNum entnum; net::EntNum entnum;

View File

@ -36,6 +36,7 @@ private:
// msg handlers // msg handlers
bool ProcessEntSpawnMsg(net::InMessage& msg); bool ProcessEntSpawnMsg(net::InMessage& msg);
bool ProcessEntMsgMsg(net::InMessage& msg); bool ProcessEntMsgMsg(net::InMessage& msg);
bool ProcessUpdateEntsMsg(net::InMessage& msg);
bool ProcessEntDestroyMsg(net::InMessage& msg); bool ProcessEntDestroyMsg(net::InMessage& msg);
bool ProcessObjDestroyOrRespawnMsg(net::InMessage& msg, bool enable); bool ProcessObjDestroyOrRespawnMsg(net::InMessage& msg, bool enable);

View File

@ -39,6 +39,8 @@ enum MessageType : uint8_t
MSG_ENTSPAWN, MSG_ENTSPAWN,
// ENTMSG <EntNum> data... // ENTMSG <EntNum> data...
MSG_ENTMSG, MSG_ENTMSG,
// UPDATEENTS <EntCount> ...
MSG_UPDATEENTS,
// ENTDESTROY <EntNum> // ENTDESTROY <EntNum>
MSG_ENTDESTROY, MSG_ENTDESTROY,
@ -67,6 +69,7 @@ using ViewPitchQ = Quantized<uint16_t, -PI_N, PI_N, PI_D>;
// entities // entities
using EntNum = uint16_t; using EntNum = uint16_t;
using EntCount = EntNum;
enum EntType : uint8_t enum EntType : uint8_t
{ {
@ -85,7 +88,7 @@ enum EntMsgType : uint8_t
EMSG_NAMETAG, EMSG_NAMETAG,
EMSG_ATTACH, EMSG_ATTACH,
EMSG_UPDATE, // EMSG_UPDATE, // deprecated
EMSG_PLAYSOUND, EMSG_PLAYSOUND,
}; };