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