#ifndef TSERVER_H #define TSERVER_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "FileSystem.h" #include "ServerList.h" #include "misc/WordFilter.h" #include "utilities/CommandDispatcher.h" #include "utilities/IdGenerator.h" #ifdef UPNP #include "misc/UPNP.h" #endif #ifdef V8NPCSERVER #include "scripting/ScriptEngine.h" #endif #include "scripting/GS2ScriptManager.h" #include "scripting/ScriptClass.h" // Resources #include "animation/GameAni.h" #include "utilities/ResourceManager.h" #include "UpdatePackage.h" class Player; class Level; class NPC; class ScriptClass; class Map; class Weapon; enum // Socket Type { SOCK_PLAYER = 0, SOCK_SERVER = 1, }; enum { FS_ALL = 0, FS_FILE = 1, FS_LEVEL = 2, FS_HEAD = 3, FS_BODY = 4, FS_SWORD = 5, FS_SHIELD = 6, }; #define FS_COUNT 7 // Player ids 0 and 1 break things. NPC id 0 breaks things. // Don't allow anything to have one of those ids. // Player ids 16000 and up is used for players on other servers and "IRC"-channels. // The players from other servers should be unique lists for each player as they are fetched depending on // what the player chooses to see (buddies, "global guilds" tab, "other servers" tab) constexpr uint16_t PLAYERID_INIT = 2; constexpr uint32_t NPCID_INIT = 10001; using AnimationManager = ResourceManager; using PackageManager = ResourceManager; using TriggerDispatcher = CommandDispatcher&>; class Server : public CSocketStub { public: // Required by CSocketStub. bool onRecv(); bool onSend() { return true; } bool onRegister() { return true; } void onUnregister() { return; } SOCKET getSocketHandle() { return m_playerSock.getHandle(); } bool canRecv() { return true; } bool canSend() { return false; } Server(const CString& pName); ~Server(); void operator()(); void cleanup(); void restart(); bool running; int init(const CString& serverip = "", const CString& serverport = "", const CString& localip = "", const CString& serverinterface = ""); bool doMain(); // Server Management int loadConfigFiles(); void loadSettings(); void loadAdminSettings(); void loadAllowedVersions(); void loadFileSystem(); void loadServerFlags(); void loadServerMessage(); void loadIPBans(); void loadClasses(bool print = false); void loadWeapons(bool print = false); void loadMaps(bool print = false); void loadMapLevels(); #ifdef V8NPCSERVER void loadNpcs(bool print = false); #endif void loadTranslations(); void loadWordFilter(); void loadAllFolders(); void loadFolderConfig(); void saveServerFlags(); void saveWeapons(); #ifdef V8NPCSERVER void saveNpcs(); std::vector> calculateNpcStats(); #endif void reportScriptException(const ScriptRunError& error); void reportScriptException(const std::string& error_message); // Get functions. const CString& getName() const { return m_name; } FileSystem* getFileSystem(int c = 0) { return &(m_filesystem[c]); } FileSystem* getAccountsFileSystem() { return &m_filesystemAccounts; } CLog& getNPCLog() { return m_npcLog; } CLog& getServerLog() { return m_serverLog; } CLog& getRCLog() { return m_rcLog; } CLog& getScriptLog() { return m_scriptLog; } CSettings& getSettings() { return m_settings; } CSettings& getAdminSettings() { return m_adminSettings; } CSocketManager& getSocketManager() { return m_sockManager; } CString getServerPath() const { return m_serverPath; } CString getServerPath(const std::string& path) const; const CString& getServerMessage() const { return m_serverMessage; } const CString& getAllowedVersionString() const { return m_allowedVersionString; } CTranslationManager& getTranslationManager() { return m_translationManager; } WordFilter& getWordFilter() { return m_wordFilter; } ServerList& getServerList() { return m_serverlist; } AnimationManager& getAnimationManager() { return m_animationManager; } PackageManager& getPackageManager() { return m_packageManager; } unsigned int getNWTime() const { return m_serverTime; } void calculateServerTime(); std::unordered_map>& getClassList() { return m_classList; } std::unordered_map>& getNPCNameList() { return m_npcNameList; } std::unordered_map& getServerFlags() { return m_serverFlags; } std::unordered_map>& getWeaponList() { return m_weaponList; } std::unordered_map>& getPlayerList() { return m_playerList; } std::unordered_map>& getNPCList() { return m_npcList; } std::vector>& getLevelList() { return m_levelList; } const std::vector>& getMapList() const { return m_mapList; } const std::vector& getStatusList() const { return m_statusList; } const std::vector& getAllowedVersions() const { return m_allowedVersions; } std::unordered_multimap>& getGroupLevels() { return m_groupLevels; } #ifdef V8NPCSERVER ScriptEngine* getScriptEngine() { return &m_scriptEngine; } int getNCPort() const { return m_ncPort; } std::shared_ptr getNPCServer() const { return m_npcServer; } #endif FileSystem* getFileSystemByType(CString& type); CString getFlag(const std::string& pFlagName); std::shared_ptr getLevel(const std::string& pLevel); std::shared_ptr getNPC(const uint32_t id) const; std::shared_ptr getPlayer(const uint16_t id) const; std::shared_ptr getPlayer(const uint16_t id, int type) const; // = PLTYPE_ANYCLIENT) const; std::shared_ptr getPlayer(const CString& account, int type) const; #ifdef V8NPCSERVER void assignNPCName(std::shared_ptr npc, const std::string& name); void removeNPCName(std::shared_ptr npc); std::shared_ptr getNPCByName(const std::string& name) const; std::shared_ptr addServerNpc(int npcId, float pX, float pY, std::shared_ptr pLevel, bool sendToPlayers = false); void handlePM(Player* player, const CString& message); void setPMFunction(uint32_t npcId, IScriptFunction* function = nullptr); #endif std::shared_ptr addNPC(const CString& pImage, const CString& pScript, float pX, float pY, std::weak_ptr pLevel, bool pLevelNPC, bool sendToPlayers = false); bool deleteNPC(int id, bool eraseFromLevel = true); bool deleteNPC(std::shared_ptr npc, bool eraseFromLevel = true); bool deleteClass(const std::string& className); bool hasClass(const std::string& className) const; ScriptClass* getClass(const std::string& className) const; void updateClass(const std::string& className, const std::string& classCode); bool isIpBanned(const CString& ip); bool isStaff(const CString& accountName); void logToFile(const std::string& fileName, const std::string& message); bool deleteFlag(const std::string& pFlagName, bool pSendToPlayers = true); bool setFlag(CString pFlag, bool pSendToPlayers = true); bool setFlag(const std::string& pFlagName, const CString& pFlagValue, bool pSendToPlayers = true); // Admin chat functions void sendToRC(const CString& pMessage, std::weak_ptr pSender = {}) const; void sendToNC(const CString& pMessage, std::weak_ptr pSender = {}) const; // Packet sending. using PlayerPredicate = std::function; void sendPacketToAll(const CString& packet, const std::set& exclude = {}) const; void sendPacketToLevelArea(const CString& packet, std::weak_ptr level, const std::set& exclude = {}, PlayerPredicate sendIf = nullptr) const; void sendPacketToLevelArea(const CString& packet, std::weak_ptr player, const std::set& exclude = {}, PlayerPredicate sendIf = nullptr) const; void sendPacketToLevelOnlyGmapArea(const CString& packet, std::weak_ptr level, const std::set& exclude = {}, PlayerPredicate sendIf = nullptr) const; void sendPacketToLevelOnlyGmapArea(const CString& packet, std::weak_ptr player, const std::set& exclude = {}, PlayerPredicate sendIf = nullptr) const; void sendPacketToOneLevel(const CString& packet, std::weak_ptr level, const std::set& exclude = {}) const; void sendPacketToType(int who, const CString& pPacket, std::weak_ptr pPlayer = {}) const; void sendPacketToType(int who, const CString& pPacket, Player* pPlayer) const; // Specific packet sending void sendShootToOneLevel(const std::weak_ptr& sharedPtr, float x, float y, float z, float angle, float zangle, float strength, const std::string& ani, const std::string& aniArgs) const; // Player Management bool addPlayer(std::shared_ptr player, uint16_t id = USHRT_MAX); bool deletePlayer(std::shared_ptr player); void playerLoggedIn(std::shared_ptr player); bool warpPlayerToSafePlace(uint16_t playerId); // Translation Management bool TS_Load(const CString& pLanguage, const CString& pFileName); CString TS_Translate(const CString& pLanguage, const CString& pKey); void TS_Reload(); void TS_Save(); // Weapon Management std::shared_ptr getWeapon(const std::string& name); bool NC_AddWeapon(std::shared_ptr pWeaponObj); bool NC_DelWeapon(const std::string& pWeaponName); void updateWeaponForPlayers(std::shared_ptr pWeapon); void updateClassForPlayers(ScriptClass* pClass); /* * GS2 Functionality */ void compileGS2Script(const std::string& source, GS2ScriptManager::user_callback_type cb); void compileGS2Script(NPC* npc, GS2ScriptManager::user_callback_type cb); void compileGS2Script(Weapon* weapon, GS2ScriptManager::user_callback_type cb); void compileGS2Script(ScriptClass* cls, GS2ScriptManager::user_callback_type cb); std::time_t getServerStartTime() const { return m_serverStartTime; } TriggerDispatcher getTriggerDispatcher() const { return m_triggerActionDispatcher; } void setShootParams(const std::string& params) { m_shootParams = params; } const std::string& getShootParams() const { return m_shootParams; } private: GS2ScriptManager m_gs2ScriptManager; template void compileScript(ScriptObjType& obj, GS2ScriptManager::user_callback_type& cb); void handleGS2Errors(const std::vector& errors, const std::string& origin); private: bool doTimedEvents(); void cleanupDeletedPlayers(); bool m_doRestart; FileSystem m_filesystem[FS_COUNT], m_filesystemAccounts; CLog m_npcLog, m_rcLog, m_serverLog, m_scriptLog; //("logs/npclog|rclog|serverlog|scriptlog.txt"); CSettings m_adminSettings, m_settings; CSocket m_playerSock; CSocketManager m_sockManager; CTranslationManager m_translationManager; WordFilter m_wordFilter; AnimationManager m_animationManager; PackageManager m_packageManager; CString m_allowedVersionString, m_name, m_serverMessage, m_serverPath; CString m_overrideIp, m_overrideLocalIp, m_overridePort, m_overrideInterface; std::vector m_allowedVersions, m_foldersConfig, m_ipBans, m_statusList, m_staffList; std::unordered_map m_serverFlags; std::unordered_map> m_weaponList; std::unordered_map> m_classList; std::unordered_map> m_npcList; std::unordered_map> m_npcNameList; IdGenerator m_npcIdGenerator{ NPCID_INIT }; std::vector> m_mapList; std::vector> m_levelList; std::unordered_multimap> m_groupLevels; std::unordered_map> m_playerList; std::unordered_set> m_deletedPlayers; IdGenerator m_playerIdGenerator{ PLAYERID_INIT }; ServerList m_serverlist; std::chrono::high_resolution_clock::time_point m_lastTimer, m_lastNewWorldTimer, m_last1mTimer, m_last5mTimer, m_last3mTimer; std::time_t m_serverStartTime; unsigned int m_serverTime; // Trigger dispatcher TriggerDispatcher m_triggerActionDispatcher; void createTriggerCommands(TriggerDispatcher::Builder cmdBuilder); std::string m_shootParams; #ifdef V8NPCSERVER ScriptEngine m_scriptEngine; int m_ncPort; std::shared_ptr m_npcServer; std::shared_ptr m_pmHandlerNpc; #endif #ifdef UPNP UPNP m_upnp; std::thread m_upnpThread; #endif }; inline std::shared_ptr Server::getNPC(const uint32_t id) const { auto iter = m_npcList.find(id); if (iter != std::end(m_npcList)) return iter->second; return nullptr; } inline bool Server::hasClass(const std::string& className) const { return m_classList.find(className) != m_classList.end(); } inline ScriptClass* Server::getClass(const std::string& className) const { auto classIter = m_classList.find(className); if (classIter != m_classList.end()) return classIter->second.get(); return nullptr; } inline CString Server::getServerPath(const std::string& path) const { return getServerPath() << std::filesystem::weakly_canonical(path).string(); } #ifdef V8NPCSERVER inline std::shared_ptr Server::getNPCByName(const std::string& name) const { auto npcIter = m_npcNameList.find(name); if (npcIter != m_npcNameList.end()) return npcIter->second.lock(); return nullptr; } #endif #include "IEnums.h" inline void Server::sendToRC(const CString& pMessage, std::weak_ptr pSender) const { int len = pMessage.find("\n"); if (len == -1) len = pMessage.length(); sendPacketToType(PLTYPE_ANYRC, CString() >> (char)PLO_RC_CHAT << pMessage.subString(0, len), pSender); } inline void Server::sendToNC(const CString& pMessage, std::weak_ptr pSender) const { int len = pMessage.find("\n"); if (len == -1) len = pMessage.length(); sendPacketToType(PLTYPE_ANYNC, CString() >> (char)PLO_RC_CHAT << pMessage.subString(0, len), pSender); } #endif