diff --git a/Client/App/App.vcproj b/Client/App/App.vcproj index 69a3788c..fbd4ecbb 100644 --- a/Client/App/App.vcproj +++ b/Client/App/App.vcproj @@ -873,6 +873,10 @@ RelativePath=".\include\v8datamodel\Teams.h" > + + @@ -942,6 +946,14 @@ > + + + + + + + + diff --git a/Client/App/include/gui/Gui.h b/Client/App/include/gui/Gui.h index a435beba..f1085b38 100644 --- a/Client/App/include/gui/Gui.h +++ b/Client/App/include/gui/Gui.h @@ -1,4 +1,5 @@ #pragma once +#include "RbxGraphics/Adorn.h" #include "v8tree/Instance.h" #include "gui/GuiEvent.h" #include "gui/Layout.h" diff --git a/Client/App/include/reflection/property.h b/Client/App/include/reflection/property.h index 2660483f..43506b42 100644 --- a/Client/App/include/reflection/property.h +++ b/Client/App/include/reflection/property.h @@ -138,8 +138,14 @@ namespace RBX TypedPropertyDescriptor(ClassDescriptor&, const Type&, const char*, const char*, std::auto_ptr, Functionality); public: virtual bool isReadOnly() const; - PropType getValue(const DescribedBase*) const; - void setValue(DescribedBase*, const PropType&) const; + PropType getValue(const DescribedBase* object) const + { + return getset->getValue(object); + } + void setValue(DescribedBase* object, const PropType& value) const + { + getset->setValue(object, value); + } virtual bool equalValues(const DescribedBase*, const DescribedBase*) const; virtual bool hasStringValue() const; virtual std::string getStringValue(const DescribedBase*) const; diff --git a/Client/App/include/security/SecurityContext.h b/Client/App/include/security/SecurityContext.h new file mode 100644 index 00000000..0152e6ab --- /dev/null +++ b/Client/App/include/security/SecurityContext.h @@ -0,0 +1,89 @@ +#include +#include + +namespace RBX +{ + namespace Security + { + enum Identities + { + Anonymous, + LocalGUI, + GameScript, + CmdLine, + TrustedCOM, + TrustedWebService, + Replicator + }; + + enum Permissions + { + None, + Administrator + }; + + class Context + { + friend class Impersonator; + + private: + const Identities identity; + public: + void requirePermission(Permissions permission, const char* operation) const + { + if (!isInRole(identity, permission)) + { + if (operation) + throw std::runtime_error(G3D::format("The current security context cannot %s", operation)); + else + throw std::runtime_error(G3D::format("The current security context cannot perform the requested operation")); + } + } + + bool hasPermission(Permissions); + private: + Context(Identities identity) + : identity(identity) + { + } + + public: + static Context& current() + { + Context* context = ptr().get(); + if (!context) + { + context = new Context(Anonymous); + ptr().reset(context); + } + + return *context; + } + static __declspec(noinline) bool isInRole(Identities identity, Permissions permission); + private: + static boost::thread_specific_ptr& ptr() + { + static boost::thread_specific_ptr value; + return value; + } + }; + + class Impersonator + { + private: + Context* previous; + public: + Impersonator(Identities identity) + { + Context* newContext = new Context(identity); + previous = Context::ptr().release(); + Context::ptr().reset(newContext); + } + + ~Impersonator() + { + Context::ptr().reset(previous); + } + }; + } +} diff --git a/Client/App/include/v8datamodel/ICameraOwner.h b/Client/App/include/v8datamodel/ICameraOwner.h index bb5e36a9..7aadd7c3 100644 --- a/Client/App/include/v8datamodel/ICameraOwner.h +++ b/Client/App/include/v8datamodel/ICameraOwner.h @@ -1,13 +1,13 @@ #pragma once #include #include +#include "v8datamodel/Camera.h" #include "util/Extents.h" namespace RBX { class Primitive; class PartInstance; - class Camera; class __declspec(novtable) ICameraOwner { private: diff --git a/Client/App/include/v8datamodel/TimerService.h b/Client/App/include/v8datamodel/TimerService.h new file mode 100644 index 00000000..3c2689b4 --- /dev/null +++ b/Client/App/include/v8datamodel/TimerService.h @@ -0,0 +1,26 @@ +#include "util/RunStateOwner.h" + +namespace RBX +{ + extern const char* sTimerService; + + class TimerService : public DescribedCreatable, public Service, public Listener + { + class Item + { + public: + G3D::RealTime time; + boost::function0 func; + }; + + private: + boost::shared_ptr runService; + std::list items; + public: + TimerService(); + void delay(boost::function0, double); + protected: + virtual void onServiceProvider(const ServiceProvider*, const ServiceProvider*); + virtual void onEvent(const RunService*, Heartbeat); + }; +} diff --git a/Client/App/security/SecurityContext.cpp b/Client/App/security/SecurityContext.cpp new file mode 100644 index 00000000..1f94dff8 --- /dev/null +++ b/Client/App/security/SecurityContext.cpp @@ -0,0 +1,27 @@ +#include "security/SecurityContext.h" + +namespace RBX +{ + namespace Security + { + bool Context::isInRole(Identities identity, Permissions permission) + { + switch (identity) + { + case Anonymous: + case GameScript: + return permission == None; + + case LocalGUI: + case CmdLine: + case TrustedCOM: + case TrustedWebService: + case Replicator: + return true; + + default: + return false; + } + } + } +} diff --git a/Client/App/v8datamodel/PartInstance.cpp b/Client/App/v8datamodel/PartInstance.cpp index a6f2bb55..2a47c4e9 100644 --- a/Client/App/v8datamodel/PartInstance.cpp +++ b/Client/App/v8datamodel/PartInstance.cpp @@ -4,15 +4,13 @@ namespace RBX { - const char* const category_Part = "Part"; - - static const Reflection::PropDescriptor prop_PositionUi("Position", "Data", &PartInstance::getTranslationUi, NULL, Reflection::PropertyDescriptor::LEGACY); - static const Reflection::PropDescriptor prop_Velocity("Velocity", "Data", &PartInstance::getLinearVelocity, NULL, Reflection::PropertyDescriptor::LEGACY); - static const Reflection::PropDescriptor prop_SizeUi("Size", category_Part, &PartInstance::getPartSizeUi, NULL, Reflection::PropertyDescriptor::LEGACY); - static const Reflection::PropDescriptor prop_Dragging("DraggingV1", "Behavior", &PartInstance::getDragging, NULL, Reflection::PropertyDescriptor::LEGACY); - static const Reflection::PropDescriptor prop_formFactor("FormFactor", category_Part, &PartInstance::getFormFactor, NULL, Reflection::PropertyDescriptor::LEGACY); - static const Reflection::PropDescriptor prop_Friction("Friction", category_Part, &PartInstance::getFriction, NULL, Reflection::PropertyDescriptor::LEGACY); - static const Reflection::PropDescriptor prop_Elasticity("Elasticity", category_Part, &PartInstance::getElasticity, NULL, Reflection::PropertyDescriptor::LEGACY); + static const Reflection::PropDescriptor prop_PositionUi("Position", "Data", &PartInstance::getTranslationUi, &PartInstance::setTranslationUi, Reflection::PropertyDescriptor::UI); + static const Reflection::PropDescriptor prop_Velocity("Velocity", "Data", &PartInstance::getLinearVelocity, &PartInstance::setLinearVelocity, Reflection::PropertyDescriptor::STANDARD); + static const Reflection::PropDescriptor prop_SizeUi("Size", category_Part, &PartInstance::getPartSizeUi, &PartInstance::setPartSizeUi, Reflection::PropertyDescriptor::UI); + static const Reflection::PropDescriptor prop_Dragging("DraggingV1", "Behavior", &PartInstance::getDragging, &PartInstance::setDragging, Reflection::PropertyDescriptor::STREAMING); + static const Reflection::PropDescriptor prop_formFactor("FormFactor", category_Part, &PartInstance::getFormFactor, &PartInstance::setFormFactorXml, Reflection::PropertyDescriptor::STREAMING); + static const Reflection::PropDescriptor prop_Friction("Friction", category_Part, &PartInstance::getFriction, &PartInstance::setFriction, Reflection::PropertyDescriptor::STANDARD); + static const Reflection::PropDescriptor prop_Elasticity("Elasticity", category_Part, &PartInstance::getElasticity, &PartInstance::setElasticity, Reflection::PropertyDescriptor::STANDARD); namespace Reflection { diff --git a/Client/Network/Network.vcproj b/Client/Network/Network.vcproj index 1482bfb9..f7c93d45 100644 --- a/Client/Network/Network.vcproj +++ b/Client/Network/Network.vcproj @@ -222,6 +222,14 @@ RelativePath=".\IdManager.cpp" > + + + + diff --git a/Client/Network/Player.cpp b/Client/Network/Player.cpp new file mode 100644 index 00000000..9952436c --- /dev/null +++ b/Client/Network/Player.cpp @@ -0,0 +1,137 @@ +#include "Player.h" +#include "Client.h" +#include "v8datamodel/TimerService.h" + +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); + + Reflection::SignalDesc event_Idled("Idled", "time"); + + Backpack* Player::getPlayerBackpack() const + { + Backpack* backpack = findFirstChildOfType(); + + RBXASSERT(backpack != NULL); + return backpack; + } + + void Player::onCharacterChangedFrontend() + { + RBXASSERT(Players::frontendProcessing(this, true)); + + Player* localPlayer = Players::findLocalPlayer(this); + if (this == localPlayer) + { + Workspace* workspace = ServiceProvider::find(this); + RBXASSERT(workspace != NULL); + + if (!character) + { + workspace->getCamera()->setCameraType(Camera::FIXED_CAMERA); + workspace->getCamera()->setDistanceFromTarget(0.0f); + workspace->getCamera()->zoom(-1.0f); + } + else + { + workspace->getCamera()->setCameraSubject(Humanoid::modelIsCharacter(character.get())); + workspace->getCamera()->setCameraType(Camera::CUSTOM_CAMERA); + workspace->getCamera()->setDistanceFromTarget(13.0f); + workspace->getCamera()->zoom(-1.0f); + workspace->setDefaultMouseCommand(); + } + } + } + + void Player::setTeamColor(BrickColor value) + { + if (value != teamColor) + { + teamColor = value; + raisePropertyChanged(prop_teamColor); + } + } + + void Player::setNeutral(bool value) + { + if (value != neutral) + { + neutral = value; + raisePropertyChanged(prop_neutral); + } + } + + void Player::removeCharacter() + { + if (!Players::backendProcessing(this, true)) + throw std::runtime_error("RemoveCharacter can only be called by the backend server"); + + setCharacter(NULL); + } + + void Player::setName(const std::string& value) + { + Security::Context::current().requirePermission(Security::Administrator, "set a Player's name"); + Instance::setName(value); + } + + void Player::setCharacterAppearance(const std::string& value) + { + if (value != characterAppearance) + { + characterAppearance = value; + if (Players::backendProcessing(this, false)) + loadCharacterAppearance(); + + raisePropertyChanged(prop_characterAppearance); + } + } + + void Player::onServiceProvider(const ServiceProvider* oldProvider, const ServiceProvider* newProvider) + { + if (oldProvider && Players::backendProcessing(oldProvider, true)) + setCharacter(NULL); + + Instance::onServiceProvider(oldProvider, newProvider); + + if (newProvider && Players::frontendProcessing(newProvider, true)) + onCharacterChangedFrontend(); + + if (!oldProvider && Players::frontendProcessing(newProvider, true)) + { + RBXASSERT(Players::frontendProcessing(this, true)); + lastActivityTime = G3D::System::getLocalTime(); + doPeriodicIdleCheck(); + } + } + + void Player::doPeriodicIdleCheck() + { + if (ServiceProvider::findServiceProvider(this)) + { + RBXASSERT(Players::frontendProcessing(this, true)); + if (lastActivityTime != 0.0) + { + G3D::RealTime idleTime = G3D::System::getLocalTime() - lastActivityTime; + if (idleTime > 120.0) + { + Players* players = ServiceProvider::find(this); + if (players && players->getLocalPlayer() && Players::clientIsPresent(this, true)) + { + event_Idled.fire(this, idleTime); + } + } + } + + TimerService* timerService = ServiceProvider::create(this); + + if (timerService) + timerService->delay(boost::bind(&Player::doPeriodicIdleCheck, shared_from(this)), 30.0); + } + } + } +} diff --git a/Client/Network/Players.cpp b/Client/Network/Players.cpp new file mode 100644 index 00000000..e14418a9 --- /dev/null +++ b/Client/Network/Players.cpp @@ -0,0 +1,169 @@ +#include "Players.h" +#include "Client.h" +#include "Streaming.h" + +template +class PluginInterfaceAdapter : public PluginInterface +{ +private: + Class* c; +protected: + PluginInterfaceAdapter(Class*); +public: + virtual PluginReceiveResult OnReceive(RakPeerInterface* peer, Packet* packet); +}; + +namespace RBX +{ + namespace Network + { + class Players::Plugin : public PluginInterfaceAdapter + { + Plugin(Players*); + }; + + bool Players::clientIsPresent(const Instance* context, bool testInDatamodel) + { + return Client::clientIsPresent(context, testInDatamodel); + } + + bool Players::askAddChild(const Instance* instance) const + { + return fastDynamicCast(instance) != NULL; + } + + void Players::setConnection(RakPeerInterface* peer) + { + if (this->peer) + this->peer->DetachPlugin(plugin.get()); + + this->peer = peer; + + if (peer) + peer->AttachPlugin(plugin.get()); + } + + Player* Players::findLocalPlayer(const Instance* context) + { + Players* players = ServiceProvider::find(context); + return players ? players->getLocalPlayer() : NULL; + } + + ModelInstance* Players::findLocalCharacter(const Instance* context) + { + Player* player = Players::findLocalPlayer(context); + return player ? player->getCharacter() : NULL; + } + + void Players::setAbuseReportUrl(std::string value) + { + if (value.empty()) + abuseReporter.reset(NULL); + else + abuseReporter.reset(new AbuseReporter(value)); + } + + void Players::reportAbuse(boost::shared_ptr player, std::string comment) + { + reportAbuse(fastDynamicCast(player.get()), comment); + } + + Player* Players::getPlayerFromCharacter(Instance* character) + { + Players* players = ServiceProvider::find(character); + if (players) + { + boost::shared_ptr found = players->playerFromCharacter(shared_from(character)); + return static_cast(found.get()); + } + else + { + return NULL; + } + } + + void Players::chat(std::string message) + { + if (!localPlayer) + throw std::runtime_error("No local Player to chat from"); + + RakNet::BitStream bitStream; + bitStream << 'P'; + + Guid::Data data; + localPlayer->getGuid().extract(data); + + bitStream << data.scope->name; + bitStream << data.index; + bitStream << message; + + peer->Send(&bitStream, MEDIUM_PRIORITY, RELIABLE, 2, UNASSIGNED_SYSTEM_ADDRESS, true); + + ChatMessage event = {message, localPlayer, boost::shared_ptr()}; + Notifier::raise(event); + } + + bool Players::backendProcessing(const Instance* context, bool testInDatamodel) + { + const ServiceProvider* sp = ServiceProvider::findServiceProvider(context); + RBXASSERT(!testInDatamodel || sp); + + return sp && !Client::clientIsPresent(context, testInDatamodel); + } + + boost::shared_ptr Players::playerFromCharacter(boost::shared_ptr character) + { + boost::shared_ptr>> myPlayers = getPlayers(); + + std::vector>::const_iterator iter = myPlayers->begin(); + std::vector>::const_iterator end = myPlayers->end(); + + for (; iter != end; iter++) + { + if (static_cast(iter->get())->getCharacter() == static_cast(character.get())) + return *iter; + } + + return boost::shared_ptr(); + } + + boost::shared_ptr Players::getPlayerByID(int userID) + { + std::vector>::const_iterator iter = players->begin(); + std::vector>::const_iterator end = players->end(); + + for (; iter != end; iter++) + { + if (static_cast(iter->get())->getUserID() == userID) + return *iter; + } + + return boost::shared_ptr(); + } + + void AbuseReport::addMessage(const ChatMessage& cm) + { + int userId = cm.source ? Player::prop_userId.getValue(cm.source.get()) : 0; + AbuseReport::Message m = {userId, cm.message}; + messages.push_back(m); + } + + AbuseReporter::AbuseReporter(std::string abuseUrl) + : _data(new AbuseReporter::data) + { + requestProcessor.reset(new worker_thread(boost::bind(&AbuseReporter::processRequests, _data, abuseUrl), "rbx_abusereporter")); + } + + void AbuseReporter::add(AbuseReport& r, const std::list& chatHistory) + { + std::for_each(chatHistory.begin(), chatHistory.end(), boost::bind(&AbuseReport::addMessage, &r, _1)); + + { + boost::mutex::scoped_lock lock(_data->requestSync); + _data->queue.push(r); + } + + requestProcessor->wake(); + } + } +} diff --git a/Client/Network/include/Network/Player.h b/Client/Network/include/Network/Player.h index 8c3cdee0..3a49c18e 100644 --- a/Client/Network/include/Network/Player.h +++ b/Client/Network/include/Network/Player.h @@ -1,13 +1,16 @@ #pragma once +#include +#include "Network/Players.h" +#include "security/SecurityContext.h" +#include "humanoid/Humanoid.h" #include "v8tree/Instance.h" +#include "v8tree/Service.h" #include "v8datamodel/BrickColor.h" +#include "v8datamodel/Backpack.h" +#include "v8datamodel/Workspace.h" namespace RBX { - class ServiceProvider; - class ModelInstance; - class Backpack; - namespace Network { struct CharacterAdded @@ -46,7 +49,7 @@ namespace RBX bool under13; bool superSafeChat; int userId; - double lastActivityTime; + G3D::RealTime lastActivityTime; public: static Reflection::BoundProp prop_userId; @@ -56,7 +59,7 @@ namespace RBX private: virtual bool askAddChild(const Instance*) const; - virtual void onServiceProvider(const ServiceProvider*, const ServiceProvider*); + virtual void onServiceProvider(const ServiceProvider* oldProvider, const ServiceProvider* newProvider); void onCharacterChangedFrontend(); void registerLocalPlayerNotIdle(); public: @@ -65,20 +68,41 @@ namespace RBX virtual ~Player(); public: virtual XmlElement* write(); - virtual void setName(const std::string&); - ModelInstance* getCharacter() const; + virtual void setName(const std::string& value); + ModelInstance* getCharacter() const + { + return character.get(); + } void setCharacter(ModelInstance*); - BrickColor getTeamColor() const; - void setTeamColor(BrickColor); - bool getNeutral() const; - void setNeutral(bool); - std::string getCharacterAppearance() const; - void setCharacterAppearance(const std::string&); + BrickColor getTeamColor() const + { + return teamColor; + } + void setTeamColor(BrickColor value); + bool getNeutral() const + { + return neutral; + } + void setNeutral(bool value); + std::string getCharacterAppearance() const + { + return characterAppearance; + } + void setCharacterAppearance(const std::string& value); bool getUnder13() const; bool getSuperSafeChat() const; - void setUnder13(bool); - void setSuperSafeChat(bool); - int getUserID() const; + void setUnder13(bool value) + { + prop_Under13.setValue(this, value); + } + void setSuperSafeChat(bool value) + { + prop_SuperSafeChat.setValue(this, value); + } + int getUserID() const + { + return userId; + } void rebuildBackpack(); Backpack* getPlayerBackpack() const; void loadCharacter(); diff --git a/Client/Network/include/Network/Players.h b/Client/Network/include/Network/Players.h index 599bdfbe..bb8eb557 100644 --- a/Client/Network/include/Network/Players.h +++ b/Client/Network/include/Network/Players.h @@ -1,18 +1,24 @@ #pragma once #include "Network/Player.h" #include "Network/SuperSafeChanged.h" +#include "v8tree/Service.h" +#include "v8datamodel/ModelInstance.h" #include #include class RakPeerInterface; -class Packet; +struct Packet; + +template +class PluginInterfaceAdapter; namespace RBX { - class ModelInstance; - namespace Network { + class Player; + struct CharacterAdded; + struct ChatMessage { public: @@ -30,16 +36,8 @@ namespace RBX public: struct Message { - public: int userID; std::string text; - - public: - //Message(const Message&); - Message(); - ~Message(); - public: - //Message& operator=(const Message&); }; public: @@ -49,7 +47,7 @@ namespace RBX std::list messages; public: - void addMessage(const ChatMessage&); + void addMessage(const ChatMessage& cm); public: //AbuseReport(const AbuseReport&); AbuseReport(); @@ -81,9 +79,9 @@ namespace RBX public: //AbuseReporter(const AbuseReporter&); - AbuseReporter(std::string); + AbuseReporter(std::string abuseUrl); public: - void add(AbuseReport&, const std::list&); + void add(AbuseReport& r, const std::list& chatHistory); public: ~AbuseReporter(); public: @@ -125,25 +123,37 @@ namespace RBX public: bool superSafeOn() const; boost::shared_ptr createLocalPlayer(int); - Player* getLocalPlayer() const; + Player* getLocalPlayer() const + { + return localPlayer.get(); + } int getNumPlayers() const; - int numPlayers() const; - int getMaxPlayers() const; + int numPlayers() const + { + return (int)players->size(); + } + int getMaxPlayers() const + { + return maxPlayers; + } void setMaxPlayers(int); - boost::shared_ptr>> getPlayers(); - void chat(std::string); - void reportAbuse(boost::shared_ptr, std::string); + boost::shared_ptr>> getPlayers() + { + return players.read(); + } + void chat(std::string message); + void reportAbuse(boost::shared_ptr player, std::string comment); void reportAbuse(Player*, std::string); std::list::const_iterator chatHistory_begin(); std::list::const_iterator chatHistory_end(); bool canReportAbuse() const; - void setAbuseReportUrl(std::string); + void setAbuseReportUrl(std::string value); bool OnReceive(RakPeerInterface*, Packet*); - void setConnection(RakPeerInterface*); - boost::shared_ptr playerFromCharacter(boost::shared_ptr); - boost::shared_ptr getPlayerByID(int); + void setConnection(RakPeerInterface* peer); + boost::shared_ptr playerFromCharacter(boost::shared_ptr character); + boost::shared_ptr getPlayerByID(int userID); protected: - virtual bool askAddChild(const Instance*) const; + virtual bool askAddChild(const Instance* instance) const; virtual void onChildAdded(Instance*); virtual void onChildRemoving(Instance*); private: @@ -152,12 +162,12 @@ namespace RBX //Players& operator=(const Players&); public: - static Player* getPlayerFromCharacter(Instance*); - static ModelInstance* findLocalCharacter(const Instance*); - static Player* findLocalPlayer(const Instance*); - static bool clientIsPresent(const Instance*, bool); + static Player* getPlayerFromCharacter(Instance* character); + static ModelInstance* findLocalCharacter(const Instance* context); + static Player* findLocalPlayer(const Instance* context); + static bool clientIsPresent(const Instance* context, bool testInDatamodel); static bool serverIsPresent(const Instance*, bool); - static bool frontendProcessing(const Instance*, bool); + static bool frontendProcessing(const Instance* context, bool testInDatamodel); static bool backendProcessing(const Instance*, bool); }; }