forked from aya/aya
Initial commit
This commit is contained in:
260
engine/network/src/Peer.cpp
Normal file
260
engine/network/src/Peer.cpp
Normal file
@@ -0,0 +1,260 @@
|
||||
#include "Peer.hpp"
|
||||
|
||||
#include "Replicator.hpp"
|
||||
#include "RakNet/GetTime.hpp"
|
||||
#include "RakNet/RakPeer.hpp"
|
||||
#include "ConcurrentRakPeer.hpp"
|
||||
#include "Log.hpp"
|
||||
|
||||
#include "Debug.hpp"
|
||||
|
||||
#include "DataModel/Stats.hpp"
|
||||
|
||||
#include "DataModel/DataModelJob.hpp"
|
||||
|
||||
#include "DataModel/PhysicsService.hpp"
|
||||
|
||||
#include "DataModel/DataModel.hpp"
|
||||
|
||||
#include "Utility/ObscureValue.hpp"
|
||||
|
||||
#include "Utility/StandardOut.hpp"
|
||||
|
||||
|
||||
#include "DataBlockEncryptor.hpp"
|
||||
|
||||
const char* const Aya::Network::sPeer = "NetworkPeer";
|
||||
|
||||
namespace Aya
|
||||
{
|
||||
namespace Network
|
||||
{
|
||||
|
||||
|
||||
static Reflection::BoundFuncDesc<Peer, void(int)> func_SetOutgoingKBPSLimit(
|
||||
&Peer::setOutgoingKBPSLimit, "SetOutgoingKBPSLimit", "limit", Security::Plugin);
|
||||
REFLECTION_END();
|
||||
|
||||
class ProfiledRakPeer : public RakNet::RakPeer
|
||||
{
|
||||
private:
|
||||
Peer& peer; // time spent per step in the Raknet thread
|
||||
typedef RakNet::RakPeer Super;
|
||||
|
||||
public:
|
||||
ProfiledRakPeer(Peer& peer)
|
||||
: peer(peer)
|
||||
{
|
||||
}
|
||||
virtual bool RunUpdateCycle(RakNet::TimeUS timeNS, RakNet::Time timeMS)
|
||||
{
|
||||
Timer<Time::Benchmark> timer;
|
||||
RakNet::BitStream updateBitStream(MAXIMUM_MTU_SIZE);
|
||||
bool result = Super::RunUpdateCycle(timeNS, timeMS, updateBitStream);
|
||||
peer.rakDutyCycle.sample(timer.delta());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
static double lerp = 0.05;
|
||||
unsigned char Peer::aesKey[16];
|
||||
|
||||
Peer::Peer()
|
||||
: rakDutyCycle(lerp)
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
aesKey[i] = 0xFE ^ 7 * i;
|
||||
|
||||
protocolVersion = NETWORK_PROTOCOL_VERSION;
|
||||
}
|
||||
|
||||
Peer::~Peer() {}
|
||||
|
||||
void Peer::onCreateRakPeer()
|
||||
{
|
||||
RakNet::RakNetGUID guid = rakPeer->rawPeer()->GetGuidFromSystemAddress(RakNet::UNASSIGNED_SYSTEM_ADDRESS);
|
||||
unsigned int seed = (unsigned int)((guid.g >> 32) ^ guid.g);
|
||||
rnr.SeedMT(seed);
|
||||
|
||||
rakPeer->rawPeer()->SetOccasionalPing(true);
|
||||
}
|
||||
|
||||
void Peer::encryptDataPart(RakNet::BitStream& bitStream)
|
||||
{
|
||||
DataBlockEncryptor encryptor;
|
||||
encryptor.SetKey(Peer::aesKey);
|
||||
|
||||
unsigned int length = (unsigned int)bitStream.GetNumberOfBytesUsed() - 1;
|
||||
|
||||
unsigned int bytesRequired = length + 6;
|
||||
bytesRequired = ((bytesRequired + 15) / 16) * 16;
|
||||
|
||||
int deltaBits = 8 * bytesRequired - (bitStream.GetNumberOfBitsAllocated() - 8);
|
||||
if (deltaBits > 0)
|
||||
bitStream.AddBitsAndReallocate(deltaBits);
|
||||
|
||||
encryptor.Encrypt((unsigned char*)bitStream.GetData() + 1, length, (unsigned char*)bitStream.GetData() + 1, &length, &rnr);
|
||||
bitStream.SetWriteOffset((1 + length) * 8);
|
||||
}
|
||||
|
||||
|
||||
void Peer::decryptDataPart(RakNet::BitStream& inBitstream)
|
||||
{
|
||||
DataBlockEncryptor encryptor;
|
||||
encryptor.SetKey(Peer::aesKey);
|
||||
|
||||
unsigned int length = (unsigned int)inBitstream.GetNumberOfBytesUsed() - 1;
|
||||
|
||||
bool success = encryptor.Decrypt((unsigned char*)inBitstream.GetData() + 1, length, (unsigned char*)inBitstream.GetData() + 1, &length);
|
||||
if (!success)
|
||||
throw std::runtime_error("Data error");
|
||||
}
|
||||
|
||||
bool Peer::askAddChild(const Instance* instance) const
|
||||
{
|
||||
return Instance::fastDynamicCast<Replicator>(instance) != NULL;
|
||||
}
|
||||
|
||||
class PeerStatsItem : public Stats::Item
|
||||
{
|
||||
Peer* peer;
|
||||
Stats::Item* rakRate;
|
||||
Stats::Item* rakActivity;
|
||||
Stats::Item* physicsSenders;
|
||||
Stats::Item* bufferHealth;
|
||||
|
||||
public:
|
||||
PeerStatsItem(Peer* peer)
|
||||
: peer(peer)
|
||||
{
|
||||
Stats::Item* item =
|
||||
createChildItem("Packets Thread"); // TODO: Rename this when possible. Unfortunately, Game service requires this to be here
|
||||
rakRate = item->createChildItem("Rate");
|
||||
rakActivity = item->createChildItem("Activity");
|
||||
physicsSenders = item->createChildItem("Physics Senders"); // TODO: More this out of "Packets Thread" when we refactor Game Service
|
||||
bufferHealth = item->createChildItem("Send Buffer Health");
|
||||
}
|
||||
|
||||
/* override */ void update()
|
||||
{
|
||||
rakActivity->formatPercent(peer->rakDutyCycle.dutyCycle());
|
||||
double rate = peer->rakDutyCycle.rate();
|
||||
rakRate->formatValue(rate, "%.2g/s", rate);
|
||||
|
||||
PhysicsService* physicsService = ServiceProvider::find<PhysicsService>(this);
|
||||
physicsSenders->formatValue(physicsService ? physicsService->numSenders() : 0);
|
||||
|
||||
double health = peer->rakPeer->GetBufferHealth();
|
||||
bufferHealth->formatValue(health, "%.4g", health);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class PacketReceiveJob : public DataModelJob
|
||||
{
|
||||
weak_ptr<DataModel> dataModel;
|
||||
ObscureValue<double> receiveRate;
|
||||
|
||||
public:
|
||||
weak_ptr<ConcurrentRakPeer> rakPeer;
|
||||
PacketReceiveJob(shared_ptr<ConcurrentRakPeer> rakPeer, DataModel* dataModel)
|
||||
: DataModelJob("Net PacketReceive", DataModelJob::DataIn, false, shared_from(dataModel), Time::Interval(0))
|
||||
, rakPeer(rakPeer)
|
||||
, dataModel(shared_from(dataModel))
|
||||
, receiveRate(NetworkSettings::singleton().getReceiveRate())
|
||||
{
|
||||
cyclicExecutive = true;
|
||||
cyclicPriority = CyclicExecutiveJobPriority_Network_ReceiveIncoming;
|
||||
}
|
||||
|
||||
private:
|
||||
Time::Interval sleepTime(const Stats& stats)
|
||||
{
|
||||
return computeStandardSleepTime(stats, receiveRate);
|
||||
}
|
||||
|
||||
virtual Job::Error error(const Stats& stats)
|
||||
{
|
||||
return computeStandardError(stats, receiveRate);
|
||||
}
|
||||
|
||||
|
||||
virtual TaskScheduler::StepResult stepDataModelJob(const Stats& stats)
|
||||
{
|
||||
if (shared_ptr<DataModel> safeDataModel = dataModel.lock())
|
||||
{
|
||||
FASTLOG1(FLog::DataModelJobs, "Packet receive start, data model: %p", safeDataModel.get());
|
||||
DataModel::scoped_write_request request(safeDataModel.get());
|
||||
|
||||
if (shared_ptr<ConcurrentRakPeer> safeRakPeer = rakPeer.lock())
|
||||
while (RakNet::Packet* packet = safeRakPeer->rawPeer()->Receive())
|
||||
safeRakPeer->DeallocatePacket(packet);
|
||||
|
||||
FASTLOG1(FLog::DataModelJobs, "Packet receive finish, data model: %p", safeDataModel.get());
|
||||
return TaskScheduler::Stepped;
|
||||
}
|
||||
return TaskScheduler::Done;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Peer::setOutgoingKBPSLimit(int limit)
|
||||
{
|
||||
if (limit <= 0)
|
||||
rakPeer->rawPeer()->SetPerConnectionOutgoingBandwidthLimit(0);
|
||||
else
|
||||
rakPeer->rawPeer()->SetPerConnectionOutgoingBandwidthLimit(1000 * G3D::iClamp(limit, 10, 10000));
|
||||
}
|
||||
|
||||
void Peer::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
||||
{
|
||||
Aya::Stats::StatsService* stats = ServiceProvider::find<Aya::Stats::StatsService>(oldProvider);
|
||||
if (stats)
|
||||
{
|
||||
shared_ptr<Stats::Item> network = shared_from_polymorphic_downcast<Stats::Item>(stats->findFirstChildByName("Network"));
|
||||
if (network)
|
||||
network->setParent(NULL);
|
||||
}
|
||||
|
||||
if (receiveJob)
|
||||
{
|
||||
receiveJob->rakPeer.reset(); // Make sure it doesn't try to run from now on. (The concurrency is such that it isn't running now)
|
||||
TaskScheduler::singleton().remove(receiveJob);
|
||||
receiveJob.reset();
|
||||
}
|
||||
|
||||
// Ensure that all Replicators are removed, because they
|
||||
// point to rakPeer, which is about to be deleted
|
||||
this->removeAllChildren();
|
||||
|
||||
if (rakPeer)
|
||||
{
|
||||
rakPeer->rawPeer()->DetachPlugin(this);
|
||||
rakPeer.reset();
|
||||
}
|
||||
|
||||
Super::onServiceProvider(oldProvider, newProvider);
|
||||
|
||||
if (newProvider)
|
||||
{
|
||||
rakPeer.reset(new ConcurrentRakPeer(new ProfiledRakPeer(*this), boost::polymorphic_downcast<DataModel*>(newProvider)));
|
||||
rakPeer->rawPeer()->AttachPlugin(this);
|
||||
// rakPeer->rawPeer()->SetMTUSize(1400);
|
||||
onCreateRakPeer();
|
||||
|
||||
receiveJob = shared_ptr<PacketReceiveJob>(new PacketReceiveJob(rakPeer, boost::polymorphic_downcast<DataModel*>(newProvider)));
|
||||
TaskScheduler::singleton().add(receiveJob);
|
||||
}
|
||||
|
||||
stats = ServiceProvider::find<Aya::Stats::StatsService>(newProvider);
|
||||
if (stats)
|
||||
{
|
||||
AYAASSERT(!shared_from_polymorphic_downcast<Stats::Item>(stats->findFirstChildByName("Network")));
|
||||
shared_ptr<Stats::Item> network = Creatable<Instance>::create<PeerStatsItem>(this);
|
||||
network->setName("Network");
|
||||
network->setParent(stats);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Network
|
||||
} // namespace Aya
|
||||
Reference in New Issue
Block a user