diff --git a/Client/App/App.vcproj b/Client/App/App.vcproj
index 6176a9ea..2cd03e80 100644
--- a/Client/App/App.vcproj
+++ b/Client/App/App.vcproj
@@ -1,1901 +1,1909 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Client/App/include/reflection/property.h b/Client/App/include/reflection/property.h
index 5d2e6197..286fc2b0 100644
--- a/Client/App/include/reflection/property.h
+++ b/Client/App/include/reflection/property.h
@@ -39,7 +39,10 @@ namespace RBX
public:
bool isPublic() const;
virtual bool isReadOnly() const = 0;
- bool canStreamWrite() const;
+ bool canStreamWrite() const
+ {
+ return bCanStreamWrite;
+ }
bool operator==(const PropertyDescriptor& other) const
{
return this == &other;
diff --git a/Client/App/include/util/Events.h b/Client/App/include/util/Events.h
index 6956549c..ed7e24a4 100644
--- a/Client/App/include/util/Events.h
+++ b/Client/App/include/util/Events.h
@@ -24,6 +24,7 @@ namespace RBX
protected:
virtual void onEvent(const Class*, Event) = 0;
Listener& operator=(const Listener&);
+
virtual ~Listener()
{
}
@@ -38,13 +39,18 @@ namespace RBX
protected:
Notifier(const Notifier&);
+
Notifier()
: listeners(),
raiseRange(NULL)
{
}
+
Notifier& operator=(const Notifier&);
- virtual ~Notifier();
+
+ virtual ~Notifier()
+ {
+ }
public:
void addListener(Listener*) const;
diff --git a/Client/App/include/util/HitTestFilter.h b/Client/App/include/util/HitTestFilter.h
index 8f15d2d9..6427e03e 100644
--- a/Client/App/include/util/HitTestFilter.h
+++ b/Client/App/include/util/HitTestFilter.h
@@ -15,11 +15,6 @@ namespace RBX
};
public:
- virtual Result filterResult(const Primitive*) const = 0;
- public:
- //HitTestFilter(const HitTestFilter&);
- HitTestFilter();
- public:
- //HitTestFilter& operator=(const HitTestFilter&);
+ virtual Result filterResult(const Primitive* testMe) const = 0;
};
}
diff --git a/Client/App/include/util/Object.h b/Client/App/include/util/Object.h
index 21765710..84156a1c 100644
--- a/Client/App/include/util/Object.h
+++ b/Client/App/include/util/Object.h
@@ -11,6 +11,18 @@ namespace RBX
return r ? boost::shared_static_cast(r->shared_from_this()) : boost::shared_ptr();
}
+ template
+ boost::shared_ptr shared_from_dynamic_cast(boost::enable_shared_from_this* r)
+ {
+ return r ? boost::shared_dynamic_cast(r->shared_from_this()) : boost::shared_ptr();
+ }
+
+ template
+ boost::shared_ptr shared_from_polymorphic_downcast(boost::enable_shared_from_this* r)
+ {
+ return r ? boost::shared_polymorphic_downcast(r->shared_from_this()) : boost::shared_ptr();
+ }
+
class Object
{
public:
diff --git a/Client/App/include/util/Profiling.h b/Client/App/include/util/Profiling.h
index 534e66cd..272e1a73 100644
--- a/Client/App/include/util/Profiling.h
+++ b/Client/App/include/util/Profiling.h
@@ -58,7 +58,7 @@ namespace RBX
//ThreadProfiler(const ThreadProfiler&);
ThreadProfiler(const char* name);
public:
- void sample(void* thread);
+ void sample(HANDLE thread);
public:
~ThreadProfiler() {}
public:
diff --git a/Client/App/include/v8datamodel/Filters.h b/Client/App/include/v8datamodel/Filters.h
new file mode 100644
index 00000000..198fc7f0
--- /dev/null
+++ b/Client/App/include/v8datamodel/Filters.h
@@ -0,0 +1,37 @@
+#pragma once
+#include "util/HitTestFilter.h"
+#include "v8datamodel/ModelInstance.h"
+#include "v8datamodel/PartInstance.h"
+#include
+
+namespace RBX
+{
+ class Unlocked : public HitTestFilter
+ {
+ public:
+ virtual HitTestFilter::Result filterResult(const Primitive* testMe) const
+ {
+ return unlocked(testMe) ? HitTestFilter::INCLUDE_PRIM : HitTestFilter::STOP_TEST;
+ }
+
+ static bool unlocked(const Primitive* testMe);
+ };
+
+ class PartByLocalCharacter : public HitTestFilter
+ {
+ protected:
+ boost::shared_ptr character;
+ boost::shared_ptr head;
+
+ public:
+ PartByLocalCharacter(Instance* root);
+ virtual HitTestFilter::Result filterResult(const Primitive* testMe) const;
+ };
+
+ class UnlockedPartByLocalCharacter : public PartByLocalCharacter
+ {
+ public:
+ UnlockedPartByLocalCharacter(Instance*);
+ virtual HitTestFilter::Result filterResult(const Primitive* testMe) const;
+ };
+}
diff --git a/Client/App/include/v8datamodel/Stats.h b/Client/App/include/v8datamodel/Stats.h
index 2db38144..4f06fd17 100644
--- a/Client/App/include/v8datamodel/Stats.h
+++ b/Client/App/include/v8datamodel/Stats.h
@@ -40,6 +40,9 @@ namespace RBX
template
Item* createChildItem(const char* name, boost::function0 func);
+
+ template
+ void formatValue(const T& value);
};
template
diff --git a/Client/App/include/v8world/Mechanism.h b/Client/App/include/v8world/Mechanism.h
index d7a147a8..65e45bae 100644
--- a/Client/App/include/v8world/Mechanism.h
+++ b/Client/App/include/v8world/Mechanism.h
@@ -18,9 +18,22 @@ namespace RBX
std::vector trackers;
public:
std::list::iterator myIt;
- Mechanism::~Mechanism() { RBXASSERT(this->trackers.size() == 0); }
- const std::set& getAssemblies() const {return assemblies;}
- std::set& getAssemblies() {return assemblies;}
+
+ Mechanism::~Mechanism()
+ {
+ RBXASSERT(this->trackers.size() == 0);
+ }
+
+ const std::set& getAssemblies() const
+ {
+ return assemblies;
+ }
+
+ std::set& getAssemblies()
+ {
+ return assemblies;
+ }
+
void notifyMovingPrimitives();
void insertAssembly(Assembly* a);
void removeAssembly(Assembly* a);
@@ -38,8 +51,16 @@ namespace RBX
bool containedBy(Mechanism*);
void stopTracking();
public:
- //MechanismTracker();
- ~MechanismTracker() {this->stopTracking();}
+ MechanismTracker()
+ : mechanism(NULL)
+ {
+ }
+
+ ~MechanismTracker()
+ {
+ this->stopTracking();
+ }
+
bool tracking();
void setMechanism(Mechanism*);
Mechanism* getMechanism();
diff --git a/Client/App/include/v8world/SimJobStage.h b/Client/App/include/v8world/SimJobStage.h
index a4066d7e..936590f0 100644
--- a/Client/App/include/v8world/SimJobStage.h
+++ b/Client/App/include/v8world/SimJobStage.h
@@ -23,7 +23,6 @@ namespace RBX
public:
Mechanism* nextMechanism(std::list& list, const Mechanism* current);
public:
- //SimJobStage(const SimJobStage&);
SimJobStage(IStage* upstream, World* world);
virtual ~SimJobStage();
public:
@@ -36,6 +35,8 @@ namespace RBX
void onAssemblyAdded(Assembly* a);
void onAssemblyRemoving(Assembly* a);
void notifyMovingPrimitives();
- //SimJobStage& operator=(const SimJobStage&);
+
+ template
+ void reportMechanisms(Class& callback, MechanismTracker& tracker, const Mechanism* ignore);
};
}
diff --git a/Client/App/util/Profiling.cpp b/Client/App/util/Profiling.cpp
index 7f4b9b03..16c8107c 100644
--- a/Client/App/util/Profiling.cpp
+++ b/Client/App/util/Profiling.cpp
@@ -41,7 +41,7 @@ namespace RBX
{
}
- void ThreadProfiler::sample(void* thread)
+ void ThreadProfiler::sample(HANDLE thread)
{
double time = G3D::System::getTick();
if (bucketTimeSpan + lastSampleTime <= time)
diff --git a/Client/App/v8datamodel/Filters.cpp b/Client/App/v8datamodel/Filters.cpp
new file mode 100644
index 00000000..364569a1
--- /dev/null
+++ b/Client/App/v8datamodel/Filters.cpp
@@ -0,0 +1,32 @@
+#include "v8datamodel/Filters.h"
+#include "humanoid/Humanoid.h"
+#include "Network/Players.h"
+
+namespace RBX
+{
+ bool Unlocked::unlocked(const Primitive* testMe)
+ {
+ return !PartInstance::fromPrimitiveConst(testMe)->getPartLocked();
+ }
+
+ PartByLocalCharacter::PartByLocalCharacter(Instance* root)
+ {
+ character = shared_from(Network::Players::findLocalCharacter(root));
+ head = shared_from(Humanoid::getHeadFromCharacter(character.get()));
+ }
+
+ HitTestFilter::Result PartByLocalCharacter::filterResult(const Primitive* testMe) const
+ {
+ if (character && head)
+ {
+ const PartInstance* p = PartInstance::fromPrimitiveConst(testMe);
+
+ if (p->isDescendentOf(character.get()))
+ {
+ return head->getIsTransparent() ? HitTestFilter::IGNORE_PRIM : HitTestFilter::STOP_TEST;
+ }
+ }
+
+ return HitTestFilter::INCLUDE_PRIM;
+ }
+}
diff --git a/Client/Network/API.cpp b/Client/Network/API.cpp
index 543372bf..7d1ac00d 100644
--- a/Client/Network/API.cpp
+++ b/Client/Network/API.cpp
@@ -1,8 +1,27 @@
#include "API.h"
+#include "Client.h"
+#include "Server.h"
+#include "IdManager.h"
+#include "NetworkSettings.h"
+#include "Network/Players.h"
namespace RBX
{
namespace Network
{
+ void API::init(const char* version)
+ {
+ API::version = version;
+
+ Client::classDescriptor();
+ Server::classDescriptor();
+ Player::classDescriptor();
+ Players::classDescriptor();
+ IdManager::classDescriptor();
+ GlobalSettings::classDescriptor();
+ NetworkSettings::classDescriptor();
+
+ NetworkSettings::singleton();
+ }
}
}
diff --git a/Client/Network/Client.cpp b/Client/Network/Client.cpp
index 5c597ea2..090b969e 100644
--- a/Client/Network/Client.cpp
+++ b/Client/Network/Client.cpp
@@ -14,6 +14,17 @@ namespace RBX
{
namespace Network
{
+ Client::Client()
+ {
+ setName("NetworkClient");
+ updateLogger();
+ }
+
+ Client::~Client()
+ {
+ rakPeer->CloseConnection(serverId, true, 0);
+ }
+
void Client::disconnect(int blockDuration)
{
removeAllChildren();
@@ -39,5 +50,48 @@ namespace RBX
updateNetworkSimulator();
}
+
+ void Client::onServiceProvider(const ServiceProvider* oldProvider, const ServiceProvider* newProvider)
+ {
+ Listener* oldListener = this;
+
+ if (oldProvider)
+ {
+ oldProvider->Notifier::removeListener(oldListener);
+ }
+
+ if (oldProvider)
+ {
+ RunService* runService = oldProvider->find();
+
+ if (runService)
+ runService->runDisabled = false;
+
+ disconnect(3000);
+
+ Players* p = oldProvider->find();
+ p->setConnection(NULL);
+ }
+
+ Instance::onServiceProvider(oldProvider, newProvider);
+
+ if (newProvider)
+ {
+ Players* p = newProvider->create();
+ p->setConnection(rakPeer.get());
+
+ RunService* runService = newProvider->find();
+
+ if (runService)
+ runService->runDisabled = true;
+ }
+
+ Listener* newListener = this;
+
+ if (newProvider)
+ {
+ newProvider->Notifier::addListener(newListener);
+ }
+ }
}
}
diff --git a/Client/Network/Client.h b/Client/Network/Client.h
index 24b81312..71d8e634 100644
--- a/Client/Network/Client.h
+++ b/Client/Network/Client.h
@@ -31,7 +31,6 @@ namespace RBX
static Reflection::SignalDesc event_ConnectionRejected;
public:
- //Client(const Client&);
Client();
virtual ~Client();
void connect(std::string, int, int, int);
@@ -44,9 +43,8 @@ namespace RBX
protected:
virtual void onServiceProvider(const ServiceProvider* oldProvider, const ServiceProvider* newProvider);
virtual void onEvent(const ServiceProvider* source, Closing event);
- public:
- //Client& operator=(const Client&);
+ public:
static bool clientIsPresent(const Instance* context, bool testInDatamodel);
};
}
diff --git a/Client/Network/IdManager.cpp b/Client/Network/IdManager.cpp
index 4ac908e8..4182d662 100644
--- a/Client/Network/IdManager.cpp
+++ b/Client/Network/IdManager.cpp
@@ -5,13 +5,6 @@
namespace RBX
{
- void IdManager::addInstance(Instance* instance, Guid::Data explicitId)
- {
- RBXASSERT(getInstance(explicitId) == NULL);
-
- instance->assignGuid(explicitId);
- addInstance(instance);
- }
void IdManager::addInstance(Instance* instance)
{
diff --git a/Client/Network/IdManager.h b/Client/Network/IdManager.h
index 1f9b1a9e..960f8a4f 100644
--- a/Client/Network/IdManager.h
+++ b/Client/Network/IdManager.h
@@ -1,3 +1,4 @@
+#pragma once
#include "reflection/reflection.h"
#include "v8tree/Instance.h"
#include "v8tree/Service.h"
@@ -15,18 +16,29 @@ namespace RBX
private:
std::map items;
boost::signals::scoped_connection removeInstanceConnection;
+
public:
- Instance* getInstance(Guid::Data id);
- void addInstance(Instance* instance, Guid::Data explicitId);
+ Instance* getInstance(Guid::Data id)
+ {
+ if (*id.scope == Name::getNullName())
+ return NULL;
+
+ std::map::const_iterator found = items.find(id);
+ return found != items.end() ? found->second : NULL;
+ }
+
+ void addInstance(Instance* instance, Guid::Data explicitId)
+ {
+ RBXASSERT(getInstance(explicitId) == NULL);
+
+ instance->assignGuid(explicitId);
+ addInstance(instance);
+ }
+
void addInstance(Instance* instance);
protected:
virtual void onServiceProvider(const ServiceProvider* oldProvider, const ServiceProvider* newProvider);
private:
void removeInstance(boost::shared_ptr instance);
- public:
- //IdManager(const IdManager&);
- IdManager();
- virtual ~IdManager();
- //IdManager& operator=(const IdManager&);
};
}
diff --git a/Client/Network/Network.vcproj b/Client/Network/Network.vcproj
index f7c93d45..a88904cf 100644
--- a/Client/Network/Network.vcproj
+++ b/Client/Network/Network.vcproj
@@ -222,6 +222,10 @@
RelativePath=".\IdManager.cpp"
>
+
+
@@ -234,6 +238,10 @@
RelativePath=".\Replicator.cpp"
>
+
+
@@ -500,6 +508,10 @@
RelativePath=".\IdManager.h"
>
+
+
@@ -512,6 +524,10 @@
RelativePath=".\Replicator.h"
>
+
+
diff --git a/Client/Network/NetworkSettings.cpp b/Client/Network/NetworkSettings.cpp
new file mode 100644
index 00000000..53d1ec52
--- /dev/null
+++ b/Client/Network/NetworkSettings.cpp
@@ -0,0 +1,37 @@
+#include "NetworkSettings.h"
+
+namespace RBX
+{
+ Reflection::BoundProp prop_LogPackets("LogPackets", "Diagnostics", &NetworkSettings::logPackets, Reflection::PropertyDescriptor::STANDARD);
+ Reflection::BoundProp prop_PrintInstances("PrintInstances", "Diagnostics", &NetworkSettings::printInstances, Reflection::PropertyDescriptor::STANDARD);
+ Reflection::BoundProp prop_PrintProperties("PrintProperties", "Diagnostics", &NetworkSettings::printProperties, Reflection::PropertyDescriptor::STANDARD);
+ Reflection::BoundProp prop_PrintPacketBuffer("PrintPacketBuffer", "Diagnostics", &NetworkSettings::printPacketBuffer, Reflection::PropertyDescriptor::STANDARD);
+ Reflection::BoundProp prop_PrintPhysicsErrors("PrintPhysicsErrors", "Diagnostics", &NetworkSettings::printPhysicsErrors, Reflection::PropertyDescriptor::STANDARD);
+
+ Reflection::BoundProp prop_MaxSendBPS("MaxSendBPS", "Simulator", &NetworkSettings::maxSendBPS, Reflection::PropertyDescriptor::STANDARD);
+ Reflection::BoundProp prop_MinExtraPing("MinExtraPing", "Simulator", &NetworkSettings::minExtraPing, Reflection::PropertyDescriptor::STANDARD);
+ Reflection::BoundProp prop_ExtraPingVariance("ExtraPingVariance", "Simulator", &NetworkSettings::extraPingVariance, Reflection::PropertyDescriptor::STANDARD);
+
+ Reflection::BoundProp prop_MaxDataModelSendBuffer("MaxDataModelSendBuffer", "Replication", &NetworkSettings::maxDataModelSendBuffer, Reflection::PropertyDescriptor::STANDARD);
+ Reflection::BoundProp prop_DataPacketSize("DataPacketSize", "Replication", &NetworkSettings::dataPacketSize, Reflection::PropertyDescriptor::STANDARD);
+ Reflection::BoundProp prop_SendRate("SendRate", "Replication", &NetworkSettings::sendRate, Reflection::PropertyDescriptor::STANDARD);
+
+ NetworkSettings::NetworkSettings()
+ : clientPhysicsSpeed(1.0f),
+ clientPhysicsLifetime(0.5f),
+ dataPacketSize(0.75f),
+ sendRate(40.0f),
+ clientPhysics(false),
+ printPhysicsErrors(false),
+ printInstances(false),
+ printProperties(false),
+ printPacketBuffer(false),
+ logPackets(false),
+ maxDataModelSendBuffer(4),
+ maxSendBPS(0),
+ minExtraPing(0),
+ extraPingVariance(0)
+ {
+ setName("Network");
+ }
+}
diff --git a/Client/Network/NetworkSettings.h b/Client/Network/NetworkSettings.h
new file mode 100644
index 00000000..cc594137
--- /dev/null
+++ b/Client/Network/NetworkSettings.h
@@ -0,0 +1,29 @@
+#pragma once
+#include "v8datamodel/GlobalSettings.h"
+
+namespace RBX
+{
+ extern const char* sNetworkSettings;
+
+ class NetworkSettings : public GlobalSettingsItem
+ {
+ public:
+ bool clientPhysics;
+ float clientPhysicsSpeed;
+ float clientPhysicsLifetime;
+ bool printPhysicsErrors;
+ bool printInstances;
+ bool printProperties;
+ bool printPacketBuffer;
+ bool logPackets;
+ int maxDataModelSendBuffer;
+ float dataPacketSize;
+ float sendRate;
+ int maxSendBPS;
+ int minExtraPing;
+ int extraPingVariance;
+
+ public:
+ NetworkSettings();
+ };
+}
diff --git a/Client/Network/Player.cpp b/Client/Network/Player.cpp
index f49ddcca..d1338dbe 100644
--- a/Client/Network/Player.cpp
+++ b/Client/Network/Player.cpp
@@ -1,16 +1,41 @@
#include "Client.h"
#include "Player.h"
+#include "security/SecurityContext.h"
#include "v8datamodel/TimerService.h"
+RBX::Reflection::PropDescriptor prop_teamColor("TeamColor", "Team", &RBX::Network::Player::getTeamColor, &RBX::Network::Player::setTeamColor, RBX::Reflection::PropertyDescriptor::STANDARD);
+RBX::Reflection::PropDescriptor prop_neutral("Neutral", "Team", &RBX::Network::Player::getNeutral, &RBX::Network::Player::setNeutral, RBX::Reflection::PropertyDescriptor::STANDARD);
+RBX::Reflection::PropDescriptor prop_characterAppearance("CharacterAppearance", "Data", &RBX::Network::Player::getCharacterAppearance, &RBX::Network::Player::setCharacterAppearance, RBX::Reflection::PropertyDescriptor::STANDARD);
+
+RBX::Reflection::RefPropDescriptor prop_Character("Character", "Data", &RBX::Network::Player::getCharacter, &RBX::Network::Player::setCharacter, RBX::Reflection::PropertyDescriptor::STANDARD);
+
+RBX::Reflection::SignalDesc event_Idled("Idled", "time");
+
+static void addChild(const boost::shared_ptr& parent, const boost::shared_ptr& child)
+{
+ child->setParent(parent.get());
+}
+
namespace RBX
{
namespace Network
{
- Reflection::PropDescriptor prop_teamColor("TeamColor", "Team", &Player::getTeamColor, &Player::setTeamColor, Reflection::PropertyDescriptor::STANDARD);
- Reflection::PropDescriptor prop_neutral("Neutral", "Team", &Player::getNeutral, &Player::setNeutral, Reflection::PropertyDescriptor::STANDARD);
- Reflection::PropDescriptor prop_characterAppearance("CharacterAppearance", "Data", &Player::getCharacterAppearance, &Player::setCharacterAppearance, Reflection::PropertyDescriptor::STANDARD);
+ Player::Player()
+ : teamColor(BrickColor::lego_1),
+ neutral(true),
+ under13(false),
+ superSafeChat(false),
+ userId(0),
+ lastActivityTime(0.0)
+ {
+ Security::Context::current().requirePermission(Security::Administrator, "create a Player");
+ setName("Player");
+ }
- Reflection::SignalDesc event_Idled("Idled", "time");
+ Player::~Player()
+ {
+ setCharacter(NULL);
+ }
Backpack* Player::getPlayerBackpack() const
{
@@ -133,5 +158,35 @@ namespace RBX
timerService->delay(boost::bind(&Player::doPeriodicIdleCheck, shared_from(this)), 30.0);
}
}
+
+ void Player::setCharacter(ModelInstance* value)
+ {
+ if (value != character.get())
+ {
+ if (character.get())
+ {
+ characterDiedConnection.disconnect();
+
+ Notifier::raise(character.get());
+
+ if (Players::backendProcessing(this, false))
+ character->setParent(NULL);
+
+ character.reset();
+ }
+
+ if (value)
+ {
+ character = shared_from(value);
+
+ Notifier::raise(character.get());
+
+ if (Players::frontendProcessing(this, false))
+ onCharacterChangedFrontend();
+ }
+
+ raisePropertyChanged(prop_Character);
+ }
+ }
}
}
diff --git a/Client/Network/Players.cpp b/Client/Network/Players.cpp
index d1076bbd..6539339c 100644
--- a/Client/Network/Players.cpp
+++ b/Client/Network/Players.cpp
@@ -10,20 +10,39 @@ class PluginInterfaceAdapter : public PluginInterface
private:
Class* c;
protected:
- PluginInterfaceAdapter(Class*);
+ PluginInterfaceAdapter(Class* c)
+ : PluginInterface(),
+ c(c)
+ {
+ }
public:
virtual PluginReceiveResult OnReceive(RakPeerInterface* peer, Packet* packet);
};
+RBX::Reflection::PropDescriptor propPlayerCount("NumPlayers", "Data", &RBX::Network::Players::numPlayers, NULL, RBX::Reflection::PropertyDescriptor::UI);
+RBX::Reflection::PropDescriptor propPlayerMaxCount("MaxPlayers", "Data", &RBX::Network::Players::getMaxPlayers, &RBX::Network::Players::setMaxPlayers, RBX::Reflection::PropertyDescriptor::STANDARD);
+
namespace RBX
{
namespace Network
{
class Players::Plugin : public PluginInterfaceAdapter
{
- Plugin(Players*);
+ public:
+ Plugin(Players* players)
+ : PluginInterfaceAdapter(players)
+ {
+ }
};
+ Players::~Players()
+ {
+ if (peer)
+ peer->DetachPlugin(plugin.get());
+
+ peer = NULL;
+ }
+
bool Players::clientIsPresent(const Instance* context, bool testInDatamodel)
{
return Client::clientIsPresent(context, testInDatamodel);
@@ -65,6 +84,15 @@ namespace RBX
abuseReporter.reset(new AbuseReporter(value));
}
+ void Players::setMaxPlayers(int value)
+ {
+ if (value != maxPlayers)
+ {
+ maxPlayers = value;
+ raisePropertyChanged(propPlayerMaxCount);
+ }
+ }
+
void Players::reportAbuse(boost::shared_ptr player, std::string comment)
{
reportAbuse(fastDynamicCast(player.get()), comment);
diff --git a/Client/Network/Replicator.cpp b/Client/Network/Replicator.cpp
index 67f0c729..67c867fa 100644
--- a/Client/Network/Replicator.cpp
+++ b/Client/Network/Replicator.cpp
@@ -1,14 +1,111 @@
#include
#include
+#include
#include "Replicator.h"
+#include "NetworkSettings.h"
+#include "v8datamodel/Stats.h"
+#include "v8datamodel/PartInstance.h"
+#include "v8world/SimJobStage.h"
+#include "util/Log.h"
+#include "util/standardout.h"
#include
-void checkDisconnect(RBX::Instance* instance);
+enum ValueType // NOTE: may not be intended for this file
+{
+ ValueType_nil,
+ ValueType_string,
+ ValueType_bool,
+ ValueType_int,
+ ValueType_float,
+ ValueType_BrickColor,
+ ValueType_Color3,
+ ValueType_Vector3,
+ ValueType_BrickVector,
+ ValueType_CoordinateFrame,
+ ValueType_Enum,
+ ValueType_Ref,
+ ValueType_ContentId
+};
+
+class ExtractorHack : public RakPeer
+{
+public:
+ HANDLE getProcessPacketsThreadHandle() // the lengths you have to go through to get a protected member
+ {
+ return processPacketsThreadHandle;
+ }
+};
+
+static void checkDisconnect(RBX::Instance* instance)
+{
+ RBX::Network::Replicator* repl = RBX::Instance::fastDynamicCast(instance);
+
+ if (repl && repl->disconnected)
+ {
+ repl->setParent(NULL);
+ }
+}
+
+void writeValueType(bool includeLength, ValueType vt, RakNet::BitStream& outBitStream)
+{
+ if (includeLength)
+ {
+ unsigned char c = (unsigned char)vt;
+ outBitStream.WriteBits(&c, 4, true);
+ }
+}
+
+FilePacketLogger::FilePacketLogger(RBX::Network::Peer* peer)
+ : packetLogFile(NULL)
+{
+ char filename[256];
+
+ std::string pathName = RBX::Log::current()->logFile;
+ pathName.erase(pathName.rfind('\\') + 1);
+
+ sprintf(filename, "PacketLog%i.csv", RakNet::GetTime());
+ pathName += filename;
+
+ packetLogFile = fopen(pathName.c_str(), "wt");
+
+ if (packetLogFile)
+ {
+ RBX::StandardOut::singleton()->print(RBX::MESSAGE_INFO, "Logging packets to %s", pathName.c_str());
+ }
+ else
+ {
+ RBX::StandardOut::singleton()->print(RBX::MESSAGE_WARNING, "Failed to create log file %s", pathName.c_str());
+ }
+}
+
+void FilePacketLogger::OnAttach(RakPeerInterface* peer)
+{
+ PacketLogger::OnAttach(peer);
+ LogHeader();
+}
+
+void FilePacketLogger::WriteLog(const char* str)
+{
+ if (packetLogFile)
+ fputs(str, packetLogFile);
+}
namespace RBX
{
namespace Network
{
+ Peer::Peer()
+ : profilePacketsThread(new Profiling::ThreadProfiler("Packets Thread")),
+ rakPeer(new RakPeer)
+ {
+ rakPeer->AttachPlugin(this);
+ }
+
+ Peer::~Peer()
+ {
+ rakPeer->DetachPlugin(this);
+ }
+
void Peer::onEvent(const RunService* source, Heartbeat event)
{
double tick = G3D::System::getTick() + event.step / 5.0;
@@ -30,5 +127,740 @@ namespace RBX
{
return rakPeer.get();
}
+
+ void Peer::Update(RakPeerInterface* peer)
+ {
+ RBXASSERT(peer == rakPeer.get());
+
+ profilePacketsThread->sample(static_cast(peer)->getProcessPacketsThreadHandle());
+ PluginInterface::Update(peer); // TODO: blank function shared by multiple symbols. is this the correct one?
+ }
+
+ void Peer::updateLogger()
+ {
+ if (!NetworkSettings::singleton().logPackets)
+ {
+ logger.reset();
+ }
+ else if (!logger)
+ {
+ logger.reset(new FilePacketLogger(this));
+ rakPeer->AttachPlugin(logger.get());
+ }
+ }
+
+ void Peer::updateNetworkSimulator()
+ {
+ rakPeer->ApplyNetworkSimulator(
+ NetworkSettings::singleton().maxSendBPS,
+ NetworkSettings::singleton().minExtraPing,
+ NetworkSettings::singleton().extraPingVariance
+ );
+ }
+
+ Replicator::Replicator(SystemAddress remotePlayerId, RakPeerInterface* peer)
+ : lastSendTime(G3D::System::getLocalTime()),
+ lastCharacterSendTime(G3D::System::getLocalTime()),
+ profileReplication(new Profiling::CodeProfiler("Replication")),
+ profileDataListening(new Profiling::CodeProfiler("Data Listening")),
+ profileDataIn(new Profiling::CodeProfiler("Data In")),
+ profileDataOut(new Profiling::CodeProfiler("Data Out")),
+ profilePhysicsIn(new Profiling::CodeProfiler("Physics In")),
+ profilePhysicsOut(new Profiling::CodeProfiler("Physics Out")),
+ remotePlayerId(remotePlayerId),
+ peer(peer),
+ disconnected(false),
+ receivedGlobals(false),
+ sendPhysicsEnabled(false),
+ players(NULL),
+ deserializeProperty(NULL),
+ removingInstance(NULL)
+ {
+ peer->AttachPlugin(this);
+ setName(remotePlayerId.ToString(true));
+
+ profileDataListening->parent = profileReplication.get();
+ profileDataIn->parent = profileReplication.get();
+ profileDataOut->parent = profileReplication.get();
+ profilePhysicsIn->parent = profileReplication.get();
+ profilePhysicsOut->parent = profileReplication.get();
+ }
+
+ Replicator::~Replicator()
+ {
+ if (peer)
+ peer->DetachPlugin(this);
+ }
+
+ bool Replicator::wantReplicate(const Instance* source) const
+ {
+ return fastDynamicCast(source) == NULL ? true : false;
+ }
+
+ bool Replicator::canSendItems()
+ {
+ return receivedGlobals;
+ }
+
+ void Replicator::closeConnection()
+ {
+ peer->CloseConnection(remotePlayerId, true);
+ setParent(NULL);
+ }
+
+ Player* Replicator::findTargetPlayer()
+ {
+ return players ? players->getLocalPlayer() : NULL;
+ }
+
+ Mechanism* Replicator::findTargetPlayerCharacterMechanism()
+ {
+ Player* player = findTargetPlayer();
+
+ if (player && player->getCharacter())
+ {
+ PartInstance* part = player->getCharacter()->getPrimaryPart();
+
+ if (part)
+ return Mechanism::getMechanismFromPrimitive(part->getPrimitive());
+ }
+
+ return NULL;
+ }
+
+ bool Replicator::isSerializePending(const Instance* instance) const
+ {
+ return pendingNewInstances.find(instance) != pendingNewInstances.end();
+ }
+
+ void Replicator::disconnectAllPropertyChangedConnections()
+ {
+ while (propertyChangedConnections.size() > 0)
+ {
+ propertyChangedConnections.begin()->second.disconnect();
+ propertyChangedConnections.erase(propertyChangedConnections.begin());
+ }
+ }
+
+ void Replicator::requestCharacter()
+ {
+ RakNet::BitStream bitStream;
+
+ bitStream << 'L';
+
+ Player* player = findTargetPlayer();
+ if (!player)
+ throw std::runtime_error("Attempting to send a Character request without a local Player");
+
+ serializeId(bitStream, player);
+
+ if (NetworkSettings::singleton().printInstances)
+ {
+ Guid::Data id;
+ player->getGuid().extract(id);
+
+ StandardOut::singleton()->print(MESSAGE_INFO, "Replicator: Requesting character for %s", id.readableString(4).c_str());
+ }
+
+ peer->Send(&bitStream, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, remotePlayerId, false);
+ }
+
+ void Replicator::disconnectPropertyChanged(boost::shared_ptr instance)
+ {
+ std::map, boost::signals::connection>::iterator iter = propertyChangedConnections.find(instance);
+
+ if (iter != propertyChangedConnections.end())
+ {
+ iter->second.disconnect();
+ propertyChangedConnections.erase(iter);
+ }
+ }
+
+ void Replicator::deleteDisconnectInstances()
+ {
+ std::set>::iterator iter = deleteOnDisconnectInstances.begin();
+ std::set>::iterator end = deleteOnDisconnectInstances.end();
+
+ for (; iter != end; iter++)
+ {
+ boost::shared_ptr instance = iter->lock();
+ if (instance)
+ instance->setParent(NULL);
+ }
+
+ deleteOnDisconnectInstances.clear();
+ }
+
+ bool Replicator::isInReplicationScope(const Instance* instance) const
+ {
+ std::vector::const_iterator begin = replicationContainers.begin();
+ std::vector::const_iterator end = replicationContainers.end();
+
+ if (std::find_if(begin, end, boost::bind(&Instance::isDescendentOf, instance, _1)) != end)
+ {
+ return true;
+ }
+ else
+ {
+ return std::find(begin, end, instance) != end ? true : false;
+ }
+ }
+
+ void Replicator::readMarker(RakNet::BitStream& inBitstream)
+ {
+ intptr_t marker;
+ inBitstream >> marker;
+
+ if (NetworkSettings::singleton().printInstances)
+ {
+ StandardOut::singleton()->print(MESSAGE_INFO, "Received marker %d from %s", (intptr_t)marker, remotePlayerId.ToString());
+ }
+
+ RBXASSERT(reinterpret_cast(marker) == incomingMarkers.front().get());
+
+ Marker::event_Returned.fire(incomingMarkers.front().get());
+ incomingMarkers.pop();
+ }
+
+ boost::shared_ptr Replicator::sendMarker()
+ {
+ boost::shared_ptr marker = Marker::newMarker();
+
+ RakNet::BitStream bitStream;
+ bitStream << 'N';
+ bitStream << (intptr_t)marker.get();
+
+ if (NetworkSettings::singleton().printInstances)
+ {
+ StandardOut::singleton()->print(MESSAGE_INFO, "Replicator: Requesting Marker %d of %s", (intptr_t)marker.get(), remotePlayerId.ToString());
+ }
+
+ incomingMarkers.push(marker);
+ peer->Send(&bitStream, HIGH_PRIORITY, RELIABLE, 0, remotePlayerId, false);
+
+ return marker;
+ }
+
+ bool Replicator::sendPhysicsPacket()
+ {
+ if (!sendPhysicsEnabled)
+ {
+ return false;
+ }
+ else if (!peer->GetStatistics(remotePlayerId)) // TODO: vtable offset does not match due to missing function in RakPeerInterface
+ {
+ return false;
+ }
+ else
+ {
+ Profiling::Mark mark(*profilePhysicsOut, false);
+ JobSender jobSender(*this, peer);
+
+ Workspace* workspace = ServiceProvider::find(this);
+ if (workspace)
+ {
+ SimJobStage& sim = workspace->getWorld()->getSimJobStage();
+ Mechanism* mech = findTargetPlayerCharacterMechanism();
+ if (mech)
+ {
+ G3D::RealTime t = G3D::System::getLocalTime();
+ if (t > lastCharacterSendTime + 0.1)
+ {
+ jobSender.setPacketPriority(MEDIUM_PRIORITY);
+ jobSender.report(*mech);
+ jobSender.setPacketPriority(MEDIUM_PRIORITY);
+
+ lastCharacterSendTime = t;
+ }
+ else
+ {
+ mech = NULL;
+ }
+ }
+
+ sim.reportMechanisms(jobSender, jobStagePos, mech);
+ }
+
+ return jobSender.sentPacket;
+ }
+ }
+
+ bool Replicator::sendItems()
+ {
+ if (!canSendItems())
+ {
+ return false;
+ }
+ else
+ {
+ Profiling::Mark mark(*profileDataOut, false);
+ ItemSender sender(*this, peer);
+
+ while (pendingItems.size() > 0)
+ {
+ boost::shared_ptr- item = *pendingItems.begin();
+
+ if (item)
+ {
+ if (!sender.send(*item))
+ break;
+ }
+
+ pendingItems.pop_front();
+ }
+
+ return sender.sentItems;
+ }
+ }
+
+ const Instance* Replicator::getDefault(const Name& className)
+ {
+ Security::Impersonator impersonate(Security::Replicator);
+
+ std::map>::iterator iter = defaultObjects.find(&className);
+
+ if (iter == defaultObjects.end())
+ {
+ boost::shared_ptr instance = AbstractFactoryProduct::create(className);
+
+ if (!instance)
+ {
+ StandardOut::singleton()->print(MESSAGE_ERROR, "Replication: Can\'t create default object of type %s", className.c_str());
+ }
+
+ defaultObjects[&className] = instance;
+ return instance.get();
+ }
+ else
+ {
+ return iter->second.get();
+ }
+ }
+
+ SharedStringDictionary& Replicator::getSharedDictionary(const Reflection::PropertyDescriptor& descriptor)
+ {
+ std::map>::iterator iter = strings.find(&descriptor);
+
+ if (iter == strings.end())
+ {
+ boost::shared_ptr result(new SharedStringDictionary);
+
+ strings[&descriptor] = result;
+ return *result;
+ }
+ else
+ {
+ return *iter->second;
+ }
+ }
+
+ void Replicator::receiveData(Packet* packet)
+ {
+ Profiling::Mark mark(*profileDataIn, false);
+
+ RakNet::BitStream inBitstream(packet->data, packet->length, false);
+ inBitstream.IgnoreBits(8);
+
+ Security::Impersonator impersonate(Security::Replicator);
+
+ while (true)
+ {
+ Item::ItemType itemType;
+ Item::readItemType(inBitstream, itemType);
+
+ switch (itemType)
+ {
+ case Item::ItemTypeDelete:
+ readInstanceDelete(inBitstream);
+ break;
+ case Item::ItemTypeNew:
+ readInstanceNew(inBitstream);
+ break;
+ case Item::ItemTypeChangeProperty:
+ readChangedProperty(inBitstream);
+ break;
+ case Item::ItemTypeMarker:
+ readMarker(inBitstream);
+ break;
+ case Item::ItemTypeEnd:
+ return;
+ }
+ }
+ }
+
+ //99% match
+ void Replicator::Update(RakPeerInterface* peer)
+ {
+ PluginInterface::Update(peer);
+
+ if (getParent())
+ {
+ Profiling::Mark mark(*profileReplication, false);
+
+ RakNetStatistics* statistics = peer->GetStatistics(remotePlayerId);
+
+ if (statistics)
+ {
+ G3D::RealTime t = G3D::System::getLocalTime();
+
+ if (1.0f / NetworkSettings::singleton().sendRate + lastSendTime < t)
+ {
+ int maxBuffer = NetworkSettings::singleton().maxDataModelSendBuffer;
+ int sendBuffer = statistics->messageSendBuffer[MEDIUM_PRIORITY] + statistics->messageSendBuffer[HIGH_PRIORITY];
+
+ if (maxBuffer - sendBuffer >= 2 && !statistics->bandwidthExceeded)
+ {
+ sendPhysicsPacket();
+ sendItems();
+ lastSendTime = t;
+ }
+ }
+
+ if (NetworkSettings::singleton().printPacketBuffer)
+ {
+ size_t sendBuffer = statistics->messageSendBuffer[MEDIUM_PRIORITY] + statistics->messageSendBuffer[HIGH_PRIORITY] * 2;
+ size_t maxBuffer = NetworkSettings::singleton().maxDataModelSendBuffer;
+
+ if (sendBuffer > maxBuffer)
+ {
+ StandardOut::singleton()->print(MESSAGE_WARNING, "SendBuffer = %d", sendBuffer);
+ }
+ else if (sendBuffer > 0)
+ {
+ StandardOut::singleton()->print(MESSAGE_INFO, "SendBuffer = %d", sendBuffer);
+ }
+ }
+ }
+ }
+ }
+
+ void Replicator::createStatsItems(Stats::StatsService* stats)
+ {
+ if (statsItem)
+ {
+ statsItem->setParent(NULL);
+ statsItem.reset();
+ }
+
+ if (stats)
+ {
+ boost::shared_ptr network = shared_from_polymorphic_downcast(stats->findFirstChildByName("Network"));
+
+ if (network)
+ {
+ statsItem = Creatable::create(shared_from(this), peer->GetStatistics(remotePlayerId));
+ statsItem->setName(getName());
+ statsItem->setParent2(network);
+ }
+ }
+ }
+
+ //95.65% match
+ //functionally accurate, *slightly* different instruction ordering
+ bool Replicator::isChildOfPendingDeleteInstance(const Instance* instance) const
+ {
+ for (const Instance* current = instance->getParent(); current != NULL; current = current->getParent())
+ {
+ if (pendingDeleteInstances.find(current) != pendingDeleteInstances.end())
+ return true;
+ }
+
+ return false;
+ }
+
+ void Replicator::onPropertyChanged(boost::shared_ptr instance, const Reflection::PropertyDescriptor* descriptor)
+ {
+ Profiling::Mark mark(*profileReplication, false);
+ Profiling::Mark mark2(*profileDataListening, false);
+
+ if (instance.get() != removingInstance)
+ {
+ bool isDeserializeProperty = false;
+
+ if (deserializeProperty)
+ {
+ const DescribedBase* inst = static_cast(instance.get()); // force static cast outside of the RBXASSERT block
+ RBXASSERT(descriptor->isMemberOf(inst));
+
+ if (descriptor == &deserializeProperty->getDescriptor() && inst == deserializeProperty->getInstance())
+ isDeserializeProperty = true;
+ }
+
+ if (!isDeserializeProperty && (descriptor->canStreamWrite() || descriptor == &Instance::propParent) && !isSerializePending(instance.get()))
+ {
+ const DescribedBase* inst = static_cast(instance.get());
+ RBXASSERT(descriptor->isMemberOf(inst));
+
+ Player* targetPlayer = findTargetPlayer();
+
+ if (targetPlayer && targetPlayer->getCharacter() && instance->isDescendentOf(targetPlayer->getCharacter()))
+ {
+ pendingItems.push_front(boost::shared_ptr
- (new ChangePropertyItem(*this, instance, *descriptor)));
+ }
+ else
+ {
+ pendingItems.push_back(boost::shared_ptr
- (new ChangePropertyItem(*this, instance, *descriptor)));
+ }
+ }
+ }
+ }
+
+ void Replicator::Item::readItemType(RakNet::BitStream& stream, ItemType& value)
+ {
+ value = ItemTypeEnd;
+
+ stream.ReadBits((unsigned char*)&value, 2, true);
+
+ if (value < 1 || value > 3) // neither of Delete, New, ChangeProperty
+ {
+ stream.Read(value);
+ }
+ }
+
+ void Replicator::Item::writeItemType(RakNet::BitStream& stream, ItemType value)
+ {
+ if (value >= 1 && value <= 3) // Delete, New, ChangeProperty
+ {
+ stream.WriteBits((unsigned char*)&value, 2, true);
+ }
+ else
+ {
+ unsigned char zeroes = 0;
+ stream.WriteBits(&zeroes, 2, true);
+ stream.Write(value);
+ }
+ }
+
+ Replicator::ItemSender::ItemSender(Replicator& replicator, RakPeerInterface* peer)
+ : replicator(replicator),
+ peer(peer),
+ maxStreamSize(G3D::iRound(NetworkSettings::singleton().dataPacketSize * (peer->GetMTUSize(replicator.remotePlayerId) - 128))),
+ sentItems(false)
+ {
+ }
+
+ Replicator::ItemSender::~ItemSender()
+ {
+ if (bitStream.GetNumberOfBitsUsed() > 0)
+ {
+ Item::writeItemType(bitStream, Item::ItemTypeEnd);
+ peer->Send(&bitStream, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, replicator.remotePlayerId, false);
+ bitStream.Reset();
+ }
+ }
+
+ bool Replicator::ItemSender::send(Item& item)
+ {
+ if (bitStream.GetNumberOfBytesUsed() < maxStreamSize)
+ {
+ if (bitStream.GetNumberOfBitsUsed() == 0)
+ {
+ bitStream << 'M';
+ }
+
+ item.write(bitStream);
+ sentItems = true;
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ Replicator::JobSender::JobSender(Replicator& replicator, RakPeerInterface* peer)
+ : replicator(replicator),
+ peer(peer),
+ firstA(NULL),
+ firstM(NULL),
+ packetPriority(MEDIUM_PRIORITY),
+ sentPacket(false),
+ hasOpenPacket(false),
+ bitStream(1364) // what
+ {
+ }
+
+ Replicator::JobSender::~JobSender()
+ {
+ if (hasOpenPacket)
+ {
+ closePacket();
+ }
+ }
+
+ void Replicator::JobSender::setPacketPriority(PacketPriority packetPriority)
+ {
+ if (this->packetPriority != packetPriority)
+ {
+ closePacket();
+ this->packetPriority = packetPriority;
+ }
+ }
+
+ void Replicator::JobSender::closePacket()
+ {
+ replicator.serializeId(bitStream, NULL);
+ peer->Send(&bitStream, packetPriority, UNRELIABLE, 0, replicator.remotePlayerId, false);
+ hasOpenPacket = false;
+ }
+
+ bool Replicator::JobSender::report(const Mechanism& m)
+ {
+ if (!hasOpenPacket)
+ {
+ bitStream.Reset();
+
+ bitStream << (char)0x18;
+ bitStream << RakNet::GetTime();
+ bitStream << 'O';
+
+ hasOpenPacket = true;
+ sentPacket = true;
+ }
+
+ replicator.sendPhysicsData(bitStream, m);
+ replicator.serializeId(bitStream, NULL);
+
+ return bitStream.GetNumberOfBytesUsed() < 1364u;
+ }
+
+ void Replicator::MarkerItem::write(RakNet::BitStream& bitStream)
+ {
+ writeItemType(bitStream, ItemTypeMarker);
+ bitStream << (int)id;
+
+ if (NetworkSettings::singleton().printInstances)
+ {
+ StandardOut::singleton()->print(MESSAGE_INFO, "Replication: Sending marker %d to %s", id, replicator.remotePlayerId.ToString());
+ }
+
+ replicator.onSentMarker(id);
+ }
+
+ Replicator::ReplicatorStatsItem::ReplicatorStatsItem(const boost::shared_ptr& replicator, const RakNetStatistics* statistics)
+ : replicator(replicator),
+ statistics(statistics),
+ instanceCount(0),
+ instanceBits(0)
+ {
+ bps = createChildItem("Bytes/s");
+ bps->createBoundChildItem("Bandwidth Exceeded", statistics->bandwidthExceeded);
+
+ packetLoss = bps->createChildItem("Packet Loss");
+ ping = createChildItem("Ping");
+
+ Item* rep = createBoundChildItem(*replicator->profileReplication);
+
+ rep->createBoundChildItem(*replicator->profileDataListening);
+ rep->createBoundChildItem(*replicator->profileDataOut);
+ rep->createBoundChildItem(*replicator->profilePhysicsOut);
+ rep->createBoundChildItem(*replicator->profileDataIn);
+ rep->createBoundChildItem(*replicator->profilePhysicsIn);
+
+ instanceSize = rep->createChildItem("Instance size (In)");
+ waitingRefs = createChildItem("Waiting Refs");
+ }
+
+ void Replicator::ReplicatorStatsItem::update()
+ {
+ if (replicator->peer)
+ {
+ bps->formatMem((size_t)statistics->bitsPerSecond / 8);
+
+ ping->formatValue(
+ replicator->peer->GetLastPing(replicator->remotePlayerId),
+ "%d avg:%d best:%d",
+ replicator->peer->GetLastPing(replicator->remotePlayerId),
+ replicator->peer->GetAveragePing(replicator->remotePlayerId),
+ replicator->peer->GetLowestPing(replicator->remotePlayerId)
+ );
+
+ waitingRefs->formatValue(replicator->numWaitingRefs());
+
+ float ratio = (float)statistics->messagesTotalBitsResent / statistics->totalBitsSent;
+ packetLoss->formatValue(ratio, "%.1g%%", ratio * 100.0);
+
+ instanceSize->formatMem(instanceCount != 0 ? instanceBits / (instanceCount * 8) : 0);
+ }
+ }
+
+ Replicator::ChangePropertyItem::ChangePropertyItem(Replicator& replicator, const boost::shared_ptr& instance, const Reflection::PropertyDescriptor& desc)
+ : Item(replicator),
+ instance(instance),
+ desc(desc)
+ {
+ }
+
+ void Replicator::ChangePropertyItem::write(RakNet::BitStream& bitStream)
+ {
+ if (replicator.isInReplicationScope(instance.get()))
+ {
+ writeItemType(bitStream, ItemTypeChangeProperty);
+
+ const Name& descName = desc.name;
+
+ replicator.serializeId(bitStream, instance.get());
+
+ SharedStringDictionary& r = replicator.propNames;
+
+ r.send(bitStream, descName.toString());
+
+ if (NetworkSettings::singleton().printProperties)
+ {
+ StandardOut::singleton()->print(
+ MESSAGE_INFO,
+ "Replication: %s << %s:%s.%s",
+ replicator.remotePlayerId.ToString(true),
+ instance->getClassName().c_str(),
+ instance->getGuid().readableString(4).c_str(),
+ descName.c_str()
+ );
+ }
+
+ replicator.serializeValue(Reflection::ConstProperty(desc, instance.get()), true, bitStream);
+ }
+ }
+
+ void Replicator::DeleteInstanceItem::write(RakNet::BitStream& bitStream)
+ {
+ size_t erased = replicator.pendingDeleteInstances.erase(instance.get());
+
+ if (erased)
+ {
+ if (!instance.get())
+ {
+ StandardOut::singleton()->print(MESSAGE_ERROR, "Replication: %s << ~NULL", replicator.remotePlayerId.ToString(true));
+ return;
+ }
+
+ try
+ {
+ writeItemType(bitStream, ItemTypeDelete);
+ replicator.serializeId(bitStream, instance.get());
+
+ if (NetworkSettings::singleton().printInstances)
+ {
+ StandardOut::singleton()->print(
+ MESSAGE_INFO,
+ "Replication: %s << ~%s:%s",
+ replicator.remotePlayerId.ToString(true),
+ instance->getClassName().c_str(),
+ instance->getGuid().readableString(4).c_str()
+ );
+ }
+ }
+ catch (std::runtime_error& e)
+ {
+ StandardOut::singleton()->print(
+ MESSAGE_ERROR,
+ "Replication: %s << ~%s, %s",
+ replicator.remotePlayerId.ToString(true),
+ instance->getClassName().c_str(),
+ e.what()
+ );
+ }
+ }
+ }
}
}
diff --git a/Client/Network/Replicator.h b/Client/Network/Replicator.h
index 9f446d35..b986b6d9 100644
--- a/Client/Network/Replicator.h
+++ b/Client/Network/Replicator.h
@@ -1,8 +1,14 @@
#pragma once
#include
-#include "util/RunStateOwner.h"
-#include "util/Events.h"
+#include
+#include
+#include
+#include "Streaming.h"
+#include "Network/Players.h"
+#include "v8world/Assembly.h"
+#include "v8world/Mechanism.h"
#include "util/Profiling.h"
+#include "util/Events.h"
#include
class RakPeer;
@@ -12,8 +18,281 @@ class PacketLogger;
namespace RBX
{
+ namespace Stats
+ {
+ class Item;
+ class StatsService;
+ }
+
namespace Network
{
+ extern const char* sMarker;
+
+ class Marker : public DescribedNonCreatable, private boost::noncopyable
+ {
+ private:
+ bool returned;
+ public:
+ static Reflection::SignalDesc event_Returned;
+ static Reflection::SignalDesc event_Progress;
+
+ private:
+ Marker()
+ : returned(false)
+ {
+ }
+ public:
+ long id() const;
+ void fireReturned();
+
+ public:
+ static boost::shared_ptr newMarker()
+ {
+ return boost::shared_ptr(new Marker);
+ }
+ static Marker* fromId(long);
+ };
+
+ extern const char* sReplicator;
+
+ class Replicator : public DescribedNonCreatable, public PluginInterface
+ {
+ class Item : private boost::noncopyable
+ {
+ public:
+ enum ItemType
+ {
+ ItemTypeEnd,
+ ItemTypeDelete,
+ ItemTypeNew,
+ ItemTypeChangeProperty,
+ ItemTypeMarker
+ };
+
+ protected:
+ Replicator& replicator;
+
+ protected:
+ Item(Replicator& replicator)
+ : replicator(replicator)
+ {
+ }
+ public:
+ virtual ~Item();
+ virtual void write(RakNet::BitStream& bitStream) = 0;
+
+ public:
+ static void writeItemType(RakNet::BitStream& stream, ItemType value);
+ static void readItemType(RakNet::BitStream& stream, ItemType& value);
+ };
+
+ class ItemSender : private boost::noncopyable
+ {
+ private:
+ Replicator& replicator;
+ RakPeerInterface* peer;
+ RakNet::BitStream bitStream;
+ const int maxStreamSize;
+ public:
+ bool sentItems;
+
+ private:
+ void openPacket();
+ void closePacket();
+ public:
+ ItemSender(Replicator& replicator, RakPeerInterface* peer);
+ ~ItemSender();
+ bool send(Item& item);
+ };
+
+ class JobSender : public boost::noncopyable
+ {
+ private:
+ const Assembly* firstA;
+ const Mechanism* firstM;
+ Replicator& replicator;
+ RakPeerInterface* peer;
+ bool hasOpenPacket;
+ RakNet::BitStream bitStream;
+ PacketPriority packetPriority;
+ public:
+ bool sentPacket;
+ private:
+ static const size_t maxStreamSize;
+
+ private:
+ void openPacket();
+ void closePacket();
+ public:
+ JobSender(Replicator& replicator, RakPeerInterface* peer);
+ void close();
+ ~JobSender();
+ void setPacketPriority(PacketPriority packetPriority);
+ bool report(const Mechanism& m);
+ bool operator()(Mechanism&);
+ };
+
+ class MarkerItem : public Item
+ {
+ private:
+ int id;
+
+ public:
+ MarkerItem(Replicator&, int);
+ virtual void write(RakNet::BitStream& bitStream);
+ };
+
+ class NewInstanceItem : public Item
+ {
+ private:
+ boost::shared_ptr instance;
+
+ public:
+ NewInstanceItem(Replicator&, const boost::shared_ptr&);
+ virtual void write(RakNet::BitStream&);
+ };
+
+ class ReplicatorStatsItem : public Stats::Item
+ {
+ private:
+ Stats::Item* waitingRefs;
+ Stats::Item* packetLoss;
+ Stats::Item* ping;
+ Stats::Item* instanceSize;
+ Stats::Item* bps;
+ const boost::shared_ptr replicator;
+ const RakNetStatistics* statistics;
+ public:
+ size_t instanceCount;
+ size_t instanceBits;
+
+ public:
+ ReplicatorStatsItem(const boost::shared_ptr& replicator, const RakNetStatistics* statistics);
+ virtual void update();
+ };
+
+ class ChangePropertyItem : public Item
+ {
+ private:
+ const Reflection::PropertyDescriptor& desc;
+ const boost::shared_ptr instance;
+
+ public:
+ ChangePropertyItem(Replicator& replicator, const boost::shared_ptr& instance, const Reflection::PropertyDescriptor& desc);
+ virtual void write(RakNet::BitStream& bitStream);
+ };
+
+ class DeleteInstanceItem : public Item
+ {
+ private:
+ boost::shared_ptr instance;
+
+ public:
+ DeleteInstanceItem(Replicator& replicator, const boost::shared_ptr& instance);
+ virtual void write(RakNet::BitStream& bitStream);
+ };
+
+ private:
+ G3D::RealTime lastSendTime;
+ MechanismTracker jobStagePos;
+ G3D::RealTime lastCharacterSendTime;
+ boost::shared_ptr statsItem;
+ std::map> strings;
+ SharedStringDictionary classNames;
+ SharedStringDictionary propNames;
+ protected:
+ std::vector replicationContainers;
+ std::vector replicationContainerConnections;
+ Players* players;
+ std::list> pendingItems;
+ std::set pendingNewInstances;
+ std::set pendingDeleteInstances;
+ bool sendPhysicsEnabled;
+ const Reflection::Property* deserializeProperty;
+ Instance* removingInstance;
+ std::set> deleteOnDisconnectInstances;
+ std::map, boost::signals::connection> propertyChangedConnections;
+ std::queue> incomingMarkers;
+ std::map> defaultObjects;
+ boost::scoped_ptr profileReplication;
+ boost::scoped_ptr profileDataListening;
+ boost::scoped_ptr profileDataIn;
+ boost::scoped_ptr profileDataOut;
+ boost::scoped_ptr profilePhysicsIn;
+ boost::scoped_ptr profilePhysicsOut;
+ private:
+ bool receivedGlobals;
+ public:
+ bool disconnected;
+ RakPeerInterface* peer;
+ const SystemAddress remotePlayerId;
+
+ public:
+ static Reflection::BoundProp prop_maxDataModelSendBuffer;
+ static Reflection::SignalDesc event_Disconnection;
+
+ protected:
+ virtual Player* findTargetPlayer();
+ Mechanism* findTargetPlayerCharacterMechanism();
+ public:
+ Replicator(SystemAddress remotePlayerId, RakPeerInterface* peer);
+ virtual ~Replicator();
+ boost::shared_ptr getPlayer()
+ {
+ return shared_from(findTargetPlayer());
+ }
+ virtual void Update(RakPeerInterface* peer);
+ virtual PluginReceiveResult OnReceive(RakPeerInterface*, Packet*);
+ virtual const Name& getClassName() const
+ {
+ return Name::getNullName();
+ }
+ virtual XmlElement* write();
+ boost::shared_ptr sendMarker();
+ bool isSerializePending(const Instance* instance) const;
+ bool isPropertyChangedPending(const Reflection::Property&) const;
+ void requestCharacter();
+ void closeConnection();
+ protected:
+ virtual void onServiceProvider(const ServiceProvider*, const ServiceProvider*);
+ void onDescendentAdded(boost::shared_ptr);
+ void onDescendentRemoving(boost::shared_ptr);
+ void onPropertyChanged(boost::shared_ptr instance, const Reflection::PropertyDescriptor* descriptor);
+ const Instance* getDefault(const Name& className);
+ void deleteDisconnectInstances();
+ void connectPropertyChanged(boost::shared_ptr instance);
+ void disconnectPropertyChanged(boost::shared_ptr instance);
+ virtual bool wantReplicate(const Instance* source) const;
+ virtual void onSentMarker(int);
+ private:
+ void disconnectAllPropertyChangedConnections();
+ void connectToReplicationContainers();
+ void removeFromPendingProperties(const Instance*);
+ bool removeFromPendingNewInstances(const Instance*);
+ bool removeFromPendingDeleteInstances(const Instance*);
+ bool isChildOfPendingDeleteInstance(const Instance* instance) const;
+ bool isInReplicationScope(const Instance* instance) const;
+ bool sendItems();
+ bool sendPhysicsPacket();
+ void sendPhysicsData(RakNet::BitStream&, const Mechanism&);
+ void sendPhysicsData(RakNet::BitStream&, const Assembly&);
+ void createStatsItems(Stats::StatsService* stats);
+ virtual bool remoteDeleteOnDisconnect(const Instance* instance) const;
+ void serializeValue(const Reflection::ConstProperty&, bool, RakNet::BitStream&);
+ void deserializeValue(RakNet::BitStream&, bool, Reflection::Property&);
+ void skipValue(RakNet::BitStream&);
+ void receiveData(Packet* packet);
+ void readInstanceNew(RakNet::BitStream&);
+ void readInstanceDelete(RakNet::BitStream&);
+ void readChangedProperty(RakNet::BitStream&);
+ void readMarker(RakNet::BitStream& inBitstream);
+ bool getCameFromRemotePlayer(const Instance*) const;
+ void setCameFromRemotePlayer(Instance*);
+ SharedStringDictionary& getSharedDictionary(const Reflection::PropertyDescriptor& descriptor);
+ PluginReceiveResult OnReceive_ID_TIMESTAMP(Packet*);
+ virtual bool canSendItems();
+ };
+
extern const char* sPeer;
class Peer : public Reflection::Described, public PluginInterface, public Listener
@@ -23,20 +302,32 @@ namespace RBX
boost::scoped_ptr profilePacketsThread;
protected:
const boost::scoped_ptr rakPeer;
- public:
- //Peer(const Peer&);
+
protected:
Peer();
virtual ~Peer();
- virtual bool askAddChild(const Instance*) const;
+ virtual bool askAddChild(const Instance* instance) const
+ {
+ return fastDynamicCast(instance) != NULL;
+ }
RakPeerInterface* peerInterface();
void updateLogger();
void updateNetworkSimulator();
virtual void onEvent(const RunService* source, Heartbeat event);
- virtual void Update(RakPeerInterface*);
+ virtual void Update(RakPeerInterface* peer);
virtual void onServiceProvider(const ServiceProvider* oldProvider, const ServiceProvider* newProvider);
- public:
- //Peer& operator=(const Peer&);
};
}
}
+
+class FilePacketLogger : public PacketLogger
+{
+private:
+ FILE* packetLogFile;
+
+public:
+ FilePacketLogger(RBX::Network::Peer*);
+ virtual ~FilePacketLogger();
+ virtual void OnAttach(RakPeerInterface* peer);
+ virtual void WriteLog(const char*);
+};
diff --git a/Client/Network/Server.cpp b/Client/Network/Server.cpp
new file mode 100644
index 00000000..91af2cab
--- /dev/null
+++ b/Client/Network/Server.cpp
@@ -0,0 +1,130 @@
+#include
+#include "Server.h"
+#include "IdManager.h"
+#include "API.h"
+#include "util/standardout.h"
+
+static bool isReplicator(boost::shared_ptr instance)
+{
+ return RBX::Instance::fastDynamicCast(instance.get()) != NULL;
+}
+
+namespace RBX
+{
+ namespace Network
+ {
+ Server::Server()
+ : outgoingPort(0)
+ {
+ setName("NetworkServer");
+ rakPeer->SetMaximumIncomingConnections(32);
+ rakPeer->SetIncomingPassword(API::version.c_str(), (int)API::version.size());
+ updateLogger();
+ }
+
+ Server::~Server() {}
+
+ void Server::stop(int blockDuration)
+ {
+ if (rakPeer->IsActive())
+ rakPeer->Shutdown(blockDuration);
+
+ removeAllChildren();
+ }
+
+ void Server::start(int port, int threadSleepTime)
+ {
+ SocketDescriptor d(port, "");
+
+ if (!rakPeer->Startup(32, threadSleepTime, &d, 1))
+ throw std::runtime_error("Failed to start network server");
+
+ outgoingPort = port;
+ StandardOut::singleton()->print(MESSAGE_INFO, "Starting network server on port %d", port);
+
+ unsigned numAddresses = rakPeer->GetNumberOfAddresses();
+
+ StandardOut::singleton()->print(MESSAGE_INFO, "IP addresses:");
+
+ for (unsigned i = 0; i < numAddresses; i++)
+ {
+ StandardOut::singleton()->print(MESSAGE_INFO, "%s", rakPeer->GetLocalIP(i));
+ }
+
+ updateNetworkSimulator();
+ }
+
+ int Server::getClientCount()
+ {
+ if (&(*getChildren()))
+ {
+ return std::count_if(getChildren()->begin(), getChildren()->end(), &isReplicator);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ void Server::onServiceProvider(const ServiceProvider* oldProvider, const ServiceProvider* newProvider)
+ {
+ if (oldProvider)
+ {
+ players->setConnection(NULL);
+
+ if (rakPeer->IsActive())
+ rakPeer->Shutdown(1000);
+
+ removeAllChildren();
+
+ players.reset();
+ }
+
+ Peer::onServiceProvider(oldProvider, newProvider);
+
+ if (newProvider)
+ {
+ players = shared_from(newProvider->create());
+
+ players->setConnection(peerInterface());
+ }
+ }
+
+ void Server::setServerManagerPing(std::string pingUrl, std::string publicIP, int thumbnailId)
+ {
+ pingThread.reset(new worker_thread(boost::bind(&Server::ping, boost::weak_ptr(shared_from(this)), publicIP, thumbnailId, pingUrl), "rbx_serverping"));
+ }
+
+ Server::ClientProxy::ClientProxy(SystemAddress systemAddress, Server* server)
+ : Replicator(systemAddress, server->peerInterface()),
+ server(server)
+ {
+ }
+
+ Server::ClientProxy::~ClientProxy() {}
+
+ void Server::ClientProxy::onSentMarker(long id)
+ {
+ sendPhysicsEnabled = true;
+ }
+
+ void Server::ClientProxy::sendTop()
+ {
+ RakNet::BitStream bitStream;
+
+ bitStream << 'K';
+
+ std::vector::iterator end = replicationContainers.end();
+
+ for (std::vector::iterator iter = replicationContainers.begin(); iter != end; iter++)
+ {
+ RBXASSERT(*iter != NULL);
+
+ serializeId(bitStream, *iter);
+ ServiceProvider::create(this)->addInstance(*iter);
+ }
+
+ peer->Send(&bitStream, MEDIUM_PRIORITY, RELIABLE_ORDERED, 0, remotePlayerId, false);
+ }
+ }
+}
diff --git a/Client/Network/Server.h b/Client/Network/Server.h
new file mode 100644
index 00000000..d71e63bb
--- /dev/null
+++ b/Client/Network/Server.h
@@ -0,0 +1,75 @@
+#pragma once
+#include "Network/Players.h"
+#include "Replicator.h"
+
+namespace RBX
+{
+ namespace Network
+ {
+ extern const char* sServer;
+
+ class Server : public DescribedCreatable
+ {
+ class ClientProxy : public Replicator
+ {
+ private:
+ boost::shared_ptr remotePlayer;
+ Server* server;
+
+ public:
+ ClientProxy(SystemAddress systemAddress, Server* server);
+ virtual ~ClientProxy();
+ void sendTop();
+ virtual PluginReceiveResult OnReceive(RakPeerInterface*, Packet*);
+ protected:
+ virtual Player* findTargetPlayer()
+ {
+ return remotePlayer.get();
+ }
+
+ virtual void onSentMarker(long id);
+ private:
+ virtual bool remoteDeleteOnDisconnect(const Instance* instance) const
+ {
+ std::vector::const_iterator begin = replicationContainers.begin();
+ std::vector::const_iterator end = replicationContainers.end();
+
+ return std::find(begin, end, instance) == end;
+ }
+
+ virtual bool canSendItems();
+ };
+
+ private:
+ int outgoingPort;
+ boost::scoped_ptr pingThread;
+ boost::shared_ptr players;
+
+ static int lastId;
+ public:
+ static Reflection::SignalDesc)> event_IncommingConnection;
+
+ public:
+ Server();
+ virtual ~Server();
+
+ void start(int port, int threadSleepTime);
+ void stop(int blockDuration);
+ int getClientCount();
+ virtual XmlElement* write();
+ virtual PluginReceiveResult OnReceive(RakPeerInterface*, Packet*);
+ void setServerManagerPing(std::string pingUrl, std::string publicIP, int thumbnailId);
+ protected:
+ virtual void onServiceProvider(const ServiceProvider* oldProvider, const ServiceProvider* newProvider);
+ virtual bool askAddChild(const Instance* instance) const
+ {
+ return fastDynamicCast(instance) != NULL;
+ }
+
+ private:
+ static worker_thread::work_result ping(boost::weak_ptr, std::string, int, std::string);
+ public:
+ static bool serverIsPresent(const Instance* context, bool testInDatamodel);
+ };
+ }
+}
diff --git a/Client/Network/Streaming.cpp b/Client/Network/Streaming.cpp
index a246db7b..567b572f 100644
--- a/Client/Network/Streaming.cpp
+++ b/Client/Network/Streaming.cpp
@@ -19,6 +19,12 @@ namespace RBX
return stream;
}
+ RakNet::BitStream& operator<<(RakNet::BitStream& stream, long value)
+ {
+ stream.Write(value);
+ return stream;
+ }
+
RakNet::BitStream& operator<<(RakNet::BitStream& stream, unsigned int value)
{
stream.Write(value);
@@ -72,6 +78,12 @@ namespace RBX
return stream;
}
+ RakNet::BitStream& operator<<(RakNet::BitStream& stream, long& value)
+ {
+ stream.Write(value);
+ return stream;
+ }
+
RakNet::BitStream& operator>>(RakNet::BitStream& stream, unsigned int& value)
{
stream.Read(value);
diff --git a/Client/Network/Streaming.h b/Client/Network/Streaming.h
index 7ebb9839..b51bc992 100644
--- a/Client/Network/Streaming.h
+++ b/Client/Network/Streaming.h
@@ -1,14 +1,8 @@
+#pragma once
#include "v8datamodel/BrickColor.h"
#include "v8tree/Instance.h"
-#include "util/ContentProvider.h"
-#include "util/Name.h"
-#include "util/Guid.h"
-#include "reflection/property.h"
#include
-#include
#include
-#include