mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
322 lines
8.6 KiB
C++
322 lines
8.6 KiB
C++
/* Copyright 2003-2008 ROBLOX Corporation, All Rights Reserved */
|
|
#if 1 // remove this file when fast flag RemoveInterpolationReciever is accepted and removed
|
|
|
|
#include "InterpolatingPhysicsReceiver.h"
|
|
#include "Compressor.h"
|
|
#include "GetTime.h"
|
|
#include "RakPeerInterface.h"
|
|
#include "NetworkSettings.h"
|
|
|
|
#include "Replicator.h"
|
|
|
|
#include "Network/Player.h"
|
|
|
|
#include "Util/ObscureValue.h"
|
|
|
|
#include "V8DataModel/PartInstance.h"
|
|
#include "V8DataModel/Workspace.h"
|
|
#include "V8DataModel/DataModel.h"
|
|
|
|
#include "V8World/World.h"
|
|
#include "V8World/Primitive.h"
|
|
#include "V8World/Assembly.h"
|
|
|
|
using namespace RBX;
|
|
using namespace RBX::Network;
|
|
|
|
FASTFLAG(RemoveInterpolationReciever)
|
|
|
|
class InterpolatingPhysicsReceiver::Job : public ReplicatorJob
|
|
{
|
|
weak_ptr<InterpolatingPhysicsReceiver> receiver;
|
|
ObscureValue<double> receiveRate;
|
|
public:
|
|
Job(shared_ptr<InterpolatingPhysicsReceiver> receiver)
|
|
:ReplicatorJob("Net InterpolatePhysics", *(receiver->replicator), DataModelJob::PhysicsIn)
|
|
,receiver(receiver)
|
|
,receiveRate(replicator->settings().getReceiveRate())
|
|
{
|
|
cyclicExecutive = true;
|
|
}
|
|
|
|
private:
|
|
virtual Time::Interval sleepTime(const Stats& stats)
|
|
{
|
|
return computeStandardSleepTime(stats, receiveRate);
|
|
}
|
|
virtual Error error(const Stats& stats)
|
|
{
|
|
return computeStandardError(stats, receiveRate);
|
|
}
|
|
|
|
virtual TaskScheduler::StepResult stepDataModelJob(const Stats& stats)
|
|
{
|
|
if(shared_ptr<InterpolatingPhysicsReceiver> safeReceiver = receiver.lock()){
|
|
safeReceiver->step(RakNet::GetTimeMS());
|
|
return TaskScheduler::Stepped;
|
|
}
|
|
return TaskScheduler::Done;
|
|
}
|
|
};
|
|
|
|
static double lerpValue = 0.05;
|
|
|
|
InterpolatingPhysicsReceiver::InterpolatingPhysicsReceiver(Replicator* replicator, bool isServer)
|
|
: PhysicsReceiver(replicator, isServer)
|
|
, outOfOrderMechanisms(lerpValue)
|
|
{
|
|
if(FFlag::RemoveInterpolationReciever)
|
|
{
|
|
RBXASSERT(false); // nobody should be creating this
|
|
}
|
|
}
|
|
|
|
void InterpolatingPhysicsReceiver::start(shared_ptr<PhysicsReceiver> physicsReceiver)
|
|
{
|
|
shared_ptr<InterpolatingPhysicsReceiver> interpolatingPhysicsReceiver = boost::dynamic_pointer_cast<InterpolatingPhysicsReceiver>(physicsReceiver);
|
|
if(interpolatingPhysicsReceiver.get() != this)
|
|
RBXCRASH();
|
|
|
|
tryToCreateJob(interpolatingPhysicsReceiver);
|
|
if(!job)
|
|
serviceProviderConnection = replicator->ancestryChangedSignal.connect(boost::bind(&InterpolatingPhysicsReceiver::onAncestryChanged, this, interpolatingPhysicsReceiver));
|
|
}
|
|
|
|
void InterpolatingPhysicsReceiver::tryToCreateJob(shared_ptr<InterpolatingPhysicsReceiver> interpolatingPhysicsReceiver)
|
|
{
|
|
RBXASSERT(interpolatingPhysicsReceiver.get() == this);
|
|
RBXASSERT(!job);
|
|
|
|
// We can't create the job until we're in the DataModel
|
|
if (!DataModel::get(replicator))
|
|
return;
|
|
|
|
job.reset(new Job(interpolatingPhysicsReceiver));
|
|
TaskScheduler::singleton().add(job);
|
|
}
|
|
|
|
void InterpolatingPhysicsReceiver::onAncestryChanged(shared_ptr<InterpolatingPhysicsReceiver> interpolatingPhysicsReceiver)
|
|
{
|
|
tryToCreateJob(interpolatingPhysicsReceiver);
|
|
if (job)
|
|
// We've succeeded in creating the job
|
|
serviceProviderConnection.disconnect();
|
|
}
|
|
|
|
InterpolatingPhysicsReceiver::~InterpolatingPhysicsReceiver()
|
|
{
|
|
TaskScheduler::singleton().remove(job);
|
|
}
|
|
|
|
|
|
void InterpolatingPhysicsReceiver::Nugget::receive(RakNet::BitStream& bitstream, RakNet::Time timeStamp, const ModelInstance* noLagModel, InterpolatingPhysicsReceiver* receiver)
|
|
{
|
|
if (history->count>0)
|
|
{
|
|
// Check for out-of-order data
|
|
if (timeStamp < history->data[history->last].networkTime)
|
|
{
|
|
MechanismItem dummy;
|
|
int numNodesInHistory;
|
|
receiver->receiveMechanism(bitstream, part.get(), dummy, timeStamp, numNodesInHistory);
|
|
receiver->outOfOrderMechanisms.sample(1);
|
|
return;
|
|
}
|
|
else
|
|
receiver->outOfOrderMechanisms.sample(0);
|
|
}
|
|
|
|
history->last = (history->last + 1 + bufferSize) % bufferSize;
|
|
if (history->count<bufferSize)
|
|
history->count++;
|
|
|
|
MechanismItem& item = history->data[history->last];
|
|
|
|
int numNodesInHistory;
|
|
receiver->receiveMechanism(bitstream, part.get(), item, timeStamp, numNodesInHistory);
|
|
item.networkTime = timeStamp;
|
|
|
|
// Now compute the lag
|
|
if (noLagModel && part && part->isDescendantOf(noLagModel))
|
|
{
|
|
lag = 0;
|
|
}
|
|
else if (history->count>=2)
|
|
{
|
|
const int index2 = (history->last - 1 + bufferSize) % bufferSize;
|
|
double newLag = (double)(item.networkTime - history->data[index2].networkTime);
|
|
|
|
// Add a lag buffer
|
|
const double physicsReceiveDelayFactor = 1.2;
|
|
newLag *= physicsReceiveDelayFactor;
|
|
|
|
// Use an asymetric log average that favors large values.
|
|
// Small values will be large in number, so they will win out
|
|
// over time.
|
|
#if 1
|
|
lag = (lag*lag + newLag*newLag) / (lag + newLag);
|
|
#else
|
|
if (newLag>lag)
|
|
lag = 0.8 * newLag + 0.2 * lag;
|
|
else
|
|
lag = 0.2 * newLag + 0.8 * lag;
|
|
#endif
|
|
}
|
|
receiver->sampleLag(lag);
|
|
}
|
|
|
|
|
|
void InterpolatingPhysicsReceiver::setLerpedPhysics(const MechanismItem& itemBefore, const MechanismItem& itemAfter, float lerpAlpha)
|
|
{
|
|
// For efficiency we don't bother lerping near maxima
|
|
if (lerpAlpha<=0.001)
|
|
setPhysics(itemBefore);
|
|
else if (lerpAlpha>=0.99)
|
|
setPhysics(itemAfter);
|
|
else if (!MechanismItem::consistent(&itemBefore, &itemAfter))
|
|
setPhysics(itemAfter);
|
|
else
|
|
{
|
|
MechanismItem::lerp(&itemBefore, &itemAfter, &reusableMechanismItem, lerpAlpha);
|
|
setPhysics(reusableMechanismItem);
|
|
}
|
|
}
|
|
|
|
|
|
bool InterpolatingPhysicsReceiver::Nugget::step(RakNet::Time time, InterpolatingPhysicsReceiver* receiver) const
|
|
{
|
|
if (part->getDragging()) {
|
|
return false;
|
|
}
|
|
|
|
if (!part->getPartPrimitive()->getAssembly()) {
|
|
return false;
|
|
}
|
|
|
|
if (!receiver->okDistributedReceivePart(part)) {
|
|
return false;
|
|
}
|
|
|
|
if (history->count==0)
|
|
return true;
|
|
|
|
if (history->count==1)
|
|
{
|
|
receiver->setPhysics(history->data[history->last]);
|
|
return true;
|
|
}
|
|
|
|
const double laggedTime = time - lag;
|
|
|
|
const MechanismItem* itemAfter = 0;
|
|
int i;
|
|
for (i = 0; i<history->count; ++i)
|
|
{
|
|
const int index = (history->last - i + bufferSize) % bufferSize;
|
|
|
|
const MechanismItem& item(history->data[index]);
|
|
|
|
if (item.networkTime > laggedTime)
|
|
{
|
|
itemAfter = &item;
|
|
}
|
|
else if (itemAfter==0)
|
|
{
|
|
itemAfter = &item;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
float lerpAlpha = (float)(laggedTime - item.networkTime) / (float)(itemAfter->networkTime - item.networkTime);
|
|
// TODO: Allow for forward-looking lerpAlpha up to a point
|
|
receiver->setLerpedPhysics(item, *itemAfter, lerpAlpha);
|
|
receiver->sampleBufferSeek(i+1);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
receiver->sampleBufferSeek(i);
|
|
receiver->setPhysics(*itemAfter);
|
|
|
|
return true;
|
|
}
|
|
|
|
void InterpolatingPhysicsReceiver::step(RakNet::Time time)
|
|
{
|
|
lagStats.clearBufferAccumulator();
|
|
Nuggets::index<Nugget::part_tag>::type& index = nuggets.get<Nugget::part_tag>();
|
|
Nuggets::index_iterator<Nugget::part_tag>::type iter = index.begin();
|
|
Nuggets::index_iterator<Nugget::part_tag>::type end = index.end();
|
|
while (iter!=end)
|
|
{
|
|
// TODO: Erase if no longer relevant
|
|
if (!(*iter).step(time, this))
|
|
iter = index.erase(iter);
|
|
else
|
|
++iter;
|
|
}
|
|
lagStats.sampleBufferAccumulator();
|
|
}
|
|
|
|
void InterpolatingPhysicsReceiver::receivePacket(RakNet::BitStream& inBitstream, RakNet::Time timeStamp, ReplicatorStats::PhysicsReceiverStats* stats)
|
|
{
|
|
lagStats.clearLagAccumulator();
|
|
Nuggets::index<Nugget::part_tag>::type& index = nuggets.get<Nugget::part_tag>();
|
|
|
|
const ModelInstance* noLagModel = 0;
|
|
|
|
while (true)
|
|
{
|
|
shared_ptr<PartInstance> part;
|
|
|
|
if (!receiveRootPart(part, inBitstream)) {
|
|
break; // no instance - done
|
|
}
|
|
|
|
Nuggets::index_iterator<Nugget::part_tag>::type it = index.find(part);
|
|
|
|
if (it!=index.end())
|
|
{
|
|
index.modify(it, boost::bind(&Nugget::receive, _1, boost::ref(inBitstream), timeStamp, noLagModel, this));
|
|
}
|
|
else
|
|
{
|
|
Nugget nugget(part);
|
|
nugget.receive(inBitstream, timeStamp, noLagModel, this);
|
|
if (part)
|
|
index.insert(nugget);
|
|
}
|
|
}
|
|
lagStats.sampleLagAccumulator();
|
|
}
|
|
|
|
|
|
InterpolatingPhysicsReceiver::LagStats::LagStats()
|
|
:averageBufferSeek(lerpValue)
|
|
,averageLag(lerpValue)
|
|
,maxBufferSeek(0)
|
|
{
|
|
}
|
|
|
|
void InterpolatingPhysicsReceiver::LagStats::clearBufferAccumulator()
|
|
{
|
|
bufferSeekAccumulator = AccumulatorSet();
|
|
}
|
|
|
|
void InterpolatingPhysicsReceiver::LagStats::sampleBufferAccumulator()
|
|
{
|
|
if (boost::accumulators::count(bufferSeekAccumulator)>0)
|
|
averageBufferSeek.sample(boost::accumulators::mean(bufferSeekAccumulator));
|
|
}
|
|
void InterpolatingPhysicsReceiver::LagStats::clearLagAccumulator()
|
|
{
|
|
lagAccumulator = AccumulatorSet();
|
|
}
|
|
|
|
void InterpolatingPhysicsReceiver::LagStats::sampleLagAccumulator()
|
|
{
|
|
if (boost::accumulators::count(lagAccumulator)>0)
|
|
averageLag.sample(boost::accumulators::mean(lagAccumulator));
|
|
}
|
|
|
|
#endif // remove this file when fast flag RemoveInterpolationReciever is accepted and removed
|