1 /// \file 2 /// \brief Extends SQLite3ServerPlugin to support logging functions, including compressing images. 3 /// 4 /// This file is part of RakNet Copyright 2003 Jenkins Software LLC 5 /// 6 /// Raknet is available under the terms of the GPLv3 license, see /usr/local/share/licenses/raknet-3.9.2_10,1/GPLv3. 7 8 9 #ifndef ___SQLITE_SERVER_LOGGER_PLUGIN_H 10 #define ___SQLITE_SERVER_LOGGER_PLUGIN_H 11 12 #include "SQLite3ServerPlugin.h" 13 #include "SQLiteLoggerCommon.h" 14 15 class RakPeerInterface; 16 17 #define MAX_PACKETS_PER_CPU_INPUT_THREAD 16 18 19 namespace RakNet 20 { 21 22 /// \brief Extends SQLite3ServerPlugin to support logging functions, including compressing images. 23 /// \details SQLiteClientLoggerPlugin has the ability to send logs. Logs contain a description of the name of the table, the name of the columns, the types, and the data.<BR> 24 /// This class will create tables as necessary to support the client inputs.<BR> 25 /// Also, if images are sent, the server will format them from uncompressed to compressed JPG using jpeg-7 in a thread.<BR> 26 /// Compatible as a plugin with both RakPeerInterface and PacketizedTCP 27 /// \ingroup SQL_LITE_3_PLUGIN 28 class RAK_DLL_EXPORT SQLiteServerLoggerPlugin : public SQLite3ServerPlugin 29 { 30 public: 31 SQLiteServerLoggerPlugin(); 32 virtual ~SQLiteServerLoggerPlugin(); 33 34 /// Used with SetSessionManagementMode() 35 enum SessionManagementMode 36 { 37 /// \brief USE_ANY_DB_HANDLE is for games where the clients are not allowed to create new databases, and the clients don't care which database is written to. Typically only used if there is only one database ever. 38 /// \details Ignore SQLiteClientLoggerPlugin::StartSession(), and rely on a prior call to SQLite3ServerPlugin::AddDBHandle(). 39 /// If no handles exist, logging calls silently fail. 40 USE_ANY_DB_HANDLE, 41 42 /// \brief USE_NAMED_DB_HANDLE is for games where the clients are not allowed to create new databases. Instead, they use named databases precreated. 43 /// \details Interpret the sessionId parameter passed to SQLiteClientLoggerPlugin::StartSession() as the dbIdentifier parameter passed to SQLite3ServerPlugin::AddDBHandle(). 44 /// Use this database if it exists. If not, logging calls silently fail. 45 USE_NAMED_DB_HANDLE, 46 47 /// \brief CREATE_EACH_NAMED_DB_HANDLE is for single player games or multiplayer games where every game has a unique sesionId. 48 /// \details Use the sessionId parameter passed to SQLiteClientLoggerPlugin::StartSession() as a dbIdentifier. 49 /// A new database is created for each user 50 CREATE_EACH_NAMED_DB_HANDLE, 51 52 /// \brief CREATE_SHARED_NAMED_DB_HANDLE is for multiplayer games where you don't want to have to transmit and synchronize a unique sessionId. Everyone is in the same sessionId. 53 /// \details Use the sessionId parameter passed to SQLiteClientLoggerPlugin::StartSession() as a dbIdentifier. 54 /// A new database is created only if the sessionId is different. Two users using the same sessionId will write to the same database 55 CREATE_SHARED_NAMED_DB_HANDLE, 56 }; 57 58 /// \brief Determine if and how to automatically create databases, rather than call SQLite3ServerPlugin::AddDBHandle() 59 /// \details Typically you want one database to hold the events of a single application session, rather than one database for all sessions over all time. 60 /// A user of SQLiteClientLoggerPlugin can optionally call SQLiteClientLoggerPlugin::StartSession() in order to join a session to enable this. 61 /// Call this before calling AddDBHandle(), and before any clients connect 62 /// \param[in] _sessionManagementMode See SessionManagementMode. Default is CREATE_EACH_NAMED_DB_HANDLE. 63 /// \param[in] _createDirectoryForFile If true, uses the current server date as a directory to the filename. This ensures filenames do not overwrite each other, even if the sessionId is reused. Defaults to true. 64 /// \param[in] _newDatabaseFilePath For CREATE_EACH_NAMED_DB_HANDLE and CREATE_SHARED_NAMED_DB_HANDLE, will create the databases here. Not used for the other modes. Defaults to whatever the operating system picks (usually the application directory). 65 void SetSessionManagementMode(SessionManagementMode _sessionManagementMode, bool _createDirectoryForFile, const char *_newDatabaseFilePath); 66 67 /// Close all connected sessions, writing all databases immediately 68 void CloseAllSessions(void); 69 70 /// \brief Enable using realtime shader based DXT compression on an Nvidia based video card. This will activate OpenGL 71 /// \details Call this before anyone connects. 72 /// If not enabled, or initialization fails, then jpg compression is used instead on the CPU. 73 /// Defaults to false, in case the target system does not support OpenGL 74 /// \param[in] enable True to enable, false to disable. 75 void SetEnableDXTCompression(bool enable); 76 77 struct ProcessingStatus 78 { 79 int packetsBuffered; 80 int cpuPendingProcessing; 81 int cpuProcessedAwaitingDeallocation; 82 int cpuNumThreadsWorking; 83 int sqlPendingProcessing; 84 int sqlProcessedAwaitingDeallocation; 85 int sqlNumThreadsWorking; 86 }; 87 88 /// Return the thread and command processing statuses 89 void GetProcessingStatus(ProcessingStatus *processingStatus); 90 /// \internal 91 void Update(void); 92 93 /// \internal For plugin handling 94 virtual PluginReceiveResult OnReceive(Packet *packet); 95 /// \internal For plugin handling 96 virtual void OnClosedConnection(SystemAddress systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ); 97 /// \internal For plugin handling 98 virtual void OnShutdown(void); 99 virtual void OnAttach(void); 100 virtual void OnDetach(void); 101 102 struct SessionNameAndSystemAddress 103 { 104 RakNet::RakString sessionName; 105 SystemAddress systemAddress; 106 sqlite3 *referencedPointer; 107 RakNetTimeMS timestampDelta; 108 // RakNetTimeMS dbAgeWhenCreated; 109 }; 110 DataStructures::List<SessionNameAndSystemAddress> loggedInSessions; 111 112 // An incoming data packet, and when it arrived 113 struct CPUThreadInputNode 114 { 115 Packet *packet; 116 // RakNetTimeMS whenMessageArrived; 117 // Time difference from their time to server time, plus the age of the database at the time the session was created 118 // Applied to CPUThreadOutputNode::clientSendingTime before being passed to SQL 119 RakNetTimeMS timestampDelta; 120 RakNet::RakString dbIdentifier; 121 }; 122 // As packets arrive, they are added to a CPUThreadInput structure. 123 // When the structure is full, or when a maximum amount of time has elapsed, whichever is first, then it is pushed to a thread for processing 124 // Deallocated in the thread 125 struct CPUThreadInput 126 { 127 // One or more incoming data packets, and when those packets arrived 128 // Processed on the CPU, possibly with batch processing for image compression 129 CPUThreadInputNode cpuInputArray[MAX_PACKETS_PER_CPU_INPUT_THREAD]; 130 int arraySize; 131 }; 132 // Each CPUThreadInputNode is unpacked to a CPUThreadOutputNode 133 // Images are now in compressed format, should the parameter list indeed have a query 134 struct CPUThreadOutputNode 135 { 136 Packet *packet; // Passthrough 137 // RakNetTimeMS whenMessageArrived; // Passthrough 138 RakNet::RakString dbIdentifier; // Passthrough 139 // SystemAddress systemAddress; 140 char ipAddressString[32]; 141 RakNet::RakString tableName; 142 RakNet::RakString file; 143 RakNetTimeMS clientSendingTime; 144 unsigned char parameterCount; 145 bool isFunctionCall; 146 DataStructures::List<RakNet::RakString> insertingColumnNames; 147 LogParameter parameterList[MAX_SQLLITE_LOGGER_PARAMETERS]; 148 uint32_t tickCount; 149 int line; 150 }; 151 // List of CPUThreadOutputNode 152 struct CPUThreadOutput 153 { 154 // cpuOutputNodeArray pointers are not deallocated, just handed over to SQLThreadInput 155 // Each element in the array is pushed to one SQLThreadInput 156 CPUThreadOutputNode *cpuOutputNodeArray[MAX_PACKETS_PER_CPU_INPUT_THREAD]; 157 int arraySize; 158 }; 159 struct SQLThreadInput 160 { 161 sqlite3 *dbHandle; 162 CPUThreadOutputNode *cpuOutputNode; 163 }; 164 struct SQLThreadOutput 165 { 166 // cpuOutputNode gets deallocated here 167 CPUThreadOutputNode *cpuOutputNode; 168 }; 169 170 protected: 171 unsigned int CreateDBHandle(RakNet::RakString dbIdentifier); 172 void CloseUnreferencedSessions(void); 173 174 SessionManagementMode sessionManagementMode; 175 bool createDirectoryForFile; 176 RakNet::RakString newDatabaseFilePath; 177 178 ThreadPool<CPUThreadInput*, CPUThreadOutput*> cpuLoggerThreadPool; 179 ThreadPool<SQLThreadInput, SQLThreadOutput> sqlLoggerThreadPool; 180 181 CPUThreadInput *LockCpuThreadInput(void); 182 void ClearCpuThreadInput(void); 183 void UnlockCpuThreadInput(void); 184 void CancelLockCpuThreadInput(void); 185 void PushCpuThreadInputIfNecessary(void); 186 void PushCpuThreadInput(void); 187 void StopCPUSQLThreads(void); 188 189 CPUThreadInput *cpuThreadInput; 190 RakNetTime whenCpuThreadInputAllocated; 191 bool dxtCompressionEnabled; 192 193 }; 194 }; 195 196 #endif 197