/**************************************************************************** Copyright (c) 2013-2016 Chukong Technologies Inc. Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. Copyright (c) 2021 Bytedance Inc. https://adxe.org Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ #ifndef __CCCONSOLE_H__ #define __CCCONSOLE_H__ /// @cond DO_NOT_SHOW #if defined(_MSC_VER) || defined(__MINGW32__) #include #ifndef __SSIZE_T #define __SSIZE_T typedef SSIZE_T ssize_t; #endif // __SSIZE_T #endif #include #include #include #include #include #include #include #include "yasio/detail/socket.hpp" #include "base/CCRef.h" #include "base/ccMacros.h" #include "platform/CCPlatformMacros.h" NS_CC_BEGIN /// The max length of CCLog message. static const int MAX_LOG_LENGTH = 16*1024; /** @brief Output Debug message. */ void CC_DLL log(const char * format, ...) CC_FORMAT_PRINTF(1, 2); /** Console is helper class that lets the developer control the game from TCP connection. Console will spawn a new thread that will listen to a specified TCP port. Console has a basic token parser. Each token is associated with an std::function. If the std::function<> needs to use the cocos2d API, it needs to call ``` scheduler->performFunctionInCocosThread( ... ); ``` */ class CC_DLL Console : public Ref { public: /** Console Utils */ class Utility { public: // Trimming functions static std::string& ltrim(std::string& s); static std::string& rtrim(std::string& s); static std::string& trim(std::string& s); // split static std::vector& split(const std::string& s, char delim, std::vector& elems); static std::vector split(const std::string& s, char delim); /** Checks myString is a floating-point type. */ static bool isFloat(const std::string& myString); /** send a message to console */ static ssize_t sendToConsole(int fd, const void* buffer, size_t length, int flags = 0); /** my dprintf() */ static ssize_t mydprintf(int sock, const char *format, ...); /** send prompt string to console */ static void sendPrompt(int fd); /** set a new string for the prompt. */ static void setPrompt(const std::string &prompt); /** get the prompt string. */ static const std::string& getPrompt(); private: static std::string _prompt; /*!< prompt */ }; /** Command Struct */ class CC_DLL Command { public: using Callback = std::function; /** Constructor */ Command(); Command(const std::string& name, const std::string& help); Command(const std::string& name, const std::string& help, const Callback& callback); /** Copy constructor */ Command(const Command& o); /** Move constructor */ Command(Command&& o); /** Destructor */ ~Command(); /** Copy operator */ Command& operator=(const Command& o); /** Move operator */ Command& operator=(Command&& o); /** add callback */ void addCallback(const Callback& callback); /** add sub command */ void addSubCommand(const Command& subCmd); /** get sub command */ const Command* getSubCommand(const std::string& subCmdName) const; /** delete sub command */ void delSubCommand(const std::string& subCmdName); /** help command handler */ void commandHelp(int fd, const std::string& args); /** generic command handler */ void commandGeneric(int fd, const std::string& args); /** Gets the name of the current command */ const std::string& getName() const { return _name; } /** Gets the help information of the current command */ const std::string& getHelp() const { return _help; } private: std::string _name; std::string _help; Callback _callback; std::unordered_map _subCommands; }; /** Constructor */ Console(); /** Destructor */ virtual ~Console(); /** starts listening to specified TCP port */ bool listenOnTCP(int port); /** starts listening to specified file descriptor */ bool listenOnFileDescriptor(int fd); /** stops the Console. 'stop' will be called at destruction time as well */ void stop(); /** add custom command */ void addCommand(const Command& cmd); void addSubCommand(const std::string& cmdName, const Command& subCmd); void addSubCommand(Command& cmd, const Command& subCmd); /** get custom command */ const Command* getCommand(const std::string& cmdName); const Command* getSubCommand(const std::string& cmdName, const std::string& subCmdName); const Command* getSubCommand(const Command& cmd, const std::string& subCmdName); /** delete custom command */ void delCommand(const std::string& cmdName); void delSubCommand(const std::string& cmdName, const std::string& subCmdName); void delSubCommand(Command& cmd, const std::string& subCmdName); /** log something in the console */ void log(const char *buf); /** * set bind address * * @address : 127.0.0.1 */ void setBindAddress(const std::string &address); /** Checks whether the server for console is bound with ipv6 address */ bool isIpv6Server() const; /** The command separator */ CC_SYNTHESIZE(char, _commandSeparator, CommandSeparator); protected: // Main Loop void loop(); // Helpers ssize_t readline(socket_native_type fd, char *buf, size_t maxlen); ssize_t readBytes(socket_native_type fd, char* buffer, size_t maxlen, bool* more); bool parseCommand(socket_native_type fd); void performCommand(socket_native_type fd, const std::string& command); void addClient(); // create a map of command. void createCommandAllocator(); void createCommandConfig(); void createCommandDebugMsg(); void createCommandDirector(); void createCommandExit(); void createCommandFileUtils(); void createCommandFps(); void createCommandHelp(); void createCommandProjection(); void createCommandResolution(); void createCommandSceneGraph(); void createCommandTexture(); void createCommandTouch(); void createCommandUpload(); void createCommandVersion(); // Add commands here void commandAllocator(socket_native_type fd, const std::string& args); void commandConfig(socket_native_type fd, const std::string& args); void commandDebugMsg(socket_native_type fd, const std::string& args); void commandDebugMsgSubCommandOnOff(socket_native_type fd, const std::string& args); void commandDirectorSubCommandPause(socket_native_type fd, const std::string& args); void commandDirectorSubCommandResume(socket_native_type fd, const std::string& args); void commandDirectorSubCommandStop(socket_native_type fd, const std::string& args); void commandDirectorSubCommandStart(socket_native_type fd, const std::string& args); void commandDirectorSubCommandEnd(socket_native_type fd, const std::string& args); void commandExit(socket_native_type fd, const std::string& args); void commandFileUtils(socket_native_type fd, const std::string& args); void commandFileUtilsSubCommandFlush(socket_native_type fd, const std::string& args); void commandFps(socket_native_type fd, const std::string& args); void commandFpsSubCommandOnOff(socket_native_type fd, const std::string& args); void commandHelp(socket_native_type fd, const std::string& args); void commandProjection(socket_native_type fd, const std::string& args); void commandProjectionSubCommand2d(socket_native_type fd, const std::string& args); void commandProjectionSubCommand3d(socket_native_type fd, const std::string& args); void commandResolution(socket_native_type fd, const std::string& args); void commandResolutionSubCommandEmpty(socket_native_type fd, const std::string& args); void commandSceneGraph(socket_native_type fd, const std::string& args); void commandTextures(socket_native_type fd, const std::string& args); void commandTexturesSubCommandFlush(socket_native_type fd, const std::string& args); void commandTouchSubCommandTap(socket_native_type fd, const std::string& args); void commandTouchSubCommandSwipe(socket_native_type fd, const std::string& args); void commandUpload(socket_native_type fd); void commandVersion(socket_native_type fd, const std::string& args); // file descriptor: socket, console, etc. socket_native_type _listenfd; socket_native_type _maxfd; std::vector _fds; std::thread _thread; fd_set _read_set; bool _running; bool _endThread; bool _isIpv6Server; std::unordered_map _commands; // strings generated by cocos2d sent to the remote console bool _sendDebugStrings; std::mutex _DebugStringsMutex; std::vector _DebugStrings; intptr_t _touchId; std::string _bindAddress; private: CC_DISALLOW_COPY_AND_ASSIGN(Console); // helper functions int printSceneGraph(socket_native_type fd, Node* node, int level); void printSceneGraphBoot(socket_native_type fd); void printFileUtils(socket_native_type fd); /** send help message to console */ static void sendHelp(socket_native_type fd, const std::unordered_map& commands, const char* msg); }; NS_CC_END /// @endcond #endif /* defined(__CCCONSOLE_H__) */