mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
734 lines
23 KiB
C++
734 lines
23 KiB
C++
/* Copyright 2003-2006 ROBLOX Corporation, All Rights Reserved */
|
|
|
|
#include "Server.h"
|
|
|
|
#include "Network/API.h"
|
|
#include "Network/NetworkClusterPacketCache.h"
|
|
#include "Network/Players.h"
|
|
#include "ConcurrentRakPeer.h"
|
|
#include "NetworkSettings.h"
|
|
#include "NetworkOwnerJob.h"
|
|
#include "ServerReplicator.h"
|
|
#include "Util.h"
|
|
|
|
#include "Script/ModuleScript.h"
|
|
#include "Script/script.h"
|
|
#include "Util/http.h"
|
|
#include "Util/RobloxGoogleAnalytics.h"
|
|
#include "Util/Statistics.h"
|
|
#include "Util/SoundService.h"
|
|
#include "V8DataModel/DataModel.h"
|
|
#include "V8DataModel/partinstance.h"
|
|
#include "V8DataModel/Workspace.h" // TODO - move distributed physics switch somewhere else
|
|
#include "V8DataModel/message.h"
|
|
#include "V8DataModel/MegaCluster.h"
|
|
#include "V8datamodel/TimerService.h"
|
|
#include "V8World/Assembly.h"
|
|
#include "V8World/Mechanism.h"
|
|
#include "V8World/Primitive.h"
|
|
|
|
#include "RakPeer.h"
|
|
#include "GetTime.h"
|
|
#include <boost/thread/xtime.hpp>
|
|
#include <sstream>
|
|
|
|
#if !defined(_WIN32)
|
|
#include <ifaddrs.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#include "FastLog.h"
|
|
|
|
#include "script/LuaVM.h"
|
|
|
|
DYNAMIC_LOGGROUP(NetworkJoin)
|
|
|
|
FASTFLAG(DebugLocalRccServerConnection)
|
|
DYNAMIC_FASTFLAG(DebugDisableTimeoutDisconnect)
|
|
DYNAMIC_FASTFLAGVARIABLE(RCCSupportCloudEdit, false)
|
|
DYNAMIC_FASTFLAGVARIABLE(CloudEditGARespectsThrottling, false)
|
|
DYNAMIC_FASTFLAGVARIABLE(CloudEditCheckClientPresent, false)
|
|
|
|
using namespace RBX;
|
|
using namespace Network;
|
|
using namespace RakNet;
|
|
|
|
const char* const Network::sServer = "NetworkServer";
|
|
|
|
REFLECTION_BEGIN();
|
|
static Reflection::BoundFuncDesc<Server, void(int, int)> server_startFunction(&Server::start, "Start", "port", 0, "threadSleepTime", 20, Security::Plugin);
|
|
static Reflection::BoundFuncDesc<Server, void(int)> f_disconnect(&Server::stop, "Stop", "blockDuration", 1000, Security::LocalUser);
|
|
static Reflection::BoundFuncDesc<Server, int()> f_GetClientCount(&Server::getClientCount, "GetClientCount", Security::LocalUser);
|
|
static Reflection::PropDescriptor<Server, int> prop_Port("Port", category_Data, &Server::getPort, NULL);
|
|
static Reflection::BoundFuncDesc<Server, void(bool)> func_SetIsPlayerAuthenticationRequired(&Server::setIsPlayerAuthenticationRequired, "SetIsPlayerAuthenticationRequired", "value", Security::Roblox);
|
|
static Reflection::BoundFuncDesc<Server, void()> func_ConfigureAsCloudEditServer(&Server::configureAsCloudEditServer, "ConfigureAsCloudEditServer", Security::Roblox);
|
|
|
|
static Reflection::EventDesc<Server, void(shared_ptr<Instance>, FilterResult, shared_ptr<Instance>, std::string)> desc_dataBasicFiltered(&Server::dataBasicFilteredSignal, "DataBasicFiltered", "peer", "result", "instance", "member", Security::LocalUser);
|
|
static Reflection::EventDesc<Server, void(shared_ptr<Instance>, FilterResult, shared_ptr<Instance>, std::string)> desc_dataCustomFiltered(&Server::dataCustomFilteredSignal, "DataCustomFiltered", "peer", "result", "instance", "member", Security::LocalUser);
|
|
|
|
Reflection::EventDesc<Server, void(std::string, shared_ptr<Instance>)> event_IncommingConnection(&Server::incommingConnectionSignal, "IncommingConnection", "peer", "replicator", Security::RobloxScript);
|
|
REFLECTION_END();
|
|
|
|
static const int maxClients = 128;
|
|
|
|
static shared_ptr<Network::ServerReplicator> createReplicator(RakNet::SystemAddress a, Network::Server* s, NetworkSettings* networkSettings)
|
|
{
|
|
// Creates an ordinary ServerReplicator without security or cheat handling code
|
|
return Creatable<Instance>::create<Network::ServerReplicator>(a, s, networkSettings);
|
|
}
|
|
|
|
boost::function<shared_ptr<ServerReplicator>(RakNet::SystemAddress, Server*, NetworkSettings*)> Server::createReplicator = ::createReplicator;
|
|
|
|
// allowedSecuirtyVersions is modified from a RCCService thread,
|
|
// we need to protect it with a mutex because this list is checked every time a new client joins the server.
|
|
static std::vector<std::string> allowedSecurityVersions;
|
|
static boost::mutex securityVersionsMutex;
|
|
|
|
struct Accumulator
|
|
{
|
|
float total;
|
|
int num;
|
|
|
|
Accumulator() : total(0), num(0) {}
|
|
void add(float value) {total += value; num++;}
|
|
float getAvgValue() {return total / num;}
|
|
};
|
|
|
|
static void reportServerStats(weak_ptr<Server> server)
|
|
{
|
|
shared_ptr<Server> sharedServer = server.lock();
|
|
if (!sharedServer)
|
|
return;
|
|
|
|
DataModel* dm = DataModel::get(sharedServer.get());
|
|
if (!dm)
|
|
return;
|
|
|
|
double totalKBytesSendPerSec = 0;
|
|
double totalDataBytesSendPerSec = 0;
|
|
double totalPhysicsBytesSendPerSec = 0;
|
|
int numPlayers = 0;
|
|
|
|
typedef boost::unordered_map<std::string, Accumulator> PacketLossPercentByPlatforms;
|
|
PacketLossPercentByPlatforms packetLossPercentByPlatforms;
|
|
|
|
if (sharedServer->getChildren())
|
|
{
|
|
Instances::const_iterator end = sharedServer->getChildren()->end();
|
|
for (Instances::const_iterator iter = sharedServer->getChildren()->begin(); iter != end; ++iter)
|
|
{
|
|
if (ServerReplicator* rep = Instance::fastDynamicCast<ServerReplicator>(iter->get()))
|
|
{
|
|
if (Player* player = rep->getRemotePlayer())
|
|
{
|
|
// log stats only for player that has been in game for more then 5 mins
|
|
int elapsedTime = (RakNet::GetTimeUS() - rep->stats().peerStats.rakStats.connectionStartTime) / 1e6f;
|
|
if (elapsedTime > 5 * 60)
|
|
{
|
|
std::string osPlatform = player->getOsPlatform();
|
|
|
|
const ReplicatorStats& stats = rep->stats();
|
|
totalKBytesSendPerSec += stats.kiloBytesSentPerSecond;
|
|
totalDataBytesSendPerSec += stats.dataPacketsSent.rate() * stats.dataPacketsSentSize.value();
|
|
totalPhysicsBytesSendPerSec += stats.physicsSenderStats.physicsPacketsSent.rate() * stats.physicsSenderStats.physicsPacketsSentSize.value();
|
|
numPlayers++;
|
|
|
|
packetLossPercentByPlatforms[osPlatform].add(rep->stats().peerStats.maxPacketloss);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (totalKBytesSendPerSec)
|
|
{
|
|
Analytics::EphemeralCounter::reportStats("ServerBytesSentPerSec", totalKBytesSendPerSec);
|
|
Analytics::EphemeralCounter::reportStats("ServerDataBytesSentPerSec", totalDataBytesSendPerSec);
|
|
Analytics::EphemeralCounter::reportStats("ServerPhysicsBytesSentPerSec", totalPhysicsBytesSendPerSec);
|
|
|
|
Analytics::EphemeralCounter::reportStats("ServerBytesSentPerSecPerPlayer", totalKBytesSendPerSec / numPlayers);
|
|
Analytics::EphemeralCounter::reportStats("ServerDataBytesSentPerSecPerPlayer", totalDataBytesSendPerSec / numPlayers);
|
|
Analytics::EphemeralCounter::reportStats("ServerPhysicsBytesSentPerSecPerPlayer", totalPhysicsBytesSendPerSec / numPlayers);
|
|
}
|
|
|
|
for (PacketLossPercentByPlatforms::iterator i = packetLossPercentByPlatforms.begin(); i != packetLossPercentByPlatforms.end(); i++)
|
|
{
|
|
Analytics::EphemeralCounter::reportStats("ServerPacketLossPercent_"+i->first, i->second.getAvgValue());
|
|
}
|
|
|
|
dm->create<TimerService>()->delay(boost::bind(&reportServerStats, server), 10*60);
|
|
}
|
|
|
|
Server::Server(void)
|
|
:outgoingPort(0)
|
|
, isPlayerAuthenticationRequired(false)
|
|
, networkSettings(&NetworkSettings::singleton())
|
|
, isCloudEditServer(false)
|
|
{
|
|
Security::Context::current().requirePermission(Security::Plugin, "create a NetworkServer");
|
|
setName(sServer);
|
|
|
|
//Allow empty script to always come in
|
|
registerLegalScript("");
|
|
scriptsByCurrentBytecode[""] = 0;
|
|
scriptsByLegacyBytecode[""] = 0;
|
|
|
|
|
|
FASTLOG(FLog::Network, "NetworkServer:Create");
|
|
}
|
|
|
|
Server::~Server(void)
|
|
{
|
|
FASTLOG(FLog::Network, "NetworkServer:Destroy");
|
|
}
|
|
|
|
bool Server::serverIsPresent(const Instance* context, bool testInDatamodel)
|
|
{
|
|
const ServiceProvider* serviceProvider = ServiceProvider::findServiceProvider(context);
|
|
RBXASSERT(!testInDatamodel || serviceProvider!=NULL);
|
|
return ServiceProvider::find<Server>(serviceProvider)!=NULL;
|
|
}
|
|
|
|
std::vector<std::string> Server::getAllIPv4Addresses()
|
|
{
|
|
std::vector<std::string> ipAddresses;
|
|
|
|
#if defined(_WIN32)
|
|
WORD wVersionRequested;
|
|
WSADATA wsaData;
|
|
char name[255];
|
|
PHOSTENT hostinfo;
|
|
wVersionRequested = MAKEWORD( 1, 1 );
|
|
char *ip;
|
|
|
|
if ( WSAStartup( wVersionRequested, &wsaData ) == 0 )
|
|
{
|
|
if( gethostname ( name, sizeof(name)) == 0)
|
|
{
|
|
if((hostinfo = gethostbyname(name)) != NULL)
|
|
{
|
|
int nCount = 0;
|
|
while(hostinfo->h_addr_list[nCount])
|
|
{
|
|
ip = inet_ntoa(*(
|
|
struct in_addr *)hostinfo->h_addr_list[nCount]);
|
|
ipAddresses.push_back(std::string(ip));
|
|
nCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ips that we get here are only external,
|
|
// so add a local address as well
|
|
// server can't connect to same machine clients otherwise
|
|
ipAddresses.push_back("127.0.0.1");
|
|
|
|
#else
|
|
struct ifaddrs * ifAddrStruct = NULL;
|
|
struct ifaddrs * ifa = NULL;
|
|
void * tmpAddrPtr = NULL;
|
|
|
|
getifaddrs(&ifAddrStruct);
|
|
|
|
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next)
|
|
{
|
|
if (!ifa->ifa_addr)
|
|
{
|
|
continue;
|
|
}
|
|
if (ifa->ifa_addr->sa_family == AF_INET)
|
|
{
|
|
tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
|
|
char addressBuffer[INET_ADDRSTRLEN];
|
|
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
|
|
ipAddresses.push_back(std::string(addressBuffer));
|
|
}
|
|
}
|
|
|
|
if (ifAddrStruct!=NULL)
|
|
freeifaddrs(ifAddrStruct);
|
|
#endif
|
|
|
|
return ipAddresses;
|
|
}
|
|
|
|
void Server::start(int port, int threadSleepTime)
|
|
{
|
|
if (DFFlag::CloudEditCheckClientPresent && Players::clientIsPresent(this))
|
|
throw RBX::runtime_error("Can not call server, client is present.");
|
|
|
|
FASTLOG(FLog::Network, "NetworkServer:Start");
|
|
|
|
StartupResult res = STARTUP_OTHER_FAILURE;
|
|
std::vector<std::string> addresses;
|
|
|
|
#ifdef RBX_STUDIO_BUILD
|
|
addresses = Server::getAllIPv4Addresses();
|
|
shared_ptr<RakNet::SocketDescriptor> sdArray(new RakNet::SocketDescriptor[addresses.size()]);
|
|
|
|
for (unsigned int i = 0; i < addresses.size(); i++)
|
|
{
|
|
sdArray.get()[i].port = port;
|
|
strcpy(sdArray.get()[i].hostAddress, addresses[i].c_str());
|
|
}
|
|
|
|
res = rakPeer->rawPeer()->Startup(maxClients, sdArray.get(), addresses.size());
|
|
#else
|
|
RakNet::SocketDescriptor d(port, 0);
|
|
res = rakPeer->rawPeer()->Startup(maxClients, &d, 1);
|
|
#endif
|
|
|
|
if (res != RakNet::RAKNET_STARTED)
|
|
throw std::runtime_error(RBX::format("Failed to start network server, id %d", res));
|
|
|
|
#ifdef RBX_STUDIO_BUILD
|
|
for (unsigned int i = 0; i < addresses.size(); i++)
|
|
{
|
|
StandardOut::singleton()->printf(MESSAGE_SENSITIVE,"Started network server on %s|%i",addresses[i].c_str(),port);
|
|
}
|
|
#else
|
|
RakNet::SystemAddress address = rakPeer->rawPeer()->GetMyBoundAddress();
|
|
outgoingPort = address.GetPort();
|
|
|
|
StandardOut::singleton()->printf(MESSAGE_SENSITIVE, "Started network server %s", RakNetAddressToString(address).c_str());
|
|
#endif
|
|
|
|
DataModel *dataModel = DataModel::get(this);
|
|
|
|
int startupMillis = static_cast<int>((Time::nowFast() - dataModel->getDataModelInitTime()).msec());
|
|
RobloxGoogleAnalytics::trackUserTiming(GA_CATEGORY_GAME, "ServerStartTime", startupMillis);
|
|
|
|
if(DFFlag::DebugDisableTimeoutDisconnect)
|
|
rakPeer->rawPeer()->SetTimeoutTime(10*60*1000, UNASSIGNED_SYSTEM_ADDRESS);
|
|
|
|
|
|
dataModel->create<TimerService>()->delay(boost::bind(&reportServerStats, weak_from(this)), 5 * 60);
|
|
}
|
|
|
|
static bool isReplicator(shared_ptr<Instance> instance)
|
|
{
|
|
return Instance::fastDynamicCast<Replicator>(instance.get())!=NULL;
|
|
}
|
|
|
|
int Server::getClientCount()
|
|
{
|
|
if (DFFlag::CloudEditCheckClientPresent && Players::clientIsPresent(this))
|
|
throw RBX::runtime_error("Can not call server, client is present.");
|
|
|
|
if (getChildren())
|
|
return std::count_if(getChildren()->begin(), getChildren()->end(), &isReplicator);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void Server::stop(int blockDuration)
|
|
{
|
|
if (DFFlag::CloudEditCheckClientPresent && Players::clientIsPresent(this))
|
|
throw RBX::runtime_error("Can not call server, client is present.");
|
|
|
|
FASTLOG1(FLog::Network, "NetworkServer:Stop blockDuration(%d)", blockDuration);
|
|
|
|
// The following line will remove the Replicators
|
|
// we have to do this first before shutting down rakpeer because replicator might hold
|
|
// a list of unprocessed packets that was allocated from a pool inside rakpeer. rakpeer
|
|
// clears this pool in shutdown.
|
|
this->visitChildren(boost::bind(&Instance::unlockParent, _1));
|
|
this->removeAllChildren();
|
|
|
|
if (rakPeer->rawPeer()->IsActive())
|
|
rakPeer->rawPeer()->Shutdown(blockDuration);
|
|
}
|
|
|
|
static void reportCloudEditGA(const char* label, int value = 0)
|
|
{
|
|
if (DFFlag::CloudEditGARespectsThrottling)
|
|
{
|
|
RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_GAME, "CloudEdit", label, value);
|
|
}
|
|
else
|
|
{
|
|
RobloxGoogleAnalytics::trackEventWithoutThrottling(GA_CATEGORY_GAME, "CloudEdit", label, value);
|
|
}
|
|
}
|
|
|
|
static void reportCloudEditStats(weak_ptr<Server> server)
|
|
{
|
|
shared_ptr<Server> sharedServer = server.lock();
|
|
if (!sharedServer)
|
|
return;
|
|
|
|
DataModel* dm = DataModel::get(sharedServer.get());
|
|
if (!dm)
|
|
return;
|
|
|
|
RBXASSERT(dm->currentThreadHasWriteLock());
|
|
|
|
int players = -1;
|
|
if (Players* p = dm->find<Players>())
|
|
{
|
|
players = p->numChildren();
|
|
}
|
|
|
|
reportCloudEditGA("5 Minute Usage", players);
|
|
|
|
dm->create<TimerService>()->delay(boost::bind(&reportCloudEditStats, server), 5*60);
|
|
}
|
|
|
|
void Server::configureAsCloudEditServer()
|
|
{
|
|
if (DFFlag::CloudEditCheckClientPresent && Players::clientIsPresent(this))
|
|
throw RBX::runtime_error("Can not call server, client is present.");
|
|
|
|
if (!DFFlag::RCCSupportCloudEdit)
|
|
{
|
|
return;
|
|
}
|
|
|
|
initWithCloudEditSecurity();
|
|
rakPeer->rawPeer()->SetIncomingPassword(Network::versionB.c_str(), Network::versionB.size());
|
|
isCloudEditServer = true;
|
|
reportCloudEditGA("Server Start");
|
|
DataModel::get(this)->create<TimerService>()->delay(boost::bind(&reportCloudEditStats, weak_from(this)), 5*60);
|
|
}
|
|
|
|
void Server::onCreateRakPeer()
|
|
{
|
|
Super::onCreateRakPeer();
|
|
rakPeer->rawPeer()->SetMaximumIncomingConnections(maxClients);
|
|
if (FFlag::DebugLocalRccServerConnection)
|
|
{
|
|
Network::versionB = "test";
|
|
}
|
|
rakPeer->rawPeer()->SetIncomingPassword(Network::versionB.c_str(), Network::versionB.size());
|
|
}
|
|
|
|
|
|
void Server::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
|
{
|
|
if (oldProvider)
|
|
{
|
|
TaskScheduler::singleton().remove(networkOwnerJob);
|
|
networkOwnerJob.reset();
|
|
|
|
if (players)
|
|
{
|
|
players->setConnection(NULL);
|
|
stop();
|
|
players.reset();
|
|
}
|
|
itemAddedConnection.disconnect();
|
|
workspaceLoadedConnection.disconnect();
|
|
}
|
|
|
|
|
|
|
|
if (newProvider && Players::clientIsPresent(newProvider))
|
|
throw RBX::runtime_error("Can not create server, client is present.");
|
|
|
|
Super::onServiceProvider(oldProvider, newProvider);
|
|
|
|
if (newProvider)
|
|
{
|
|
players = shared_from(ServiceProvider::create<Players>(newProvider));
|
|
players->setConnection(rakPeer.get());
|
|
|
|
DataModel* dataModel = boost::polymorphic_downcast<DataModel*>(newProvider);
|
|
|
|
if (networkSettings->usePhysicsPacketCache)
|
|
physicsPacketCache = ServiceProvider::create<PhysicsPacketCache>(newProvider);
|
|
|
|
if (networkSettings->useInstancePacketCache)
|
|
instancePacketCache = ServiceProvider::create<InstancePacketCache>(newProvider);
|
|
|
|
ServiceProvider::create<OneQuarterClusterPacketCache >(newProvider);
|
|
ServiceProvider::create<ClusterPacketCache>(newProvider);
|
|
|
|
if (networkSettings->distributedPhysicsEnabled)
|
|
{
|
|
networkOwnerJob = shared_ptr<NetworkOwnerJob>( new NetworkOwnerJob(shared_from(dataModel) ) );
|
|
TaskScheduler::singleton().add(networkOwnerJob);
|
|
}
|
|
|
|
RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_GAME, "PlaceID", "none", dataModel->getPlaceID());
|
|
|
|
|
|
if (0 == dataModel->getPlaceID())
|
|
{
|
|
onWorkspaceLoaded();
|
|
}
|
|
else
|
|
{
|
|
workspaceLoadedConnection = dataModel->workspaceLoadedSignal.connect(boost::bind(&Server::onWorkspaceLoaded, this));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Server::onWorkspaceLoaded()
|
|
{
|
|
Workspace *workspace = ServiceProvider::find<Workspace>(this);
|
|
|
|
if (workspace->getNetworkStreamingEnabled())
|
|
RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_GAME, "NetworkStreamingEnabled");
|
|
|
|
{
|
|
MegaClusterInstance *megaCluster = Instance::fastDynamicCast<MegaClusterInstance>(workspace->getTerrain());
|
|
if (megaCluster && megaCluster->isAllocated())
|
|
{
|
|
char placeId[32];
|
|
sprintf(placeId, "%d", DataModel::get(this)->getPlaceID());
|
|
|
|
if (megaCluster->isSmooth())
|
|
{
|
|
RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_GAME, "SmoothTerrain", placeId);
|
|
}
|
|
else
|
|
{
|
|
RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_GAME, "LegacyTerrain", placeId);
|
|
}
|
|
}
|
|
}
|
|
|
|
StarterPlayerService* sps = ServiceProvider::create<StarterPlayerService>(this);
|
|
if (sps)
|
|
sps->recordSettingsInGA();
|
|
|
|
if (DataModel* dataModel = DataModel::get(this))
|
|
{
|
|
dataModel->visitDescendants(boost::bind(&Server::onItemAdded, this, _1));
|
|
itemAddedConnection = dataModel->onDemandWrite()->descendantAddedSignal.connect(boost::bind(&Server::onItemAdded, this, _1));
|
|
}
|
|
else
|
|
{
|
|
RBXASSERT(false);
|
|
}
|
|
}
|
|
|
|
void Server::onItemAdded(shared_ptr<Instance> item)
|
|
{
|
|
boost::optional<std::string> scriptSource = getScriptSourceFromInstance(item.get());
|
|
|
|
if (scriptSource)
|
|
registerLegalScript(*scriptSource);
|
|
}
|
|
|
|
boost::optional<std::string> Server::getScriptSourceFromInstance(Instance* instance) const
|
|
{
|
|
if (const Script* script = Instance::fastDynamicCast<Script>(instance))
|
|
{
|
|
if (script->isCodeEmbedded())
|
|
{
|
|
return script->getEmbeddedCode().get().getSource();
|
|
}
|
|
else
|
|
{
|
|
return script->getCachedRemoteSource().getSource();
|
|
}
|
|
}
|
|
else if (const ModuleScript* moduleScript = Instance::fastDynamicCast<ModuleScript>(instance))
|
|
{
|
|
if (moduleScript->getScriptId().isNull())
|
|
{
|
|
return moduleScript->getSource().getSource();
|
|
}
|
|
else
|
|
{
|
|
return moduleScript->getCachedRemoteSource().getSource();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return boost::optional<std::string>();
|
|
}
|
|
}
|
|
|
|
bool Server::askAddChild(const Instance* instance) const
|
|
{
|
|
return Instance::fastDynamicCast<ServerReplicator>(instance) != NULL;
|
|
}
|
|
|
|
RakNet::PluginReceiveResult Server::OnReceive(RakNet::Packet *packet)
|
|
{
|
|
RakNet::PluginReceiveResult result = Super::OnReceive(packet);
|
|
if (result!=RR_CONTINUE_PROCESSING)
|
|
return result;
|
|
|
|
switch ((unsigned char) packet->data[0])
|
|
{
|
|
case ID_NEW_INCOMING_CONNECTION:
|
|
{
|
|
shared_ptr<ServerReplicator> proxy;
|
|
try
|
|
{
|
|
FASTLOG(FLog::Network, "NetworkServer:NewIncomingConnection");
|
|
FASTLOG1F(DFLog::NetworkJoin, "Server::OnReceive new connection @ %f s", Time::nowFastSec());
|
|
StandardOut::singleton()->printf(MESSAGE_SENSITIVE, "New connection from %s\n", RakNetAddressToString(packet->systemAddress).c_str());
|
|
|
|
proxy = createReplicator(packet->systemAddress, this, networkSettings);
|
|
proxy->setAndLockParent(this);
|
|
incommingConnectionSignal(RakNetAddressToString(packet->systemAddress), proxy);
|
|
}
|
|
catch (RBX::base_exception& e)
|
|
{
|
|
FASTLOG1(FLog::Error, "On receive packet %d", (unsigned char) packet->data[0]);
|
|
FASTLOGS(FLog::Error, "Error %s", e.what());
|
|
StandardOut::singleton()->printf(MESSAGE_ERROR, "Server::OnReceive packet %d: %s", (unsigned char) packet->data[0], e.what());
|
|
if (proxy)
|
|
{
|
|
// Disconnect
|
|
proxy->unlockParent();
|
|
proxy->setParent(NULL);
|
|
}
|
|
}
|
|
}
|
|
return RR_CONTINUE_PROCESSING;
|
|
default:
|
|
return RR_CONTINUE_PROCESSING;
|
|
}
|
|
}
|
|
|
|
void Server::setAllowedSecurityVersions(const std::vector<std::string>& versions)
|
|
{
|
|
boost::mutex::scoped_lock lock(securityVersionsMutex);
|
|
allowedSecurityVersions = versions;
|
|
}
|
|
|
|
bool Server::isLegalReceiveInstance(Instance* instance) const
|
|
{
|
|
return isScriptLegal(instance);
|
|
}
|
|
|
|
bool Server::isScriptLegal(Instance* instance) const
|
|
{
|
|
boost::optional<std::string> scriptSource = getScriptSourceFromInstance(instance);
|
|
|
|
return !scriptSource || scriptsBySource.find(*scriptSource) != scriptsBySource.end();
|
|
}
|
|
|
|
boost::optional<long> Server::getScriptIndexForSource(const std::string& source) const
|
|
{
|
|
boost::unordered_map<std::string, long>::const_iterator it = scriptsBySource.find(source);
|
|
|
|
if (it != scriptsBySource.end())
|
|
return it->second;
|
|
else
|
|
return boost::optional<long>();
|
|
}
|
|
|
|
boost::optional<long> Server::getScriptIndexForBytecode(const std::string& bytecode, bool isCurrentFormat) const
|
|
{
|
|
boost::unordered_map<std::string, long>::const_iterator it = isCurrentFormat ? scriptsByCurrentBytecode.find(bytecode)
|
|
: scriptsByLegacyBytecode.find(bytecode);
|
|
boost::unordered_map<std::string, long>::const_iterator end = isCurrentFormat ? scriptsByCurrentBytecode.end()
|
|
: scriptsByLegacyBytecode.end();
|
|
|
|
if (it != end)
|
|
return it->second;
|
|
else
|
|
return boost::optional<long>();
|
|
}
|
|
|
|
boost::optional<ProtectedString> Server::getScriptSourceForIndex(long index) const
|
|
{
|
|
if (index >= 0 && index < static_cast<long>(legalScripts.size()))
|
|
return ProtectedString::fromTrustedSource(legalScripts[index].source);
|
|
else
|
|
return boost::optional<ProtectedString>();
|
|
}
|
|
|
|
boost::optional<std::string> Server::getScriptBytecodeForIndex(long index, bool isCurrentFormat) const
|
|
{
|
|
if (index >= 0 && index < static_cast<long>(legalScripts.size()))
|
|
return isCurrentFormat ? legalScripts[index].currentBytecode : legalScripts[index].legacyBytecode;
|
|
else
|
|
return boost::optional<std::string>();
|
|
}
|
|
|
|
void Server::registerLegalScript(const std::string& source)
|
|
{
|
|
if (scriptsBySource.find(source) == scriptsBySource.end())
|
|
{
|
|
long index = legalScripts.size();
|
|
|
|
legalScripts.push_back(LegalScript());
|
|
auto& it = legalScripts.back();
|
|
it.source = source;
|
|
it.currentBytecode = LuaVM::compile(source);
|
|
it.legacyBytecode = LuaVM::compileLegacy(source);
|
|
scriptsBySource[source] = index;
|
|
|
|
if (!it.currentBytecode.empty())
|
|
{
|
|
scriptsByCurrentBytecode[it.currentBytecode] = index;
|
|
scriptsByLegacyBytecode[it.legacyBytecode] = index;
|
|
}
|
|
}
|
|
}
|
|
|
|
boost::optional<int> Server::getPlaceAuthenticationResultForOrigin(int originPlaceId)
|
|
{
|
|
boost::mutex::scoped_lock lock(placeAuthenticationResultCacheMutex);
|
|
|
|
boost::unordered_map<int, int>::const_iterator iter = placeAuthenticationResultCache.find(originPlaceId);
|
|
|
|
if (iter != placeAuthenticationResultCache.end())
|
|
return iter->second;
|
|
else
|
|
return boost::optional<int>();
|
|
}
|
|
|
|
void Server::registerPlaceAuthenticationResult(int originPlaceId, int result)
|
|
{
|
|
boost::mutex::scoped_lock lock(placeAuthenticationResultCacheMutex);
|
|
placeAuthenticationResultCache[originPlaceId] = result;
|
|
}
|
|
|
|
bool Server::securityKeyMatches(const std::string& key)
|
|
{
|
|
if (FFlag::DebugLocalRccServerConnection)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (isCloudEdit())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
boost::mutex::scoped_lock lock(securityVersionsMutex);
|
|
if (allowedSecurityVersions.empty())
|
|
return true;
|
|
|
|
for (unsigned int i = 0; i < allowedSecurityVersions.size(); i++)
|
|
{
|
|
if (key == allowedSecurityVersions[i])
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Server::protocolVersionMatches(int protocolVer)
|
|
{
|
|
BOOST_STATIC_ASSERT(NETWORK_PROTOCOL_VERSION_MIN <= NETWORK_PROTOCOL_VERSION);
|
|
|
|
return protocolVer >= NETWORK_PROTOCOL_VERSION_MIN && protocolVer <= NETWORK_PROTOCOL_VERSION;
|
|
}
|
|
|
|
NetworkOwnerJob* Server::getNetworkOwnerJob()
|
|
{
|
|
if (networkOwnerJob)
|
|
{
|
|
return static_cast<NetworkOwnerJob*>(networkOwnerJob.get());
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|