Files
SLikeNet/DependentExtensions/SQLite3Plugin/Logger/ServerOnly/SQLiteServerLoggerPlugin.cpp
2025-11-24 14:19:51 +05:30

1336 lines
49 KiB
C++

/*
* 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 <time.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/timeb.h>
#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<SQLiteServerLoggerPlugin::CPUThreadOutput>(_FILE_AND_LINE_);
cpuThreadOutput->arraySize=cpuThreadInput->arraySize;
//cpuThreadOutput->cpuOutputNodeArray=SLNet::OP_NEW_ARRAY<SQLiteServerLoggerPlugin::CPUThreadOutputNode*>(cpuThreadInput->arraySize,_FILE_AND_LINE_);
//printf("1. arraySize=%i, ",cpuThreadInput->arraySize);
for (i=0; i<cpuThreadInput->arraySize; i++)
{
cpuThreadOutput->cpuOutputNodeArray[i]= SLNet::OP_NEW<SQLiteServerLoggerPlugin::CPUThreadOutputNode>(_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<size_t>(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<SQLPreparedStatements>(_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<SLNet::RakString> existingColumnNames;
DataStructures::List<SLNet::RakString> 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; parameterCountIndex<cpuOutputNode->parameterCount; 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; parameterCountIndex<cpuOutputNode->parameterCount+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; 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(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<unsigned int>(-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<size_t>(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<sqlite3 *> 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<sqlite3*> 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<unsigned int>(-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<unsigned int>(-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<CPUThreadInput>(_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;
}