#include "CTransformable.h" #include "CEngine.h" #include "CServer.h" #include "CBitFlags8.h" #include "CGame.h" #include "CConVarManager.h" #include "CClient.h" #include "U_General.h" #include "CPhysicalEntity.h" #include "glm/glm.hpp" #include "glm/gtx/compatibility.hpp" void CTransformable::V_Init() { Transform.OnTransformChanged += [this](CTransformBase::CMeasurePack msp, CTransformBase* _this) { if(!NetSync) { return; } auto server = CEngine::GetInstance()->Components.GetComponentTyped(); if(!server) { return; } auto msg = std::make_shared(); msg->Entity = CEntityHandle(GetEntity()); msg->ChangePosition = msp.Position != _this->GetPosition(); msg->ChangeRotation = msp.Rotation != _this->GetRotation(); msg->ChangeScale = msp.Scale != _this->GetScale(); msg->RemoteChanged = CEngine::GetInstance()->Time.GetCurrent(); //TODO time differences? msg->Pack = _this->GetPRS(); //TODO also take in the count stream distance and space culling //TODO lerp server->SendUnreliableMessageToAll(msg); //TODO should be reliable }; } void CNetTransformChanged::V_Write(CBufferWrapper& wrapper) { CBitFlags8 flags; flags.set(0, ChangePosition); flags.set(1, ChangeRotation); flags.set(2, ChangeScale); //auto dur = std::chrono::duration_cast>(RemoteChanged.time_since_epoch()); wrapper.Write(Entity.Get()->GetUUID()); wrapper.Write(RemoteChanged); //TODO don't write/read it raw. Do the serialization wrapper.Write(flags); if(ChangePosition) { wrapper.Write(Pack.Position); } if(ChangeRotation) { wrapper.Write(Pack.Rotation); } if(ChangeScale) { wrapper.Write(Pack.Scale); } } void CNetTransformChanged::V_Read(CBufferWrapper& wrapper) { auto client = CEngine::GetInstance()->Components.GetComponentTyped(); if(!client) { return; } auto _entid = wrapper.Read(); RemoteChanged = wrapper.Read(); auto flags = wrapper.Read(); std::uint16_t entid = client->ServerEntityMap[_entid]; auto game = CEngine::GetInstance()->Components.GetComponentTyped(); auto world = game->Worlds.GetWorld(); auto it = std::find_if(world->Entities.Items.begin(), world->Entities.Items.end(), [entid](auto& kv) -> bool { return kv.second->GetUUID() == entid; }); if(it == world->Entities.Items.end()) { Log::ErrInstance() << "No such entity\n"; //TODO disconnect -- fatal! } ChangePosition = flags[0]; ChangeRotation = flags[1]; ChangeScale = flags[2]; if(ChangePosition) { wrapper.Read(Pack.Position); } if(ChangeRotation) { wrapper.Read(Pack.Rotation); } if(ChangeScale) { wrapper.Read(Pack.Scale); } if(it != world->Entities.Items.end()) //TODO HACK { Entity = CEntityHandle(it->second.get()); } } void CNetTransformChanged::Process() { if(!Entity.IsValid()) { return; } //TODO HACK bool Interpolation = true; std::uint8_t InterpFrames = 1; COMPONENT_CALL_GET(Interpolation, CConVarManager, GetConVarValue("cl.interp", true)); COMPONENT_CALL_GET(InterpFrames, CConVarManager, GetConVarValue("cl.interp.frames", 1)); auto entity = Entity.Get(); if(!entity) { return; } auto transformable = entity->Components.GetComponentTyped(); if(!transformable) { return; } transformable->m_remoteTransformable = true; if(!ChangePosition) { Pack.Position = transformable->Transform.GetPosition(); } if(!ChangeRotation) { Pack.Rotation = transformable->Transform.GetRotation(); } if(!ChangeScale) { Pack.Scale = transformable->Transform.GetScale(); } if(!Interpolation) { transformable->Transform.SetPRS(Pack); } else { auto clientNow = CEngine::GetInstance()->Time.GetCurrent(); static std::chrono::duration serverToClientOffset = std::chrono::duration(0); auto measuredOffset = clientNow - NetTime; serverToClientOffset = serverToClientOffset * 0.9f + measuredOffset * 0.1f; CTimePoint clientDomainTime = TIME_EXPR(NetTime + serverToClientOffset); transformable->InterpQueue.push_back({ RemoteChanged, Pack }); //Log::Instance() << "RemoteChanged: " << RemoteChanged.time_since_epoch().count() << "; Client time: " << CEngine::GetInstance()->Time.GetCurrent().time_since_epoch() << Log::Endl; } } void CTransformable::V_Update() { //dump.trans.timestep int timesPerSecond = 100; size_t maxmem = 1000000; COMPONENT_CALL_GET(timesPerSecond, CConVarManager, GetConVarValue("dump.trans.timestep", 100)); COMPONENT_CALL_GET(maxmem, CConVarManager, GetConVarValue("dump.trans.maxmem", 1000000)); constexpr size_t singleSize = sizeof(CTimePoint) + sizeof(CTransformBase::CMeasurePack); constexpr size_t totalSize = singleSize * 1000000; auto now = CEngine::GetInstance()->Time.GetCurrent(); if(now - LastDump >= std::chrono::duration(1.0f / static_cast(timesPerSecond))) { DumpQueue.push_back({ now, Transform.GetPRS() }); LastDump = now; } if(DumpQueue.size() >= maxmem) { DumpQueue.erase(DumpQueue.begin()); //inefficient limit } //Log::Instance() << "CTransformable::V_Update()\n"; if(!m_remoteTransformable) { return; } bool Interpolation = true; std::uint8_t InterpFrames = 1; COMPONENT_CALL_GET(Interpolation, CConVarManager, GetConVarValue("cl.interp", true)); COMPONENT_CALL_GET(InterpFrames, CConVarManager, GetConVarValue("cl.interp.frames", 1)); if(!Interpolation) { return; } //if(InterpQueue.size() < InterpFrames) { return; } //sv_rate = 1.0 / 50.0 = 0.02 //pack_1 = 1.02 //pack_2 = 1.04 //cl_time = 1.045 //pack_1_pred + (frames_i * sv_rate) = 1.02 + (1 * 0.02) = 1.02 + 0.02 = 1.04 //pack_2_pred + (frames_i * sv_rate) = 1.02 + (2 * 0.02) = 1.02 + 0.04 = 1.06 //pack_1 = 1.02 //cl_time = 1.023 //pack_1_pred = 1.02 + (1 * 0.02) = 1.04 float sv_rate = 33.0f; COMPONENT_CALL_GET(sv_rate, CClient, GetServerRate()); float sv_delay = 1.0f / sv_rate; float MaxDifference = (InterpFrames + 2) * sv_delay; //TODO not hardcoded threshold CTimePoint curTime = CEngine::GetInstance()->Time.GetCurrent(); int lerpIndex = -1; float minDiff = 9999.0f; auto lerpPoint = CEngine::GetInstance()->Time.GetCurrent() - std::chrono::duration(sv_delay * InterpFrames); for(int i = 0; i < InterpQueue.size(); i++) { auto& pair = InterpQueue.at(i); if(pair.first > curTime) { continue; } if(pair.first <= lerpPoint && ( (i == InterpQueue.size() - 1) || (InterpQueue.at(i + 1).first > lerpPoint) ) ) { lerpIndex = i; break; } } if(lerpIndex != -1) { //cl_time = 1.023 auto& lerp_unit = InterpQueue.at(lerpIndex); auto t_pack_pred_time = lerp_unit.first + std::chrono::duration(sv_delay); //1.04 auto t_pack_plus_client_diff = CEngine::GetInstance()->Time.GetCurrent() - lerp_unit.first; //0.003 float client_diff = std::chrono::duration(t_pack_plus_client_diff).count(); //0.003 float max_diff = sv_delay; //0.02 //0.003 / 0.02 = 0.15 float lerpFactor = client_diff / max_diff; lerpFactor = glm::clamp(lerpFactor, 0.0f, 1.0f); CTransformBase::CMeasurePack lerpedPack; lerpedPack.Position = glm::lerp(Transform.GetPosition(), lerp_unit.second.Position, lerpFactor); lerpedPack.Rotation = glm::slerp(Transform.GetRotation(), lerp_unit.second.Rotation, lerpFactor); lerpedPack.Scale = glm::lerp(Transform.GetScale(), lerp_unit.second.Scale, lerpFactor); Transform.SetPRS(lerpedPack); } else if(!InterpQueue.empty()) { if(CEngine::GetInstance()->Time.GetCurrent() >= InterpQueue.front().first) { Transform.SetPRS(InterpQueue.back().second); } } std::erase_if(InterpQueue, [&curTime, MaxDifference](auto& pair) -> bool { return (curTime - pair.first) >= std::chrono::duration(MaxDifference); }); //Log::Instance() << "CTransformable::Interp()\n"; } void CTransformable::FullPack(CBufferWrapper& packet) { packet.Write(Transform.GetPRS()); } void CTransformable::FullUnpack(CBufferWrapper& packet) { Transform.SetPRS(packet.Read()); } LINK_SOL_USERTYPE(CTransformable); LINK_NET_MESSAGE_TO_CLASS(CNetTransformChanged, transform); LINK_ENTITY_COMPONENT_TO_CLASS(CTransformable, transformable);