/* Copyright 2003-2007 ROBLOX Corporation, All Rights Reserved */ #pragma once #include "V8Tree/Instance.h" #include "V8Tree/Service.h" #include "Network/Player.h" #include "Network/ChatFilter.h" #include "Util/SystemAddress.h" #include "Util/GameMode.h" #include "V8DataModel/FriendService.h" #include "V8DataModel/ContentProvider.h" #include "boost/thread/thread.hpp" #include #include #include namespace RakNet { class RakPeerInterface; struct Packet; class BitStream; struct SystemAddress; } namespace RBX { class PartInstance; class ModelInstance; class Region2; class Adorn; class ScriptInformationProvider; class DataModel; namespace Network { struct MemHash { size_t checkIdx; unsigned int value; unsigned int failMask; }; typedef std::vector MemHashVector; typedef std::vector MemHashConfigs; class ConcurrentRakPeer; class ChatMessage { public: enum ChatType { CHAT_TYPE_ALL, CHAT_TYPE_TEAM, CHAT_TYPE_WHISPER, CHAT_TYPE_GAME, //CHAT_TYPE_PARTY }; std::string guid; std::string message; ChatType chatType; shared_ptr const source; shared_ptr const destination; ChatMessage(const ChatMessage& other, const std::string& message); ChatMessage(const char* message, ChatType chatType, shared_ptr source); ChatMessage(const char* message, ChatType chatType, shared_ptr source, shared_ptr destination); bool isVisibleToPlayer(shared_ptr player) const; static bool isVisibleToPlayer(shared_ptr const player, shared_ptr const source, shared_ptr const destination, const ChatType chatType); std::string getReportAbuseMessage() const; }; struct AbuseReport { struct Message { int userID; std::string text; std::string guid; }; int placeID; std::string gameJobID; int submitterID; int allegedAbuserID; std::string comment; std::list< Message > messages; void addMessage(shared_ptr reportingPlayer, const ChatMessage& cm); }; class AbuseReporter { struct data { std::queue queue; boost::mutex requestSync; // synchronizes the request queue }; shared_ptr _data; scoped_ptr requestProcessor; public: AbuseReporter(std::string abuseUrl); void add(AbuseReport& r, shared_ptr reportingPlayer, const std::list& chatHistory); private: static worker_thread::work_result processRequests(shared_ptr _data, std::string abuseUrl); }; extern const char* const sPlayers; class Players : public DescribedNonCreatable , public Service { private: typedef DescribedNonCreatable Super; public: // Unfortunately the RakNet enums are not accessible via the Players class, so this identical enum is created // and mapped in PluginInterfaceAdapter::OnReceive. - Tim enum ReceiveResult { // The plugin used this message and it shouldn't be given to the user. PLAYERS_STOP_PROCESSING_AND_DEALLOCATE=0, // The plugin is going to hold on to this message. Do not deallocate it but do not pass it to other plugins either. PLAYERS_STOP_PROCESSING, }; enum ChatOption { CLASSIC_CHAT = 0, BUBBLE_CHAT = 1, CLASSIC_AND_BUBBLE_CHAT = 2 }; enum PlayerChatType { PLAYER_CHAT_TYPE_ALL = 0, PLAYER_CHAT_TYPE_TEAM = 1, PLAYER_CHAT_TYPE_WHISPER= 2 }; static bool isNetworkClient(Instance* instance); void friendEventFired(int userId, int otherUserId, FriendService::FriendEventType friendEvent); void friendStatusChanged(int userId, int otherUserId, FriendService::FriendStatus friendStatus); private: std::string saveDataUrl; std::string loadDataUrl; std::string saveLeaderboardDataUrl; boost::unordered_set leaderboardKeys; std::string chatFilterUrl; std::string buildUserPermissionsUrl; std::string sysStatsUrl; std::string goldenHash; std::string goldenHash2; std::string goldenHash3; static bool canKickBecauseRunningInRealGameServer; static std::set goldenHashes; static boost::mutex goldenHashesMutex; static MemHashConfigs goldMemHashes; static boost::mutex goldMemHashesMutex; bool characterAutoSpawn; scoped_ptr abuseReporter; boost::intrusive_ptr::Registry> guidRegistry; std::list chatHistory; const boost::intrusive_ptr::Registry>& getGuidRegistry(); copy_on_write_ptr players; ConcurrentRakPeer* rakPeer; int maxPlayers; int preferredPlayers; int testPlayerNameId; int testPlayerUserId; shared_ptr localPlayer; ChatOption chatOption; bool nonSuperSafeChatForAllPlayersEnabled; rbx::signals::connection blockUserClientSignalConnection; rbx::signals::connection blockUserFinishedFromServerConnection; rbx::signals::connection loadLocalPlayerGuisConnection; boost::unordered_map, std::pair, boost::function > > clientBlockUserMap; void raiseChatMessageSignal(const ChatMessage& message); void raisePlayerChattedSignal(const ChatMessage& message); void loadLocalPlayerGuis(); void gotBlockUserSuccess(std::string response, bool blockUser, int blockerUserId, int blockeeUserId, boost::function resumeFunction, boost::function errorFunction); void gotBlockUserError(std::string error, bool blockUser, int blockerUserId, int blockeeUserId, boost::function errorFunction); static void onReceivedRawGetUserIdSuccess(weak_ptr weakDataModel, std::string response, boost::function resumeFunction, boost::function errorFunction); static void onReceivedRawGetUserIdError(weak_ptr weakDataModel, std::string error, boost::function errorFunction); static void onReceivedRawGetUserNameSuccess(weak_ptr weakDataModel, std::string response, boost::function resumeFunction, boost::function errorFunction); static void onReceivedRawGetUserNameError(weak_ptr weakDataModel, std::string error, boost::function errorFunction); void serverMakeBlockUserRequest(bool blockUser, int blockerUserId, int blockeeUserId, boost::function resumeFunction, boost::function errorFunction); void clientReceiveBlockUserFinished(int blockerUserId, int blockeeUserId, std::string errorString); void internalBlockUser(int blockerUserId, int blockeeUserId, bool isBlocking, boost::function resumeFunction, boost::function errorFunction); public: ReceiveResult OnReceiveChat(Player* sourceValidation, RakNet::RakPeerInterface* peer, RakNet::Packet* packet, unsigned char chatType); ReceiveResult OnReceiveReportAbuse(Player* source, RakNet::RakPeerInterface* peer, RakNet::Packet* packet); rbx::signal,shared_ptr,FriendService::FriendEventType)> friendRequestEvent; rbx::signal)> playerAddedEarlySignal; rbx::signal)> playerAddedSignal; rbx::signal)> playerRemovingSignal; rbx::signal)> playerRemovingLateSignal; rbx::signal chatMessageSignal; rbx::signal abuseReportedReceived; rbx::signal&, const shared_ptr, const std::string&, const std::string&)> sendFilteredChatMessageSignal; rbx::signal, std::string, shared_ptr)> playerChattedSignal; rbx::signal gameAnnounceSignal; rbx::remote_signal blockUserRequestFromClientSignal; rbx::remote_signal blockUserFinishedFromServerSignal; rbx::remote_signal requestCloudEditKick; rbx::remote_signal requestCloudEditShutdown; static Reflection::RefPropDescriptor propLocalPlayer; static Reflection::PropDescriptor propCharacterAutoSpawn; static Reflection::RemoteEventDesc event_requestCloudEditKick; static Reflection::RemoteEventDesc event_requestCloudEditShutdown; Players(); ~Players(); bool superSafeOn() const; shared_ptr createLocalPlayer(int userId, bool teleportedIn = false); void resetLocalPlayer(); Player* getLocalPlayer() {return localPlayer.get();} const Player* getConstLocalPlayer() const {return localPlayer.get();} Instance* getLocalPlayerDangerous() const; // only for reflection int getNumPlayers() const { return (int) players->size(); } void getUserIdFromName(std::string userName, boost::function resumeFunction, boost::function errorFunction); void getNameFromUserId(int userId, boost::function resumeFunction, boost::function errorFunction); void getFriends(int userId, boost::function) > resumeFunction, boost::function errorFunction); static void setAppearanceParent(shared_ptr model, weak_ptr instance); static void doLoadAppearance(AsyncHttpQueue::RequestResult result, shared_ptr instances, std::string contentDescription, shared_ptr model, shared_ptr amountToLoad, boost::function)> resumeFunction, boost::function errorFunction); static void doMakeAccoutrementRequests(std::string response, weak_ptr dataModel, shared_ptr model, boost::function)> resumeFunction, boost::function errorFunction); static void makeAccoutrementRequests(std::string *response, std::exception *err, weak_ptr dataModel, shared_ptr model, boost::function)> resumeFunction, boost::function errorFunction); void getCharacterAppearance(int userId, boost::function)> resumeFunction, boost::function errorFunction); int getMaxPlayers() const; int getPreferredPlayers() const; void setMaxPlayers(int value); void setPreferredPlayers(int value); void setSysStatsUrl(std::string url); void setSysHash(std::string hash); void setGoldenHashes(const std::string& windowsHash, const std::string& macHash, const std::string& windowsPlayerBetaHash); static void setGoldenHashes2(const std::set& hashes); static void setGoldMemHashes(const MemHashConfigs& hashes); std::string getSaveDataUrl(int userId) const; void setSaveDataUrl(std::string saveDataUrl); std::string getLoadDataUrl(int userId) const; void setLoadDataUrl(std::string loadDataUrl); std::string getSaveLeaderboardDataUrl(int userId) const; void setSaveLeaderboardDataUrl(std::string saveLeaderboardDataUrl); void addLeaderboardKey(std::string); bool hasLeaderboardKey(const std::string& key) const; boost::unordered_set::const_iterator beginLeaderboardKey() const; boost::unordered_set::const_iterator endLeaderboardKey() const; void setChatOption(ChatOption value); void setNonSuperSafeChatForAllPlayersEnabled(bool enabled); bool getNonSuperSafeChatForAllPlayersEnabled() const; bool getClassicChat() const { return chatOption == CLASSIC_CHAT || chatOption == CLASSIC_AND_BUBBLE_CHAT; } bool getBubbleChat() const { return chatOption == BUBBLE_CHAT || chatOption == CLASSIC_AND_BUBBLE_CHAT; } shared_ptr getPlayers() { return players.read(); } // Chat-related functions void gamechat(const std::string& message); void chat(std::string message); void teamChat(std::string); void whisperChat(std::string message, shared_ptr player); void reportAbuse(Player* player, const std::string& comment); void reportAbuseLua(shared_ptr instance, std::string reason, std::string comment); std::list::const_iterator chatHistory_begin() { return chatHistory.begin(); } std::list::const_iterator chatHistory_end() { return chatHistory.end(); } bool canReportAbuse() const; void setAbuseReportUrl(std::string value); void setChatFilterUrl(std::string value); void setBuildUserPermissionsUrl(std::string value); bool hasBuildUserPermissionsUrl() const; std::string getBuildUserPermissionsUrl(int playerId) const; void friendServiceRequest(bool makeFriends, weak_ptr sourcePlayer, int otherUserId); bool getCharacterAutoSpawnProperty() const { return characterAutoSpawn; } void setCharacterAutoSpawnProperty(bool value); bool getShouldAutoSpawnCharacter() const; void blockUser(int blockerUserId, int blockeeUserId, boost::function resumeFunction = boost::function(), boost::function errorFunction = boost::function()); void unblockUser(int blockerUserId, int blockeeUserId, boost::function resumeFunction = boost::function(), boost::function errorFunction = boost::function()); void setConnection(ConcurrentRakPeer* rakPeer); shared_ptr playerFromCharacter(shared_ptr character); shared_ptr getPlayerInstanceByID(int userID); shared_ptr getPlayerByID(int userID); static Player* getPlayerFromCharacter(RBX::Instance* character); void buildClientRegion(Region2& clientRegion); void renderDPhysicsRegions(Adorn* adorn); /////////////////////////////////////////////////////////////////////////// // // STATICS // // If Client == Client Network Address; If Server == NetworkOwner::Server() static RBX::SystemAddress findLocalSimulatorAddress(const RBX::Instance* context); static ModelInstance* findLocalCharacter(RBX::Instance* context); static const ModelInstance* findConstLocalCharacter(const RBX::Instance* context); static Player* findLocalPlayer(RBX::Instance* context); static const Player* findConstLocalPlayer(const RBX::Instance* context); static shared_ptr findAncestorPlayer(const RBX::Instance* context); static shared_ptr findPlayerWithAddress(const RBX::SystemAddress& playerAddres, const RBX::Instance* context); static bool clientIsPresent(const RBX::Instance* context, bool testInDatamodel = true); static bool serverIsPresent(const RBX::Instance* context, bool testInDatamodel = true); static bool frontendProcessing(const RBX::Instance* context, bool testInDatamodel = true); static bool backendProcessing(const RBX::Instance* context, bool testInDatamodel = true); static int getPlayerCount(const RBX::Instance* context); // TODO: Remove this some day! static bool getDistributedPhysicsEnabled(); /////////////////////////////////////////////////////////////////////////// // // These are here as "official" designation of Big-Picture states // // Frontend Backend // GameServer: x serverIsPresent (assumes !findLocalPlayer) // Visit Online: x clientIsPresent && findLocalPlayer // Watch Online: x clientIsPresent && !findLocalPlayer (i.e. - visit, no character) // Visit Solo: x x !clientIsPresent && !serverIsPresent && findLocalPlayer // Local Play: x clientIsPresent && findLocalPlayer && userID == 0 // Edit Mode: x x !clientIsPresent && !serverIsPresent && !findLocalPlayer // // typedef enum {GAME_SERVER, DPHYS_GAME_SERVER, CLIENT, DPHYS_CLIENT, WATCH_ONLINE, VISIT_SOLO, EDIT} GameMode; static RBX::Network::GameMode getGameMode(const RBX::Instance* context) { bool client = clientIsPresent(context); bool server = serverIsPresent(context); bool localPlayer = (findConstLocalPlayer(context) != NULL); bool dPhysics = getDistributedPhysicsEnabled(); RBXASSERT(!(server && (client || localPlayer))); if (server) {return dPhysics ? DPHYS_GAME_SERVER : GAME_SERVER;} if (client && localPlayer) {return dPhysics ? DPHYS_CLIENT : CLIENT;} if (client && !localPlayer) {return WATCH_ONLINE;} if (!client && localPlayer) {return VISIT_SOLO;} else {return EDIT;} } static RBX::Network::GameMode getGameMode(const RBX::Instance* context, const int placeID) { bool client = clientIsPresent(context); bool server = serverIsPresent(context); bool localPlayer = ( findConstLocalPlayer(context) != NULL); bool dPhysics = getDistributedPhysicsEnabled(); RBXASSERT(!(server && (client || localPlayer)));; if (placeID <= 0) {return LOCAL_PLAY;} if (server) {return dPhysics ? DPHYS_GAME_SERVER : GAME_SERVER;} if (client && localPlayer) {return dPhysics ? DPHYS_CLIENT : CLIENT;} if (client && !localPlayer) {return WATCH_ONLINE;} if (!client && localPlayer) {return VISIT_SOLO;} else {return EDIT;} } static bool isCloudEdit(const RBX::Instance* context); void onRemoteSysStats(int userId, const std::string& stat, const std::string& message, bool desireKick = true); bool hashMatches(const std::string& hash); static unsigned int checkGoldMemHashes(const std::vector& hashes); void disconnectPlayer(int userId, int reason); void disconnectPlayerLocal(int userId, int reason); bool getUseCoreScriptHealthBar(); protected: void disconnectPlayer(Instance& instance, int userId, int reason); std::map > cheatingPlayers; bool askAddChild(const Instance* instance) const; /*override*/ void onChildAdded(Instance* child); /*override*/ void onChildRemoving(Instance* child); /*override*/ void onDescendantRemoving(const shared_ptr& instance); /*override*/ void processRemoteEvent(const Reflection::EventDescriptor& descriptor, const Reflection::EventArguments& args, const RBX::SystemAddress& source); private: void reportScriptSecurityError(int userId, std::string hash, std::string error, std::string stack); void killPlayer(int userId); void addChatMessage(const ChatMessage& message); void checkChat(const std::string& message); void sendFilteredChatMessageSignalHelper(const RakNet::SystemAddress& systemAddress, const shared_ptr& baseData, const shared_ptr sourceInstance, shared_ptr chatEvent, const ChatFilter::Result& response); // Instance /*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); }; } }