forked from aya/aya
Initial commit
This commit is contained in:
339
engine/network/src/RoundRobinPhysicsSender.cpp
Normal file
339
engine/network/src/RoundRobinPhysicsSender.cpp
Normal file
@@ -0,0 +1,339 @@
|
||||
|
||||
|
||||
#include "RoundRobinPhysicsSender.hpp"
|
||||
#include "Replicator.hpp"
|
||||
#include "Compressor.hpp"
|
||||
|
||||
#include "Player.hpp"
|
||||
|
||||
|
||||
#include "RakNet/GetTime.hpp"
|
||||
#include "RakNet/RakPeerInterface.hpp"
|
||||
#include "NetworkSettings.hpp"
|
||||
#include "ConcurrentRakPeer.hpp"
|
||||
#include "Client.hpp"
|
||||
|
||||
#include "World/SendPhysics.hpp"
|
||||
#include "World/World.hpp"
|
||||
#include "World/Primitive.hpp"
|
||||
#include "World/Assembly.hpp"
|
||||
|
||||
#include "DataModel/PartInstance.hpp"
|
||||
|
||||
#include "DataModel/ModelInstance.hpp"
|
||||
|
||||
#include "DataModel/Workspace.hpp"
|
||||
|
||||
#include "DataModel/PhysicsService.hpp"
|
||||
|
||||
|
||||
#include "RakNet/RakNetStatistics.hpp"
|
||||
|
||||
|
||||
using namespace Aya;
|
||||
using namespace Aya::Network;
|
||||
|
||||
SYNCHRONIZED_FASTFLAG(PhysicsPacketSendWorldStepTimestamp)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RoundRobinPhysicsSender::RoundRobinPhysicsSender(Replicator& replicator)
|
||||
: PhysicsSender(replicator)
|
||||
, lastCharacterSendTime(Time::now<Time::Fast>())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void RoundRobinPhysicsSender::sendPhysicsData(RakNet::BitStream& bitStream, const Assembly* assembly)
|
||||
{
|
||||
AYAASSERT(assembly);
|
||||
|
||||
if (assembly)
|
||||
{
|
||||
senderStats = NULL;
|
||||
if (replicator.settings().trackPhysicsDetails)
|
||||
{
|
||||
senderStats = &(replicator.replicatorStats.physicsSenderStats);
|
||||
}
|
||||
|
||||
const PartInstance* part = PartInstance::fromConstPrimitive(assembly->getConstAssemblyPrimitive());
|
||||
|
||||
if (canSend(part, assembly, bitStream))
|
||||
{
|
||||
Player* player = replicator.findTargetPlayer();
|
||||
ModelInstance* m = player ? player->getCharacter() : 0;
|
||||
bool detailed = m && m->isAncestorOf(part);
|
||||
|
||||
if (replicator.isStreamingEnabled())
|
||||
{
|
||||
bitStream << false; // token: not done with packet
|
||||
bitStream << false; // not CFrame only
|
||||
|
||||
if (replicator.trySerializeId(bitStream, part))
|
||||
{
|
||||
RakNet::BitSize_t characterBytes = bitStream.GetNumberOfBytesUsed();
|
||||
CoordinateFrame cFrame = part->getCoordinateFrame();
|
||||
float accumulatedError;
|
||||
sendMechanism(bitStream, part, detailed, currentStepTimestamp, cFrame, accumulatedError, NULL);
|
||||
if (senderStats && detailed)
|
||||
{
|
||||
senderStats->details.characterAnim.increment();
|
||||
senderStats->details.characterAnimSize.sample(bitStream.GetNumberOfBytesUsed() - characterBytes);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
replicator.serializeId(bitStream, NULL); // token: done with this mechanism
|
||||
}
|
||||
}
|
||||
else if (replicator.trySerializeId(bitStream, part))
|
||||
{
|
||||
RakNet::BitSize_t characterBytes = bitStream.GetNumberOfBytesUsed();
|
||||
CoordinateFrame cFrame = part->getCoordinateFrame();
|
||||
float accumulatedError;
|
||||
sendMechanism(bitStream, part, detailed, currentStepTimestamp, cFrame, accumulatedError, NULL);
|
||||
if (senderStats && detailed)
|
||||
{
|
||||
senderStats->details.characterAnim.increment();
|
||||
senderStats->details.characterAnimSize.sample(bitStream.GetNumberOfBytesUsed() - characterBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const SimJob* RoundRobinPhysicsSender::findTargetPlayerCharacterSimJob()
|
||||
{
|
||||
if (const Player* player = replicator.findTargetPlayer())
|
||||
{
|
||||
if (const Primitive* primitive = player->getConstCharacterRoot())
|
||||
{
|
||||
return SimJob::getConstSimJobFromPrimitive(primitive);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
class RoundRobinPhysicsSender::JobSender : public boost::noncopyable
|
||||
{
|
||||
// Hacky way to keep track of the first object sent, since it may be sent again after reportClosestMechanism
|
||||
const Assembly* firstA;
|
||||
const SimJob* firstM;
|
||||
|
||||
RoundRobinPhysicsSender& sender;
|
||||
ConcurrentRakPeer* rakPeer;
|
||||
const int maxStreamSize;
|
||||
|
||||
shared_ptr<RakNet::BitStream> bitStream;
|
||||
PacketPriority packetPriority;
|
||||
|
||||
unsigned int emptySize;
|
||||
|
||||
|
||||
void openPacket()
|
||||
{
|
||||
bitStream.reset(new RakNet::BitStream(maxStreamSize));
|
||||
*bitStream << (unsigned char)ID_TIMESTAMP;
|
||||
RakNet::Time now = RakNet::GetTime();
|
||||
#if !defined(__linux) && !defined(__APPLE__)
|
||||
*bitStream << now;
|
||||
#else
|
||||
*bitStream << static_cast<unsigned long long>(now);
|
||||
#endif
|
||||
*bitStream << (unsigned char)ID_PHYSICS;
|
||||
emptySize = bitStream->GetNumberOfBitsUsed();
|
||||
++packetCount;
|
||||
itemCount = 0;
|
||||
}
|
||||
|
||||
void openPacketPhysicsOffset(RakNet::Time& newTimeStamp)
|
||||
{
|
||||
bitStream.reset(new RakNet::BitStream(maxStreamSize));
|
||||
*bitStream << (unsigned char)ID_TIMESTAMP;
|
||||
RakNet::Time now = RakNet::GetTime();
|
||||
#if !defined(__linux) && !defined(__APPLE__)
|
||||
*bitStream << now;
|
||||
#else
|
||||
*bitStream << static_cast<unsigned long long>(now);
|
||||
#endif
|
||||
*bitStream << (unsigned char)ID_PHYSICS;
|
||||
#if !defined(__linux) && !defined(__APPLE__)
|
||||
*bitStream << newTimeStamp;
|
||||
#else
|
||||
*bitStream << static_cast<unsigned long long>(newTimeStamp);
|
||||
#endif
|
||||
emptySize = bitStream->GetNumberOfBitsUsed();
|
||||
++packetCount;
|
||||
itemCount = 0;
|
||||
}
|
||||
|
||||
void closePacket()
|
||||
{
|
||||
if (bitStream->GetNumberOfBitsUsed() > emptySize)
|
||||
{
|
||||
// Packet "end" tag
|
||||
if (sender.replicator.isStreamingEnabled())
|
||||
*bitStream << true;
|
||||
else
|
||||
sender.replicator.serializeId(*bitStream, NULL);
|
||||
|
||||
// Send ID_PHYSICS
|
||||
ReplicatorStats::PhysicsSenderStats& physStats = sender.replicator.physicsSenderStats();
|
||||
rakPeer->Send(bitStream, packetPriority, UNRELIABLE, PHYSICS_CHANNEL, sender.replicator.remotePlayerId, false);
|
||||
physStats.physicsPacketsSent.sample();
|
||||
physStats.physicsPacketsSentSmooth.sample();
|
||||
physStats.physicsPacketsSentSize.sample(bitStream->GetNumberOfBytesUsed());
|
||||
|
||||
sender.itemsPerPacket.sample(itemCount);
|
||||
}
|
||||
bitStream.reset();
|
||||
}
|
||||
|
||||
public:
|
||||
int packetCount;
|
||||
unsigned int itemCount;
|
||||
|
||||
JobSender(RoundRobinPhysicsSender& sender, ConcurrentRakPeer* peer)
|
||||
: sender(sender)
|
||||
, rakPeer(peer)
|
||||
, firstA(NULL)
|
||||
, firstM(NULL)
|
||||
, packetPriority(sender.replicator.settings().getPhysicsSendPriority())
|
||||
, packetCount(0)
|
||||
, itemCount(0)
|
||||
, maxStreamSize(sender.replicator.getPhysicsMtuSize())
|
||||
{
|
||||
}
|
||||
void close()
|
||||
{
|
||||
if (bitStream)
|
||||
closePacket();
|
||||
}
|
||||
~JobSender()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
// Returns true if it wants another sample
|
||||
bool report(const SimJob& simJob)
|
||||
{
|
||||
if (SFFlag::getPhysicsPacketSendWorldStepTimestamp())
|
||||
{
|
||||
float timeOffsetDueToPhysics = 0.0f;
|
||||
Workspace* w = ServiceProvider::find<Aya::Workspace>(&sender.replicator);
|
||||
if (w)
|
||||
{
|
||||
timeOffsetDueToPhysics = w->getWorld()->getUpdateExpectedStepDelta();
|
||||
}
|
||||
|
||||
// Generates timestamp for this frame, minus a small offset based on
|
||||
// missed or extra world steps taken.
|
||||
RakNet::Time passStamp = (RakNet::Time)((TaskScheduler::singleton().lastCyclcTimestamp +
|
||||
Time::Interval(sender.replicator.getRakOffsetTime() - timeOffsetDueToPhysics))
|
||||
.timestampSeconds() *
|
||||
1000.0);
|
||||
|
||||
if (!bitStream)
|
||||
openPacketPhysicsOffset(passStamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!bitStream)
|
||||
openPacket();
|
||||
}
|
||||
|
||||
sender.sendPhysicsData(*bitStream, simJob.getConstAssembly());
|
||||
|
||||
const int newSize = bitStream->GetNumberOfBytesUsed();
|
||||
|
||||
itemCount++;
|
||||
|
||||
return newSize < maxStreamSize;
|
||||
}
|
||||
|
||||
bool operator()(SimJob& m)
|
||||
{
|
||||
return report(m);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void RoundRobinPhysicsSender::step()
|
||||
{
|
||||
if (Instance::fastDynamicCast<Client>(replicator.getParent()))
|
||||
{
|
||||
float bufferHealth = replicator.rakPeer->GetBufferHealth();
|
||||
if ((Time::nowFast() - lastBufferCheckTime).seconds() > 3.0)
|
||||
{
|
||||
if (bufferHealth <= 0.5)
|
||||
{
|
||||
// buffer is growing, reduce packets per step
|
||||
if (sendPacketsPerStep > 1) // don't go below 1
|
||||
sendPacketsPerStep--;
|
||||
}
|
||||
else if (bufferHealth >= 0.9)
|
||||
{
|
||||
// buffer health is good, push more data
|
||||
Workspace* w = ServiceProvider::find<Aya::Workspace>(&replicator);
|
||||
if (w)
|
||||
{
|
||||
if ((itemsPerPacket.value() * sendPacketsPerStep) < w->getWorld()->getSendPhysics()->getNumSimJobs())
|
||||
sendPacketsPerStep++;
|
||||
}
|
||||
}
|
||||
|
||||
lastBufferCheckTime = Time::nowFast();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int RoundRobinPhysicsSender::sendPacket(int maxPackets, PacketPriority packetPriority, ReplicatorStats::PhysicsSenderStats* stats)
|
||||
{
|
||||
if (replicator.getBufferCountAvailable(maxPackets, packetPriority) <= 0)
|
||||
return 0;
|
||||
|
||||
JobSender jobSender(*this, replicator.rakPeer.get());
|
||||
|
||||
// TODO: Check timer and feed these gradually?
|
||||
Workspace* w = ServiceProvider::find<Aya::Workspace>(&replicator);
|
||||
if (w)
|
||||
{
|
||||
const SimJob* characterSimJob = findTargetPlayerCharacterSimJob();
|
||||
|
||||
if (characterSimJob)
|
||||
{
|
||||
// For a minimal level of responsiveness, occasionally send the character with a high priority
|
||||
const Time t = Time::now<Time::Fast>();
|
||||
if (t > lastCharacterSendTime + Time::Interval(0.1))
|
||||
{
|
||||
jobSender.report(*characterSimJob);
|
||||
lastCharacterSendTime = t;
|
||||
}
|
||||
else
|
||||
{
|
||||
characterSimJob = NULL; // don't ignore me below
|
||||
}
|
||||
}
|
||||
|
||||
int numSimJobs = w->getWorld()->getSendPhysics()->getNumSimJobs();
|
||||
int numSimJobsReported = 0;
|
||||
|
||||
for (int i = 0; i < maxPackets; i++)
|
||||
{
|
||||
numSimJobsReported +=
|
||||
w->getWorld()->getSendPhysics()->reportSimJobs(jobSender, jobStagePos, characterSimJob, numSimJobs - numSimJobsReported);
|
||||
|
||||
if (numSimJobsReported >= numSimJobs)
|
||||
break;
|
||||
|
||||
jobSender.close();
|
||||
}
|
||||
}
|
||||
|
||||
// jobSend close automatically on destruction
|
||||
|
||||
return jobSender.packetCount;
|
||||
};
|
||||
Reference in New Issue
Block a user