/* * This file was taken from RakNet 4.082. * Please see licenses/RakNet license.txt for the underlying license and related copyright. * * Modified work: Copyright (c) 2016-2020, SLikeSoft UG (haftungsbeschränkt) * * This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style * license found in the license.txt file in the root directory of this source tree. */ #include "SQLiteServerLoggerPlugin.h" #include "slikenet/peerinterface.h" #include "slikenet/PacketizedTCP.h" #include "slikenet/MessageIdentifiers.h" #include "SQLiteLoggerCommon.h" #include "jpeglib.h" #include "jpeg_memory_dest.h" #include "slikenet/FileOperations.h" #include "slikenet/GetTime.h" #include #include #include #include #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4505) // unreferenced local function #endif #include "DXTCompressor.h" #ifdef _MSC_VER #pragma warning(pop) #endif #include "slikenet/linux_adapter.h" // http://web.utk.edu/~jplyon/sqlite/SQLite_optimization_FAQ.html // See jmorecfg.h /* * Ordering of RGB data in scanlines passed to or from the application. * If your application wants to deal with data in the order B,G,R, just * change these macros. You can also deal with formats such as R,G,B,X * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing * the offsets will also change the order in which colormap data is organized. * RESTRICTIONS: * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not * useful if you are using JPEG color spaces other than YCbCr or grayscale. * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE * is not 3 (they don't understand about dummy color components!). So you * can't use color quantization if you change that value. */ //#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ //#define RGB_GREEN 1 /* Offset of Green */ //#define RGB_BLUE 2 /* Offset of Blue */ //#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ using namespace SLNet; // JPEG ENCODING ERRORS struct my_error_mgr { struct jpeg_error_mgr pub; }; METHODDEF(void) my_error_exit (j_common_ptr cinfo) { // unused parameters (void)cinfo; } #define FILE_COLUMN "SourceFile" #define LINE_COLUMN "SourceLine" #define TICK_COUNT_COLUMN "AutoTickCount" #define AUTO_IP_COLUMN "AutoIPAddress" #define TIMESTAMP_NUMERIC_COLUMN "TimestampNumeric" #define TIMESTAMP_TEXT_COLUMN "TimestampText" #define FUNCTION_CALL_FRIENDLY_TEXT "FunctionCallFriendlyText" #define FUNCTION_CALL_TABLE "'functionCalls'" #define FUNCTION_CALL_PARAMETERS_TABLE "'functionCallParameters'" // Store packets in the CPUThreadInput until at most this much time has elapsed. This is so // batch processing can occur on multiple image sources at once static const SLNet::TimeMS MAX_TIME_TO_BUFFER_PACKETS=1000; // WTF am I getting this? // 2>SQLiteServerLoggerPlugin.obj : error LNK2019: unresolved external symbol _GetSqlDataTypeName referenced in function "struct SLNet::SQLite3ServerPlugin::SQLExecThreadOutput __cdecl ExecSQLLoggingThread(struct SLNet::SQLite3ServerPlugin::SQLExecThreadInput,bool *,void *)" (?ExecSQLLoggingThread@@YA?AUExecThreadOutput@SQLite3ServerPlugin@RakNet@@UExecThreadInput@23@PA_NPAX@Z) // 2>C:\RakNet\Debug\SQLiteServerLogger.exe : fatal error LNK1120: 1 unresolved externals static const char *sqlDataTypeNames[SQLLPDT_COUNT] = { "INTEGER", "INTEGER", "NUMERIC", "TEXT", "BLOB", "BLOB", }; const char *GetSqlDataTypeName2(SQLLoggerPrimaryDataType idx) {return sqlDataTypeNames[(int)idx];} void CompressAsJpeg(char **cptrInOut, uint32_t *sizeInOut, uint16_t imageWidth, uint16_t imageHeight, int16_t linePitch, unsigned char input_components) { // Compress to jpg // http://www.google.com/codesearch/p?hl=en#I-_InJ6STRE/gthumb-1.108/libgthumb/pixbuf-utils.c&q=jpeg_create_compress // http://ftp.gnome.org / pub/ GNOME / sources /gthumb /1.108/ gthumb-1.108.tar.gz/ struct jpeg_compress_struct cinfo; struct my_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr.pub); jerr.pub.error_exit = my_error_exit; jpeg_create_compress(&cinfo); cinfo.smoothing_factor = 0; cinfo.optimize_coding = false; cinfo.image_width = imageWidth; cinfo.image_height = imageHeight; cinfo.input_components = input_components; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults (&cinfo); cinfo.dct_method=JDCT_FLOAT; // Not sure why they made RGB_PIXELSIZE a global define. I added this so it only uses my value in this one place, to not break other things cinfo.hack_use_input_components_as_RGB_PIXELSIZE=1; jpeg_set_quality (&cinfo, 75, TRUE); int jpegSizeAfterCompression = 0; //size of jpeg after compression char * storage = (char*) rakMalloc_Ex(*sizeInOut+50000,_FILE_AND_LINE_); jpeg_memory_dest(&cinfo,(JOCTET*)storage,*sizeInOut+50000,&jpegSizeAfterCompression); JSAMPROW row_pointer[1]; jpeg_start_compress (&cinfo, TRUE); while( cinfo.next_scanline < cinfo.image_height ) { row_pointer[0] = (JSAMPROW) &((*cptrInOut)[cinfo.next_scanline * linePitch ]); jpeg_write_scanlines(&cinfo, row_pointer, 1); } /* finish off */ jpeg_finish_compress (&cinfo); jpeg_destroy_compress(&cinfo); rakFree_Ex(*cptrInOut,_FILE_AND_LINE_); *cptrInOut = (char*) rakRealloc_Ex(storage, jpegSizeAfterCompression,_FILE_AND_LINE_); *sizeInOut=jpegSizeAfterCompression; } static bool needsDxtInit=true; static bool dxtCompressionSupported=false; void* InitDxt() { if (needsDxtInit==true) { dxtCompressionSupported=DXTCompressor::Initialize(); if (dxtCompressionSupported==false) { printf("Warning, DXT compressor failed to start.\nImages will be compressed with jpg instead.\n"); } } needsDxtInit=false; return 0; } void DeinitDxt(void*) { if (dxtCompressionSupported) DXTCompressor::Shutdown(); needsDxtInit=true; dxtCompressionSupported=false; } SQLiteServerLoggerPlugin::CPUThreadOutput* ExecCPULoggingThread(SQLiteServerLoggerPlugin::CPUThreadInput* cpuThreadInput, bool *returnOutput, void* perThreadData) { // unused parameters (void)perThreadData; int i; *returnOutput=true; SQLiteServerLoggerPlugin::CPUThreadOutput *cpuThreadOutput = SLNet::OP_NEW(_FILE_AND_LINE_); cpuThreadOutput->arraySize=cpuThreadInput->arraySize; //cpuThreadOutput->cpuOutputNodeArray=SLNet::OP_NEW_ARRAY(cpuThreadInput->arraySize,_FILE_AND_LINE_); //printf("1. arraySize=%i, ",cpuThreadInput->arraySize); for (i=0; iarraySize; i++) { cpuThreadOutput->cpuOutputNodeArray[i]= SLNet::OP_NEW(_FILE_AND_LINE_); SQLiteServerLoggerPlugin::CPUThreadOutputNode *outputNode = cpuThreadOutput->cpuOutputNodeArray[i]; Packet *packet = cpuThreadInput->cpuInputArray[i].packet; SLNet::RakString dbIdentifier = cpuThreadInput->cpuInputArray[i].dbIdentifier; // outputNode->whenMessageArrived = cpuThreadInput->cpuInputArray[i].whenMessageArrived; outputNode->packet=packet; packet->systemAddress.ToString(true,outputNode->ipAddressString,static_cast(32)); SLNet::BitStream bitStream(packet->data, packet->length, false); bitStream.IgnoreBytes(1); bitStream.Read(outputNode->dbIdentifier); bitStream.Read(outputNode->tableName); outputNode->tableName.SQLEscape(); bitStream.Read(outputNode->line); bitStream.Read(outputNode->file); bitStream.Read(outputNode->tickCount); bitStream.Read(outputNode->clientSendingTime); bitStream.Read(outputNode->isFunctionCall); bitStream.Read(outputNode->parameterCount); if (outputNode->isFunctionCall==false) { SLNet::RakString columnName; // printf("2. parameterCount=%i, ",outputNode->parameterCount); for (int j=0; j < outputNode->parameterCount; j++) { bitStream.Read(columnName); columnName.SQLEscape(); columnName.RemoveCharacter(' '); outputNode->insertingColumnNames.Push(columnName, _FILE_AND_LINE_ ); } } int parameterCountIndex=0; // printf("3. parameterCount=%i, ",outputNode->parameterCount); while (parameterCountIndex < outputNode->parameterCount) { outputNode->parameterList[parameterCountIndex].Deserialize(&bitStream); if (outputNode->parameterList[parameterCountIndex].size>0) { parameterCountIndex++; } else { // Skip empty parameters if (outputNode->isFunctionCall==false) outputNode->insertingColumnNames.RemoveAtIndex(parameterCountIndex); outputNode->parameterCount--; } } // sqlite3_stmt *statement; // char *errorMsg; // printf("4. parameterCount=%i, ",outputNode->parameterCount); for (parameterCountIndex=0; parameterCountIndex < outputNode->parameterCount; parameterCountIndex++) { if (outputNode->parameterList[parameterCountIndex].type==SQLLPDT_IMAGE) { bool dxtCompressSuccess=false; if (dxtCompressionSupported) { char *outputData; int bufferSize = DXTCompressor::GetBufferSize(DXT1, outputNode->parameterList[parameterCountIndex].imageWidth, outputNode->parameterList[parameterCountIndex].imageHeight); int ddsHeaderSize = DXTCompressor::GetDDSHeaderSize(); outputData = (char*) rakMalloc_Ex(bufferSize + ddsHeaderSize, _FILE_AND_LINE_ ); dxtCompressSuccess = DXTCompressor::CompressImageData( DXT1, outputNode->parameterList[parameterCountIndex].data.cptr, outputNode->parameterList[parameterCountIndex].imageWidth, outputNode->parameterList[parameterCountIndex].imageHeight, outputData+ddsHeaderSize, false, outputNode->parameterList[parameterCountIndex].sourceFormatIsBGRA ); if (dxtCompressSuccess) { rakFree_Ex(outputNode->parameterList[parameterCountIndex].data.cptr,_FILE_AND_LINE_); DXTCompressor::WriteDDSHeader(DXT1, outputNode->parameterList[parameterCountIndex].imageWidth, outputNode->parameterList[parameterCountIndex].imageHeight, bufferSize, outputData); outputNode->parameterList[parameterCountIndex].data.cptr=outputData; outputNode->parameterList[parameterCountIndex].size=bufferSize + ddsHeaderSize; // static bool testWriteToDisk=true; // if (testWriteToDisk) // { // printf("Wrote test.dds\n"); // FILE *fp; // fopen_s(&fp, "test.dds", "wb"); // fwrite(outputData,1,outputNode->parameterList[parameterCountIndex].size,fp); // fclose(fp); // testWriteToDisk=false; // } } else { rakFree_Ex(outputData,_FILE_AND_LINE_); } } if (dxtCompressSuccess==false) { if (outputNode->parameterList[parameterCountIndex].sourceFormatIsBGRA) { // Endian swap each color component. Input should be RGBA // int pixelIndex; // int rowIndex; int imageHeight = outputNode->parameterList[parameterCountIndex].imageHeight; // int imageWidth = outputNode->parameterList[parameterCountIndex].imageWidth; int bytesPerPixel = outputNode->parameterList[parameterCountIndex].input_components; int linePitch = outputNode->parameterList[parameterCountIndex].linePitch; char *dataPtr = outputNode->parameterList[parameterCountIndex].data.cptr; int totalBytes=linePitch*imageHeight; char *endPtr = dataPtr+totalBytes; unsigned char temp1; if (bytesPerPixel==3) { while (dataPtr!=endPtr) { temp1=dataPtr[2]; dataPtr[2]=dataPtr[0]; dataPtr[0]=temp1; dataPtr+=3; } } else { RakAssert(bytesPerPixel==4); while (dataPtr!=endPtr) { temp1=dataPtr[2]; dataPtr[2]=dataPtr[0]; dataPtr[0]=temp1; dataPtr+=4; } } } CompressAsJpeg( &outputNode->parameterList[parameterCountIndex].data.cptr, &outputNode->parameterList[parameterCountIndex].size, outputNode->parameterList[parameterCountIndex].imageWidth, outputNode->parameterList[parameterCountIndex].imageHeight, outputNode->parameterList[parameterCountIndex].linePitch, outputNode->parameterList[parameterCountIndex].input_components ); // // static bool testWriteToDisk=true; // if (testWriteToDisk) // { // printf("Wrote test.jpg\n"); // FILE *fp; // fopen_s(&fp, "test.jpg", "wb"); // fwrite(outputNode->parameterList[parameterCountIndex].data.cptr,1,outputNode->parameterList[parameterCountIndex].size,fp); // fclose(fp); // testWriteToDisk=false; // } } } } } // printf("5. out1, "); SLNet::OP_DELETE(cpuThreadInput,_FILE_AND_LINE_); // printf("6. out2\n"); return cpuThreadOutput; } struct SQLPreparedStatements { SQLPreparedStatements() { selectNameFromMaster=0; insertIntoFunctionCalls=0; insertIntoFunctionCallParameters=0; } sqlite3_stmt *selectNameFromMaster; sqlite3_stmt *insertIntoFunctionCalls; sqlite3_stmt *insertIntoFunctionCallParameters; }; void* SQLLoggerThreadAllocPreparedStatements() { SQLPreparedStatements *s = SLNet::OP_NEW(_FILE_AND_LINE_); return s; } void SQLLoggerThreadDeallocPreparedStatements(void* statementStruct) { SQLPreparedStatements *s = (SQLPreparedStatements *) statementStruct; if (s->selectNameFromMaster) sqlite3_finalize(s->selectNameFromMaster); if (s->insertIntoFunctionCalls) sqlite3_finalize(s->insertIntoFunctionCalls); if (s->insertIntoFunctionCallParameters) sqlite3_finalize(s->insertIntoFunctionCallParameters); SLNet::OP_DELETE(s,_FILE_AND_LINE_); } void DeleteBlobOrText(void* v) { LogParameter::Free(v); } SQLiteServerLoggerPlugin::SQLThreadOutput ExecSQLLoggingThread(SQLiteServerLoggerPlugin::SQLThreadInput sqlThreadInput, bool *returnOutput, void* perThreadData) { SQLiteServerLoggerPlugin::CPUThreadOutputNode *cpuOutputNode = sqlThreadInput.cpuOutputNode; SQLPreparedStatements *preparedStatements = (SQLPreparedStatements*) perThreadData; *returnOutput=true; SQLiteServerLoggerPlugin::SQLThreadOutput sqlThreadOutput; sqlThreadOutput.cpuOutputNode=cpuOutputNode; sqlite3 *dbHandle = sqlThreadInput.dbHandle; // sqlite3_stmt *statement; char *errorMsg; sqlite3_exec(dbHandle,"BEGIN TRANSACTION", 0, 0, 0); int rc; if (cpuOutputNode->isFunctionCall) { if (preparedStatements->selectNameFromMaster==0) { // Create function tables if they are not there already if (sqlite3_prepare_v2( dbHandle, "SELECT name FROM sqlite_master WHERE type='table' AND name=" FUNCTION_CALL_TABLE " ", -1, &preparedStatements->selectNameFromMaster, 0 )!=SQLITE_OK) { RakAssert("Failed PRAGMA table_info for function tables in SQLiteServerLoggerPlugin.cpp" && 0); for (int i=0; i < cpuOutputNode->parameterCount; i++) cpuOutputNode->parameterList[i].Free(); sqlite3_exec(dbHandle,"END TRANSACTION", 0, 0, 0); return sqlThreadOutput; } } rc = sqlite3_step(preparedStatements->selectNameFromMaster); sqlite3_reset(preparedStatements->selectNameFromMaster); if (rc!=SQLITE_ROW) { // Create table if it isn't there already rc = sqlite3_exec(dbHandle,"CREATE TABLE " FUNCTION_CALL_TABLE " (functionId_pk INTEGER PRIMARY KEY, " FUNCTION_CALL_FRIENDLY_TEXT " TEXT, functionName TEXT, " FILE_COLUMN " TEXT, " LINE_COLUMN " INTEGER, " TICK_COUNT_COLUMN " INTEGER, " AUTO_IP_COLUMN " TEXT, " TIMESTAMP_TEXT_COLUMN " TIMESTAMP DATE DEFAULT (datetime('now','localtime')), " TIMESTAMP_NUMERIC_COLUMN " NUMERIC )", 0, 0, &errorMsg); RakAssert(rc==SQLITE_OK); sqlite3_free(errorMsg); // See sqlDataTypeNames for *val rc = sqlite3_exec(dbHandle,"CREATE TABLE " FUNCTION_CALL_PARAMETERS_TABLE " (fcpId_pk INTEGER PRIMARY KEY, functionId_fk integer NOT NULL, value TEXT, FOREIGN KEY (functionId_fk) REFERENCES " FUNCTION_CALL_TABLE " (functionId_pk))", 0, 0, &errorMsg); RakAssert(rc==SQLITE_OK); sqlite3_free(errorMsg); } else { // Table already there } // Insert into function calls int parameterCountIndex; SLNet::RakString functionCallFriendlyText("%s(", cpuOutputNode->tableName.C_String()); for (parameterCountIndex=0; parameterCountIndex < cpuOutputNode->parameterCount; parameterCountIndex++) { if (parameterCountIndex!=0) functionCallFriendlyText+=", "; switch (cpuOutputNode->parameterList[parameterCountIndex].type) { case SQLLPDT_POINTER: if (cpuOutputNode->parameterList[parameterCountIndex].size==4) functionCallFriendlyText+= SLNet::RakString("%p", cpuOutputNode->parameterList[parameterCountIndex].data.i); else functionCallFriendlyText+= SLNet::RakString("%p", cpuOutputNode->parameterList[parameterCountIndex].data.ll); break; case SQLLPDT_INTEGER: switch (cpuOutputNode->parameterList[parameterCountIndex].size) { case 1: functionCallFriendlyText+= SLNet::RakString("%i", cpuOutputNode->parameterList[parameterCountIndex].data.c); break; case 2: functionCallFriendlyText+= SLNet::RakString("%i", cpuOutputNode->parameterList[parameterCountIndex].data.s); break; case 4: functionCallFriendlyText+= SLNet::RakString("%i", cpuOutputNode->parameterList[parameterCountIndex].data.i); break; case 8: functionCallFriendlyText+= SLNet::RakString("%i", cpuOutputNode->parameterList[parameterCountIndex].data.ll); break; } break; case SQLLPDT_REAL: if (cpuOutputNode->parameterList[parameterCountIndex].size==sizeof(float)) functionCallFriendlyText+= SLNet::RakString("%f", cpuOutputNode->parameterList[parameterCountIndex].data.f); else functionCallFriendlyText+= SLNet::RakString("%d", cpuOutputNode->parameterList[parameterCountIndex].data.d); break; case SQLLPDT_TEXT: functionCallFriendlyText+='"'; if (cpuOutputNode->parameterList[parameterCountIndex].size>0) functionCallFriendlyText.AppendBytes(cpuOutputNode->parameterList[parameterCountIndex].data.cptr, cpuOutputNode->parameterList[parameterCountIndex].size); functionCallFriendlyText+='"'; break; case SQLLPDT_IMAGE: functionCallFriendlyText+= SLNet::RakString("<%i byte image>", cpuOutputNode->parameterList[parameterCountIndex].size, cpuOutputNode->parameterList[parameterCountIndex].data.cptr); break; case SQLLPDT_BLOB: functionCallFriendlyText+= SLNet::RakString("<%i byte binary>", cpuOutputNode->parameterList[parameterCountIndex].size, cpuOutputNode->parameterList[parameterCountIndex].data.cptr); break; } } functionCallFriendlyText+=");"; if (preparedStatements->insertIntoFunctionCalls==0) { rc = sqlite3_prepare_v2(dbHandle, "INSERT INTO " FUNCTION_CALL_TABLE " (" FUNCTION_CALL_FRIENDLY_TEXT ", " FILE_COLUMN ", " LINE_COLUMN ", " TICK_COUNT_COLUMN ", " AUTO_IP_COLUMN ", " TIMESTAMP_NUMERIC_COLUMN " ,functionName) VALUES (?,?,?,?,?,?,?)", -1, &preparedStatements->insertIntoFunctionCalls, 0); if (rc!=SQLITE_DONE && rc!=SQLITE_OK) { RakAssert("Failed INSERT INTO " FUNCTION_CALL_PARAMETERS_TABLE " in SQLiteServerLoggerPlugin.cpp" && 0); } } sqlite3_bind_text(preparedStatements->insertIntoFunctionCalls, 1, functionCallFriendlyText.C_String(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(preparedStatements->insertIntoFunctionCalls, 2, cpuOutputNode->file.C_String(), -1, SQLITE_TRANSIENT); sqlite3_bind_int(preparedStatements->insertIntoFunctionCalls, 3, cpuOutputNode->line); sqlite3_bind_int(preparedStatements->insertIntoFunctionCalls, 4, cpuOutputNode->tickCount); sqlite3_bind_text(preparedStatements->insertIntoFunctionCalls, 5, cpuOutputNode->ipAddressString, -1, SQLITE_TRANSIENT); sqlite3_bind_int(preparedStatements->insertIntoFunctionCalls, 6, (uint32_t) (cpuOutputNode->clientSendingTime)); sqlite3_bind_text(preparedStatements->insertIntoFunctionCalls, 7, cpuOutputNode->tableName.C_String(), -1, SQLITE_TRANSIENT); rc = sqlite3_step(preparedStatements->insertIntoFunctionCalls); sqlite3_reset(preparedStatements->insertIntoFunctionCalls); if (rc!=SQLITE_DONE && rc!=SQLITE_OK) { RakAssert("Failed binding parameters to functionCalls in SQLiteServerLoggerPlugin.cpp" && 0); } // sqlite3_finalize(statement); // SLNet::RakString insertIntoFunctionCallsQuery("INSERT INTO 'functionCalls' (" FUNCTION_CALL_FRIENDLY_TEXT ", " FILE_COLUMN ", " LINE_COLUMN ", " TICK_COUNT_COLUMN ",functionName) VALUES ('%s','%s',%i,%i,'%s') ", functionCallFriendlyText.C_String(), file.C_String(), line, tickCount,tableName.C_String()); // rc = sqlite3_exec(dbHandle,insertIntoFunctionCallsQuery.C_String(), 0, 0, &errorMsg); // RakAssert(rc==SQLITE_OK); // sqlite3_free(errorMsg); // Read last row id // Requires that this thread has its own connection sqlite3_int64 lastRowId = sqlite3_last_insert_rowid(dbHandle); if (preparedStatements->insertIntoFunctionCallParameters==0) { rc = sqlite3_prepare_v2(dbHandle, "INSERT INTO functionCallParameters (functionId_fk, value) VALUES (?,?);", -1, &preparedStatements->insertIntoFunctionCallParameters, 0); RakAssert(rc==SQLITE_OK); sqlite3_bind_int64(preparedStatements->insertIntoFunctionCallParameters, 1, lastRowId); } // Insert into parameters table for (parameterCountIndex=0; parameterCountIndex < cpuOutputNode->parameterCount; parameterCountIndex++) { switch (cpuOutputNode->parameterList[parameterCountIndex].type) { case SQLLPDT_POINTER: case SQLLPDT_INTEGER: switch (cpuOutputNode->parameterList[parameterCountIndex].size) { case 1: sqlite3_bind_int(preparedStatements->insertIntoFunctionCallParameters, 2, cpuOutputNode->parameterList[parameterCountIndex].data.c); break; case 2: sqlite3_bind_int(preparedStatements->insertIntoFunctionCallParameters, 2, cpuOutputNode->parameterList[parameterCountIndex].data.s); break; case 4: sqlite3_bind_int(preparedStatements->insertIntoFunctionCallParameters, 2, cpuOutputNode->parameterList[parameterCountIndex].data.i); break; case 8: sqlite3_bind_int64(preparedStatements->insertIntoFunctionCallParameters, 2, cpuOutputNode->parameterList[parameterCountIndex].data.ll); break; } break; case SQLLPDT_REAL: if (cpuOutputNode->parameterList[parameterCountIndex].size==sizeof(float)) sqlite3_bind_double(preparedStatements->insertIntoFunctionCallParameters, 2, cpuOutputNode->parameterList[parameterCountIndex].data.f); else sqlite3_bind_double(preparedStatements->insertIntoFunctionCallParameters, 2, cpuOutputNode->parameterList[parameterCountIndex].data.d); break; case SQLLPDT_TEXT: sqlite3_bind_text(preparedStatements->insertIntoFunctionCallParameters, 2, cpuOutputNode->parameterList[parameterCountIndex].data.cptr, cpuOutputNode->parameterList[parameterCountIndex].size, 0); break; case SQLLPDT_IMAGE: case SQLLPDT_BLOB: sqlite3_bind_blob(preparedStatements->insertIntoFunctionCallParameters, 2, cpuOutputNode->parameterList[parameterCountIndex].data.vptr, cpuOutputNode->parameterList[parameterCountIndex].size, DeleteBlobOrText); cpuOutputNode->parameterList[parameterCountIndex].DoNotFree(); break; default: RakAssert("Hit invalid default in case label in SQLiteServerLoggerPlugin.cpp" && 0); } rc = sqlite3_step(preparedStatements->insertIntoFunctionCallParameters); sqlite3_reset(preparedStatements->insertIntoFunctionCallParameters); if (rc!=SQLITE_DONE && rc!=SQLITE_OK) { RakAssert("Failed sqlite3_step to bind functionCall parameters in SQLiteServerLoggerPlugin.cpp" && 0); } } // if (statement) // sqlite3_finalize(statement); } else { sqlite3_stmt *pragmaTableInfo; SLNet::RakString pragmaQuery("PRAGMA table_info(%s)",cpuOutputNode->tableName.C_String()); if (sqlite3_prepare_v2( dbHandle, pragmaQuery.C_String(), -1, &pragmaTableInfo, 0 )!=SQLITE_OK) { RakAssert("Failed PRAGMA table_info for tableName in SQLiteServerLoggerPlugin.cpp" && 0); for (int i=0; i < cpuOutputNode->parameterCount; i++) cpuOutputNode->parameterList[i].Free(); sqlite3_exec(dbHandle,"END TRANSACTION", 0, 0, 0); return sqlThreadOutput; } rc = sqlite3_step(pragmaTableInfo); DataStructures::List existingColumnNames; DataStructures::List existingColumnTypes; while (rc==SQLITE_ROW) { /* int nameColumn; for (int j=0; j < sqlite3_column_count(statement); j++) { if (strcmp(sqlite3_column_name(statement,j),"name")==0) { nameColumn=j; break; } } int typeColumn; for (int j=0; j < sqlite3_column_count(statement); j++) { if (strcmp(sqlite3_column_name(statement,j),"type")==0) { typeColumn=j; break; } } */ const int nameColumn=1; const int typeColumn=2; RakAssert(strcmp(sqlite3_column_name(pragmaTableInfo,nameColumn),"name")==0); RakAssert(strcmp(sqlite3_column_name(pragmaTableInfo,typeColumn),"type")==0); SLNet::RakString columnName = sqlite3_column_text(pragmaTableInfo,nameColumn); SLNet::RakString columnType = sqlite3_column_text(pragmaTableInfo,typeColumn); existingColumnNames.Push(columnName, _FILE_AND_LINE_ ); existingColumnTypes.Push(columnType, _FILE_AND_LINE_ ); rc = sqlite3_step(pragmaTableInfo); } sqlite3_reset(pragmaTableInfo); sqlite3_finalize(pragmaTableInfo); if (rc==SQLITE_ERROR) { RakAssert("Failed sqlite3_step in SQLiteServerLoggerPlugin.cpp" && 0); for (int i=0; i < cpuOutputNode->parameterCount; i++) cpuOutputNode->parameterList[i].Free(); sqlite3_exec(dbHandle,"END TRANSACTION", 0, 0, 0); return sqlThreadOutput; } int existingColumnNamesIndex,insertingColumnNamesIndex; if (existingColumnNames.Size()==0) { SLNet::RakString createQuery("CREATE TABLE %s (rowId_pk INTEGER PRIMARY KEY, " FILE_COLUMN " TEXT, " LINE_COLUMN " INTEGER, " TICK_COUNT_COLUMN " INTEGER, " AUTO_IP_COLUMN " TEXT, " TIMESTAMP_TEXT_COLUMN " TIMESTAMP DATE DEFAULT (datetime('now','localtime')), " TIMESTAMP_NUMERIC_COLUMN " NUMERIC",cpuOutputNode->tableName.C_String()); for (int i=0; i < cpuOutputNode->parameterCount; i++) { createQuery+=", "; createQuery+=cpuOutputNode->insertingColumnNames[i]; createQuery+=" "; createQuery+=GetSqlDataTypeName2(cpuOutputNode->parameterList[i].type); } createQuery+=" )"; sqlite3_exec(dbHandle, createQuery.C_String(), 0, 0, &errorMsg); RakAssert(errorMsg==0); sqlite3_free(errorMsg); } else { // Compare what is there (columnNames,columnTypes) to what we are adding. Add what is missing bool alreadyExists; for (insertingColumnNamesIndex=0; insertingColumnNamesIndex<(int) cpuOutputNode->insertingColumnNames.Size(); insertingColumnNamesIndex++) { alreadyExists=false; for (existingColumnNamesIndex=0; existingColumnNamesIndex<(int) existingColumnNames.Size(); existingColumnNamesIndex++) { if (existingColumnNames[existingColumnNamesIndex]==cpuOutputNode->insertingColumnNames[insertingColumnNamesIndex]) { // Type mismatch? If so, abort if (existingColumnTypes[existingColumnNamesIndex]!=GetSqlDataTypeName2(cpuOutputNode->parameterList[insertingColumnNamesIndex].type)) { printf("Error: Column type mismatch. TableName=%s. ColumnName%s. Existing=%s. New=%s\n", cpuOutputNode->tableName.C_String(), existingColumnNames[existingColumnNamesIndex].C_String(), existingColumnTypes[existingColumnNamesIndex].C_String(), GetSqlDataTypeName2(cpuOutputNode->parameterList[insertingColumnNamesIndex].type) ); for (int i=0; i < cpuOutputNode->parameterCount; i++) cpuOutputNode->parameterList[i].Free(); sqlite3_exec(dbHandle,"END TRANSACTION", 0, 0, 0); return sqlThreadOutput; } alreadyExists=true; break; } } if (alreadyExists==false) { sqlite3_exec(dbHandle, SLNet::RakString("ALTER TABLE %s ADD %s %s", cpuOutputNode->tableName.C_String(), cpuOutputNode->insertingColumnNames[insertingColumnNamesIndex].C_String(), GetSqlDataTypeName2(cpuOutputNode->parameterList[insertingColumnNamesIndex].type) ).C_String(), 0, 0, &errorMsg); RakAssert(errorMsg==0); sqlite3_free(errorMsg); } } } // Insert new row SLNet::RakString insertQuery("INSERT INTO %s (", cpuOutputNode->tableName.C_String()); int parameterCountIndex; for (parameterCountIndex=0; parameterCountIndexparameterCount; parameterCountIndex++) { if (parameterCountIndex!=0) insertQuery+=", "; insertQuery+=cpuOutputNode->insertingColumnNames[parameterCountIndex].C_String(); } // Add file and line to the end insertQuery+=", " FILE_COLUMN ", " LINE_COLUMN ", " TICK_COUNT_COLUMN ", " AUTO_IP_COLUMN ", " TIMESTAMP_NUMERIC_COLUMN " ) VALUES ("; for (parameterCountIndex=0; parameterCountIndexparameterCount+5; parameterCountIndex++) { if (parameterCountIndex!=0) insertQuery+=", ?"; else insertQuery+="?"; } insertQuery+=")"; sqlite3_stmt *customStatement; if (sqlite3_prepare_v2( dbHandle, insertQuery.C_String(), -1, &customStatement, 0 )!=SQLITE_OK) { RakAssert("Failed second sqlite3_prepare_v2 in SQLiteServerLoggerPlugin.cpp" && 0); for (int i=0; i < cpuOutputNode->parameterCount; i++) cpuOutputNode->parameterList[i].Free(); sqlite3_exec(dbHandle,"END TRANSACTION", 0, 0, 0); return sqlThreadOutput; } for (parameterCountIndex=0; parameterCountIndexparameterCount; parameterCountIndex++) { switch (cpuOutputNode->parameterList[parameterCountIndex].type) { case SQLLPDT_POINTER: case SQLLPDT_INTEGER: switch (cpuOutputNode->parameterList[parameterCountIndex].size) { case 1: sqlite3_bind_int(customStatement, parameterCountIndex+1, cpuOutputNode->parameterList[parameterCountIndex].data.c); break; case 2: sqlite3_bind_int(customStatement, parameterCountIndex+1, cpuOutputNode->parameterList[parameterCountIndex].data.s); break; case 4: sqlite3_bind_int(customStatement, parameterCountIndex+1, cpuOutputNode->parameterList[parameterCountIndex].data.i); break; case 8: sqlite3_bind_int64(customStatement, parameterCountIndex+1, cpuOutputNode->parameterList[parameterCountIndex].data.ll); break; } break; case SQLLPDT_REAL: if (cpuOutputNode->parameterList[parameterCountIndex].size==sizeof(float)) sqlite3_bind_double(customStatement, parameterCountIndex+1, cpuOutputNode->parameterList[parameterCountIndex].data.f); else sqlite3_bind_double(customStatement, parameterCountIndex+1, cpuOutputNode->parameterList[parameterCountIndex].data.d); break; case SQLLPDT_TEXT: sqlite3_bind_text(customStatement, parameterCountIndex+1, cpuOutputNode->parameterList[parameterCountIndex].data.cptr, cpuOutputNode->parameterList[parameterCountIndex].size, DeleteBlobOrText); cpuOutputNode->parameterList[parameterCountIndex].DoNotFree(); break; case SQLLPDT_IMAGE: case SQLLPDT_BLOB: sqlite3_bind_blob(customStatement, parameterCountIndex+1, cpuOutputNode->parameterList[parameterCountIndex].data.vptr, cpuOutputNode->parameterList[parameterCountIndex].size, DeleteBlobOrText); cpuOutputNode->parameterList[parameterCountIndex].DoNotFree(); break; } } // Add file and line to the end sqlite3_bind_text(customStatement, parameterCountIndex+1, cpuOutputNode->file.C_String(), (int) cpuOutputNode->file.GetLength(), SQLITE_TRANSIENT); sqlite3_bind_int(customStatement, parameterCountIndex+2, cpuOutputNode->line); sqlite3_bind_int(customStatement, parameterCountIndex+3, cpuOutputNode->tickCount); sqlite3_bind_text(customStatement, parameterCountIndex+4, cpuOutputNode->ipAddressString, -1, SQLITE_TRANSIENT); sqlite3_bind_int(customStatement, parameterCountIndex+5, (uint32_t) (cpuOutputNode->clientSendingTime)); rc = sqlite3_step(customStatement); if (rc!=SQLITE_DONE && rc!=SQLITE_OK) { RakAssert("Failed sqlite3_step to bind blobs in SQLiteServerLoggerPlugin.cpp" && 0); for (int i=0; i < cpuOutputNode->parameterCount; i++) cpuOutputNode->parameterList[i].Free(); sqlite3_exec(dbHandle,"END TRANSACTION", 0, 0, 0); return sqlThreadOutput; } sqlite3_finalize(customStatement); } sqlite3_exec(dbHandle,"END TRANSACTION", 0, 0, 0); for (int i=0; i < cpuOutputNode->parameterCount; i++) cpuOutputNode->parameterList[i].Free(); return sqlThreadOutput; } SQLiteServerLoggerPlugin::SQLiteServerLoggerPlugin() { sessionManagementMode=CREATE_EACH_NAMED_DB_HANDLE; createDirectoryForFile=true; cpuThreadInput=0; dxtCompressionEnabled=false; } SQLiteServerLoggerPlugin::~SQLiteServerLoggerPlugin() { StopCPUSQLThreads(); SLNet::OP_DELETE(cpuThreadInput,_FILE_AND_LINE_); CloseAllSessions(); } void SQLiteServerLoggerPlugin::Update(void) { SQLite3ServerPlugin::Update(); bool hadOutput=false; int arrayIndex; // unsigned int i; PushCpuThreadInputIfNecessary(); while (cpuLoggerThreadPool.HasOutputFast() && cpuLoggerThreadPool.HasOutput()) { CPUThreadOutput* cpuThreadOutput=cpuLoggerThreadPool.GetOutput(); for (arrayIndex=0; arrayIndex < cpuThreadOutput->arraySize; arrayIndex++) { SQLThreadInput sqlThreadInput; CPUThreadOutputNode *outputNode = cpuThreadOutput->cpuOutputNodeArray[arrayIndex]; sqlThreadInput.cpuOutputNode=outputNode; // bool alreadyHasLoggedInSession=false; unsigned int sessionIndex; for (sessionIndex=0; sessionIndex < loggedInSessions.Size(); sessionIndex++) { if (loggedInSessions[sessionIndex].systemAddress==outputNode->packet->systemAddress && loggedInSessions[sessionIndex].sessionName==outputNode->dbIdentifier) { break; } } unsigned int idx; if (sessionManagementMode==USE_ANY_DB_HANDLE) { if (dbHandles.GetSize()>0) idx=0; else // #high - use a better approach than this static case here idx=static_cast(-1); } else { idx = dbHandles.GetIndexOf(outputNode->dbIdentifier); if (sessionIndex==loggedInSessions.Size() && (createDirectoryForFile==true || idx==-1) && sessionManagementMode==CREATE_EACH_NAMED_DB_HANDLE) { // Create it, and set idx to the new one idx = CreateDBHandle(outputNode->dbIdentifier); } else if (idx==-1) { if (sessionManagementMode==CREATE_EACH_NAMED_DB_HANDLE || sessionManagementMode==CREATE_SHARED_NAMED_DB_HANDLE) { // Create it, and set idx to the new one idx = CreateDBHandle(outputNode->dbIdentifier); } if (sessionManagementMode==USE_NAMED_DB_HANDLE) { RakAssert("Can't find named DB handle\n" && 0); } } else { // Use idx } } if (idx==-1) { DeallocPacketUnified(outputNode->packet); SLNet::OP_DELETE(outputNode,_FILE_AND_LINE_); } else { if (sessionIndex==loggedInSessions.Size()) { SessionNameAndSystemAddress sassy; sassy.sessionName=outputNode->dbIdentifier; sassy.systemAddress=outputNode->packet->systemAddress; sassy.referencedPointer=dbHandles[idx].dbHandle; SLNet::TimeMS curTime = SLNet::GetTimeMS(); SLNet::TimeMS dbAge = curTime - dbHandles[idx].whenCreated; // SLNet::TimeMS timeDelta = outputNode->clientSendingTime - curTime; sassy.timestampDelta=dbAge - outputNode->clientSendingTime ; // sassy.dbAgeWhenCreated=dbHandles[idx].whenCreated; loggedInSessions.Push(sassy, _FILE_AND_LINE_ ); sessionIndex=loggedInSessions.Size()-1; } DeallocPacketUnified(outputNode->packet); sqlThreadInput.dbHandle=dbHandles[idx].dbHandle; outputNode->clientSendingTime+=loggedInSessions[sessionIndex].timestampDelta; sqlLoggerThreadPool.AddInput(ExecSQLLoggingThread, sqlThreadInput); } } // SLNet::OP_DELETE_ARRAY(cpuThreadOutput->cpuOutputNodeArray); SLNet::OP_DELETE(cpuThreadOutput,_FILE_AND_LINE_); } while (sqlLoggerThreadPool.HasOutputFast() && sqlLoggerThreadPool.HasOutput()) { hadOutput=true; SLNet::OP_DELETE(sqlLoggerThreadPool.GetOutput().cpuOutputNode,_FILE_AND_LINE_); } if (hadOutput) CloseUnreferencedSessions(); } PluginReceiveResult SQLiteServerLoggerPlugin::OnReceive(Packet *packet) { PluginReceiveResult prr = SQLite3ServerPlugin::OnReceive(packet); if (prr!=RR_CONTINUE_PROCESSING) return prr; switch (packet->data[0]) { case ID_SQLLITE_LOGGER: { SLNet::BitStream bitStream(packet->data, packet->length, false); bitStream.IgnoreBytes(1); SLNet::RakString dbIdentifier; bitStream.Read(dbIdentifier); if (sessionManagementMode==CREATE_EACH_NAMED_DB_HANDLE) { unsigned char senderAddr[32]; packet->systemAddress.ToString(true,(char*) senderAddr, static_cast(32)); dbIdentifier+=':'; dbIdentifier+=senderAddr; } CPUThreadInput *ti = LockCpuThreadInput(); ti->cpuInputArray[ti->arraySize].packet=packet; // ti->cpuInputArray[ti->arraySize].whenMessageArrived=SLNet::GetTimeMS(); ti->cpuInputArray[ti->arraySize].dbIdentifier=dbIdentifier; UnlockCpuThreadInput(); /* unsigned int i; bool alreadyHasLoggedInSession=false; for (i=0; i < loggedInSessions.Size(); i++) { if (loggedInSessions[i].systemAddress==packet->systemAddress && loggedInSessions[i].sessionName==dbIdentifier) { alreadyHasLoggedInSession=true; break; } } unsigned int idx; if (sessionManagementMode==USE_ANY_DB_HANDLE) { if (dbHandles.GetSize()>0) idx=0; else idx=-1; } else { idx = dbHandles.GetIndexOf(dbIdentifier); if (alreadyHasLoggedInSession==false && (createDirectoryForFile==true || idx==-1) && sessionManagementMode==CREATE_EACH_NAMED_DB_HANDLE) { // Create it, and set idx to the new one idx = CreateDBHandle(dbIdentifier); } else if (idx==-1) { if (sessionManagementMode==CREATE_EACH_NAMED_DB_HANDLE || sessionManagementMode==CREATE_SHARED_NAMED_DB_HANDLE) { // Create it, and set idx to the new one idx = CreateDBHandle(dbIdentifier); } if (sessionManagementMode==USE_NAMED_DB_HANDLE) { RakAssert("Can't find named DB handle\n" && 0); } } else { // Use idx } } if (idx==-1) { return RR_STOP_PROCESSING_AND_DEALLOCATE; } if (alreadyHasLoggedInSession==false) { SessionNameAndSystemAddress sassy; sassy.sessionName=dbIdentifier; sassy.systemAddress=packet->systemAddress; sassy.referencedPointer=dbHandles[idx].dbHandle; loggedInSessions.Push(sassy); } SQLExecThreadInput input; input.dbHandle=dbHandles[idx].dbHandle; input.packet=packet; input.whenMessageArrived=SLNet::GetTimeMS()-dbHandles[idx].whenCreated; __sqlThreadPool.AddInput(ExecSQLLoggingThread, input); // printf("Pending Queries: %i\n", __sqlThreadPool.InputSize()); */ return RR_STOP_PROCESSING; } } return RR_CONTINUE_PROCESSING; } void SQLiteServerLoggerPlugin::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason ) { // unused parameters (void)rakNetGUID; (void)lostConnectionReason; SLNet::RakString removedSession; unsigned int i=0; while (i < loggedInSessions.Size()) { if (loggedInSessions[i].systemAddress==systemAddress) { removedSession=loggedInSessions[i].sessionName; loggedInSessions.RemoveAtIndexFast(i); /* unsigned int j; bool removedSessionReferenced=false; for (j=0; j < loggedInSessions.Size(); j++) { if (loggedInSessions[j].sessionName==removedSession) { removedSessionReferenced=true; break; } } if (removedSessionReferenced==false) { // if (__sqlThreadPool.InputSize()>0 || __sqlThreadPool.IsWorking()) // return; // RemoveDBHandle(removedSession, sessionManagementMode==CREATE_EACH_NAMED_DB_HANDLE||sessionManagementMode==CREATE_ONE_NAMED_DB_HANDLE); } */ } else i++; } CloseUnreferencedSessions(); } void SQLiteServerLoggerPlugin::CloseUnreferencedSessions(void) { DataStructures::List sessionNames; unsigned int j; for (j=0; j < loggedInSessions.Size(); j++) { if (sessionNames.GetIndexOf(loggedInSessions[j].referencedPointer)==-1) sessionNames.Push(loggedInSessions[j].referencedPointer, _FILE_AND_LINE_ ); } DataStructures::List unreferencedHandles; bool isReferenced; for (unsigned int i=0; i < dbHandles.GetSize(); i++) { if (dbHandles[i].dbAutoCreated) { j=0; isReferenced=false; for (j=0; j < sessionNames.Size(); j++) { if (sessionNames[j]==dbHandles[i].dbHandle) { isReferenced=true; break; } } if (isReferenced==false) { unreferencedHandles.Push(dbHandles[i].dbHandle,_FILE_AND_LINE_); } } } if (unreferencedHandles.Size()) { sqlLoggerThreadPool.LockInput(); if (sqlLoggerThreadPool.HasInputFast()==false) { RakSleep(100); while (sqlLoggerThreadPool.NumThreadsWorking()>0) RakSleep(30); for (unsigned int k=0; k < unreferencedHandles.Size(); k++) { RemoveDBHandle(unreferencedHandles[k], true); } } sqlLoggerThreadPool.UnlockInput(); if (dbHandles.GetSize()==0) StopCPUSQLThreads(); } } void SQLiteServerLoggerPlugin::CloseAllSessions(void) { loggedInSessions.Clear(false, _FILE_AND_LINE_); CloseUnreferencedSessions(); } unsigned int SQLiteServerLoggerPlugin::CreateDBHandle(SLNet::RakString dbIdentifier) { if (sessionManagementMode!=CREATE_EACH_NAMED_DB_HANDLE && sessionManagementMode!=CREATE_SHARED_NAMED_DB_HANDLE) return dbHandles.GetIndexOf(dbIdentifier); SLNet::RakString filePath = newDatabaseFilePath; if (createDirectoryForFile) { filePath+=dbIdentifier; filePath.TerminateAtLastCharacter('.'); filePath.MakeFilePath(); time_t now; struct tm ts; char buf[80]; /* Get the current time */ now = time(nullptr); /* Format and print the time, "ddd yyyy-mm-dd hh:mm:ss zzz" */ localtime_s(&ts, &now); strftime(buf, sizeof(buf), "__%a_%Y-%m-%d__%H;%M", &ts); filePath+=buf; filePath+= SLNet::RakString("__%i", SLNet::GetTimeMS()); filePath.MakeFilePath(); } // With no file data, just creates the directory structure WriteFileWithDirectories(filePath.C_String(), 0, 0); SLNet::RakString fileSafeDbIdentifier = dbIdentifier; fileSafeDbIdentifier.TerminateAtLastCharacter(':'); SLNet::RakString fileNameWithPath=filePath+fileSafeDbIdentifier; // SQL Open this file, and register it sqlite3 *database; if (sqlite3_open_v2(fileNameWithPath.C_String(), &database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0)!=SQLITE_OK) { RakAssert("sqlite3_open_v2 failed in SQLiteServerLoggerPlugin.cpp" && 0); // #high - change this - return value to int? return static_cast(-1); } if (AddDBHandle(dbIdentifier, database, true)) { char *errorMsg; int rc = sqlite3_exec(database,"PRAGMA synchronous=OFF", 0, 0, &errorMsg); RakAssert(rc==SQLITE_OK); sqlite3_free(errorMsg); rc = sqlite3_exec(database,"PRAGMA count_changes=OFF", 0, 0, &errorMsg); RakAssert(rc==SQLITE_OK); sqlite3_free(errorMsg); printf("Created %s\n", fileNameWithPath.C_String()); return dbHandles.GetIndexOf(dbIdentifier); } RakAssert("Failed to call AddDbHandle" && 0); // #high - change this - return value to int? return static_cast(-1); } void SQLiteServerLoggerPlugin::SetSessionManagementMode(SessionManagementMode _sessionManagementMode, bool _createDirectoryForFile, const char *_newDatabaseFilePath) { sessionManagementMode=_sessionManagementMode; createDirectoryForFile=_createDirectoryForFile; newDatabaseFilePath=_newDatabaseFilePath; newDatabaseFilePath.MakeFilePath(); } void SQLiteServerLoggerPlugin::OnShutdown(void) { CloseAllSessions(); } void SQLiteServerLoggerPlugin::StopCPUSQLThreads(void) { unsigned int i; int j,k; cpuLoggerThreadPool.StopThreads(); ClearCpuThreadInput(); for (i=0; i < cpuLoggerThreadPool.InputSize(); i++) { // #med - change class member - then revert name to cpuThradInput CPUThreadInput *curCPUThreadInput = cpuLoggerThreadPool.GetInputAtIndex(i); for (j=0; j < curCPUThreadInput->arraySize; j++) { DeallocPacketUnified(curCPUThreadInput->cpuInputArray[j].packet); } SLNet::OP_DELETE(curCPUThreadInput,_FILE_AND_LINE_); } cpuLoggerThreadPool.ClearInput(); for (i=0; i < cpuLoggerThreadPool.OutputSize(); i++) { CPUThreadOutput *cpuThreadOutput = cpuLoggerThreadPool.GetOutputAtIndex(i); for (j=0; j < cpuThreadOutput->arraySize; j++) { CPUThreadOutputNode *cpuThreadOutputNode = cpuThreadOutput->cpuOutputNodeArray[j]; DeallocPacketUnified(cpuThreadOutputNode->packet); for (k=0; k < cpuThreadOutputNode->parameterCount; k++) cpuThreadOutputNode->parameterList[k].Free(); SLNet::OP_DELETE(cpuThreadOutputNode,_FILE_AND_LINE_); } // SLNet::OP_DELETE_ARRAY(cpuThreadOutput->cpuOutputNodeArray,_FILE_AND_LINE_); SLNet::OP_DELETE(cpuThreadOutput,_FILE_AND_LINE_); } cpuLoggerThreadPool.ClearOutput(); sqlLoggerThreadPool.StopThreads(); for (i=0; i < sqlLoggerThreadPool.InputSize(); i++) SLNet::OP_DELETE(sqlLoggerThreadPool.GetInputAtIndex(i).cpuOutputNode,_FILE_AND_LINE_); sqlLoggerThreadPool.ClearInput(); for (i=0; i < sqlLoggerThreadPool.OutputSize(); i++) SLNet::OP_DELETE(sqlLoggerThreadPool.GetOutputAtIndex(i).cpuOutputNode,_FILE_AND_LINE_); sqlLoggerThreadPool.ClearOutput(); } void SQLiteServerLoggerPlugin::GetProcessingStatus(ProcessingStatus *processingStatus) { if (cpuThreadInput) processingStatus->packetsBuffered=cpuThreadInput->arraySize; else processingStatus->packetsBuffered=0; processingStatus->cpuPendingProcessing=cpuLoggerThreadPool.InputSize(); processingStatus->cpuProcessedAwaitingDeallocation=cpuLoggerThreadPool.OutputSize(); processingStatus->cpuNumThreadsWorking=cpuLoggerThreadPool.NumThreadsWorking(); processingStatus->sqlPendingProcessing=sqlLoggerThreadPool.InputSize(); processingStatus->sqlProcessedAwaitingDeallocation=sqlLoggerThreadPool.OutputSize(); processingStatus->sqlNumThreadsWorking=sqlLoggerThreadPool.NumThreadsWorking(); } SQLiteServerLoggerPlugin::CPUThreadInput *SQLiteServerLoggerPlugin::LockCpuThreadInput(void) { if (cpuThreadInput==0) { cpuThreadInput= SLNet::OP_NEW(_FILE_AND_LINE_); cpuThreadInput->arraySize=0; whenCpuThreadInputAllocated= SLNet::GetTimeMS(); } return cpuThreadInput; } void SQLiteServerLoggerPlugin::ClearCpuThreadInput(void) { if (cpuThreadInput!=0) { for (int i=0; i < cpuThreadInput->arraySize; i++) DeallocPacketUnified(cpuThreadInput->cpuInputArray[i].packet); SLNet::OP_DELETE(cpuThreadInput,_FILE_AND_LINE_); cpuThreadInput=0; } } void SQLiteServerLoggerPlugin::UnlockCpuThreadInput(void) { cpuThreadInput->arraySize++; if (cpuThreadInput->arraySize==MAX_PACKETS_PER_CPU_INPUT_THREAD) PushCpuThreadInput(); else PushCpuThreadInputIfNecessary(); } void SQLiteServerLoggerPlugin::CancelLockCpuThreadInput(void) { if (cpuThreadInput->arraySize==0) { SLNet::OP_DELETE(cpuThreadInput,_FILE_AND_LINE_); cpuThreadInput=0; } } void SQLiteServerLoggerPlugin::PushCpuThreadInput(void) { // cpu threads can probably be as many as I want if (cpuLoggerThreadPool.WasStarted()==false) { if (dxtCompressionEnabled) cpuLoggerThreadPool.StartThreads(1,0,InitDxt, DeinitDxt); else cpuLoggerThreadPool.StartThreads(1,0,0, 0); } // sql logger threads should probably be limited to 1 since I'm doing transaction locks and calling sqlite3_last_insert_rowid if (sqlLoggerThreadPool.WasStarted()==false) sqlLoggerThreadPool.StartThreads(1,0, SQLLoggerThreadAllocPreparedStatements, SQLLoggerThreadDeallocPreparedStatements); cpuLoggerThreadPool.AddInput(ExecCPULoggingThread, cpuThreadInput); cpuThreadInput=0; } void SQLiteServerLoggerPlugin::PushCpuThreadInputIfNecessary(void) { SLNet::TimeMS curTime = SLNet::GetTimeMS(); if (cpuThreadInput && curTime-whenCpuThreadInputAllocated>MAX_TIME_TO_BUFFER_PACKETS) PushCpuThreadInput(); } void SQLiteServerLoggerPlugin::OnAttach(void) { } void SQLiteServerLoggerPlugin::OnDetach(void) { } void SQLiteServerLoggerPlugin::SetEnableDXTCompression(bool enable) { dxtCompressionEnabled=enable; }