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()
{
auto msg = BeginEntMsg(net::EMSG_UPDATE);
auto msg = BeginUpdateMsg();
auto fields_pos = msg.Reserve<CharacterSyncFieldFlags>();
auto fields = WriteState(msg, sync_[1 - sync_current_]);
// TODO: allow this
// if (fields == 0)
// {
// DiscardMsg();
// return;
// }
if (fields == 0)
{
DiscardUpdateMsg();
return;
}
msg.WriteAt(fields_pos, fields);
}

View File

@ -40,6 +40,12 @@ bool game::Entity::TryUpdate()
return true;
}
void game::Entity::FinalizeFrame()
{
ResetMsg();
DiscardUpdateMsg();
}
void game::Entity::SetNametag(const std::string& nametag)
{
nametag_ = nametag;
@ -89,3 +95,14 @@ net::OutMessage game::Entity::BeginEntMsg(net::EntMsgType type)
msg.Write(type);
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
#include <vector>
#include <span>
#include "net/msg_producer.hpp"
#include "transform_node.hpp"
@ -29,6 +30,10 @@ public:
bool TryUpdate(); // if not already updated
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 Attach(net::EntNum parentnum);
@ -55,6 +60,8 @@ private:
protected:
net::OutMessage BeginEntMsg(net::EntMsgType type);
net::OutMessage BeginUpdateMsg();
void DiscardUpdateMsg();
protected:
World& world_;
@ -70,6 +77,9 @@ protected:
private:
int64_t upd_time_ = -1;
std::vector<char> update_msg_buf_;
std::string nametag_;
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();
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 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;
if (ShouldSeeEntity(e)) // still visible?
{
SendUpdateEntity(e); // 2) update
upd_ents.push_back(&e);
++ent_it;
++know_it;
}
else // vanished for player
else // not longed visible for player
{
SendDestroyEntity(knownum); // 3) destroy
know_it = known_ents_.erase(know_it); // remove from known
SendDestroyEntity(knownum);
know_it = known_ents_.erase(know_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;
if (ShouldSeeEntity(e)) // 1) become visible
if (ShouldSeeEntity(e))
{
SendInitEntity(e);
known_ents_.insert(entnum); // add to known
} // else: stays invisible, nothing to do
known_ents_.insert(entnum);
}
++ent_it;
}
else // ----- player knows it, but it no longer exists -----
else // player knows it, but it no longer exists
{
SendDestroyEntity(knownum); // 3) destroy
know_it = known_ents_.erase(know_it); // remove from known
SendDestroyEntity(knownum);
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);
}
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)
{
MSGDEBUG(std::cout << "seding ENTDESTROY " << entnum << std::endl;)

View File

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

View File

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

View File

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

View File

@ -73,7 +73,7 @@ void game::World::FinishFrame()
// reset ent msgs
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();
// read initial state
if (!ReadState(msg))
if (!ReadState(&msg))
throw EntityInitError();
states_[0] = states_[1]; // lerp from the read state to avoid jump
OnAttach();
radius_ = 2.0f;
}
@ -43,18 +42,16 @@ bool game::view::CharacterView::ProcessMsg(net::EntMsgType type, net::InMessage&
{
switch (type)
{
case net::EMSG_UPDATE:
return ProcessUpdateMsg(msg);
case net::EMSG_ATTACH:
skip_lerps_ = 1;
return Super::ProcessMsg(type, msg);
default:
return Super::ProcessMsg(type, msg);
}
}
bool game::view::CharacterView::ProcessUpdateMsg(net::InMessage* msg)
{
return ReadState(msg);
}
void game::view::CharacterView::Update(const UpdateInfo& 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);
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_);
@ -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();
@ -148,66 +160,55 @@ bool game::view::CharacterView::ReadState(net::InMessage& msg)
auto& new_state = states_[1];
// parse state delta
CharacterSyncFieldFlags fields;
if (!msg.Read(fields))
return false;
// transform
if (fields & CSF_TRANSFORM)
if (msg)
{
if (!net::ReadDelta(msg, sync_.pos.x) || !net::ReadDelta(msg, sync_.pos.y) ||
!net::ReadDelta(msg, sync_.pos.z) || !net::ReadDelta(msg, sync_.yaw))
// parse state delta
CharacterSyncFieldFlags fields;
if (!msg->Read(fields))
return false;
net::DecodePosition(sync_.pos, new_state.trans.position);
new_state.trans.rotation = glm::rotate(glm::quat(1.0f, 0.0f, 0.0f, 0.0f),
sync_.yaw.Decode() + glm::pi<float>() * 0.5f, glm::vec3(0, 0, 1));
}
// transform
if (fields & CSF_TRANSFORM)
{
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)
{
if (!msg.Read(sync_.idle_anim))
return false;
net::DecodePosition(sync_.pos, new_state.trans.position);
new_state.trans.rotation = glm::rotate(glm::quat(1.0f, 0.0f, 0.0f, 0.0f),
sync_.yaw.Decode() + glm::pi<float>() * 0.5f, glm::vec3(0, 0, 1));
}
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)
{
if (!msg.Read(sync_.walk_anim) || !msg.Read(sync_.run_anim))
return false;
animstate_.idle_anim_idx = sync_.idle_anim;
}
animstate_.walk_anim_idx = sync_.walk_anim;
animstate_.run_anim_idx = sync_.run_anim;
}
if (fields & CSF_LOCO_ANIMS)
{
if (!msg->Read(sync_.walk_anim) || !msg->Read(sync_.run_anim))
return false;
if (fields & CSF_LOCO_VALS)
{
if (!net::ReadDelta(msg, sync_.loco_blend) || !net::ReadDelta(msg, sync_.loco_phase))
return false;
animstate_.walk_anim_idx = sync_.walk_anim;
animstate_.run_anim_idx = sync_.run_anim;
}
new_state.loco_blend = sync_.loco_blend.Decode();
new_state.loco_phase = sync_.loco_phase.Decode();
if (fields & CSF_LOCO_VALS)
{
if (!net::ReadDelta(*msg, sync_.loco_blend) || !net::ReadDelta(*msg, sync_.loco_phase))
return false;
if (new_state.loco_phase < states_[0].loco_phase)
states_[0].loco_phase -= 1.0f;
}
if (skip_lerps_ > 0)
{
states_[0] = states_[1];
skip_lerps_--;
new_state.loco_blend = sync_.loco_blend.Decode();
new_state.loco_phase = sync_.loco_phase.Decode();
}
}
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)
{
const auto& surface_names = basemodel_->GetMesh()->surface_names;

View File

@ -34,12 +34,15 @@ public:
DELETE_COPY_MOVE(CharacterView)
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 Draw(const DrawArgs& args) override;
protected:
virtual void OnAttach() override;
private:
bool ReadState(net::InMessage& msg);
bool ProcessUpdateMsg(net::InMessage& msg);
bool ReadState(net::InMessage* msg);
SurfaceMask GetSurfaceMask(const std::string& name);
void UpdateSurfaceMask();
@ -63,7 +66,6 @@ private:
CharacterSyncState sync_;
CharacterViewState states_[2];
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)
{
float time = world_.GetTime();
@ -81,7 +86,12 @@ bool game::view::EntityView::ReadNametag(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)

View File

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

View File

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

View File

@ -20,13 +20,12 @@ public:
SimpleEntityView(WorldView& world, net::InMessage& msg);
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 Draw(const DrawArgs& args) override;
private:
bool ReadState(net::InMessage& msg);
bool ProcessUpdateMsg(net::InMessage& msg);
bool ReadState(net::InMessage* msg);
private:
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);
if (!ReadState(msg))
if (!ReadState(&msg))
throw EntityInitError();
// init the other transform to identical
root_trans_[0] = root_trans_[1];
root_.local = root_trans_[0];
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;
}
@ -47,14 +43,16 @@ bool game::view::VehicleView::ProcessMsg(net::EntMsgType type, net::InMessage& m
{
switch (type)
{
case net::EMSG_UPDATE:
return ProcessUpdateMsg(msg);
default:
return Super::ProcessMsg(type, msg);
}
}
bool game::view::VehicleView::ProcessUpdateMsg(net::InMessage* msg)
{
return ReadState(msg);
}
void game::view::VehicleView::Update(const UpdateInfo& 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;
auto& root_trans = root_trans_[1];
update_time_ = world_.GetTime();
// parse state delta
VehicleSyncFieldFlags fields;
if (!msg.Read(fields))
return false;
// flags
if (fields & VSF_FLAGS)
if (msg)
{
if (!msg.Read(flags_))
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))
// parse state delta
VehicleSyncFieldFlags fields;
if (!msg->Read(fields))
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)
// flags
if (fields & VSF_FLAGS)
{
if (!net::ReadDelta(msg, sync_.wheels[i].z_offset) ||
!net::ReadDelta(msg, sync_.wheels[i].speed))
if (!msg->Read(flags_))
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;
}
bool game::view::VehicleView::ProcessUpdateMsg(net::InMessage& msg)
{
return ReadState(msg);
}

View File

@ -27,12 +27,12 @@ public:
DELETE_COPY_MOVE(VehicleView)
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 Draw(const DrawArgs& args) override;
private:
bool ReadState(net::InMessage& msg);
bool ProcessUpdateMsg(net::InMessage& msg);
bool ReadState(net::InMessage* msg);
private:
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(assets::CacheManager::GetSound("data/breaksign.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/breakwindow.snd"));
}
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:
return ProcessEntMsgMsg(msg);
case net::MSG_UPDATEENTS:
return ProcessUpdateEntsMsg(msg);
case net::MSG_ENTDESTROY:
return ProcessEntDestroyMsg(msg);
@ -172,6 +176,49 @@ bool game::view::WorldView::ProcessEntMsgMsg(net::InMessage& 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)
{
net::EntNum entnum;

View File

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

View File

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