Files
2025-11-24 14:19:51 +05:30

221 lines
8.3 KiB
C++

/*
* Original work: Copyright (c) 2014, Oculus VR, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* RakNet License.txt file in the licenses directory of this source tree. An additional grant
* of patent rights can be found in the RakNet Patents.txt file in the same directory.
*
*
* Modified work: Copyright (c) 2016-2018, 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.
*/
/// \file
/// \brief A sample for the SQLite3Plugin, that creates a table to track connections on the server
/// The SQLite3Plugin is used with SQLite version 3 to transmit over the network calls to sqlite3_exec
#include "slikenet/peerinterface.h"
#include "SQLite3ServerPlugin.h"
#include "SQLite3ClientPlugin.h"
#include "slikenet/BitStream.h"
#include "slikenet/sleep.h"
#include "slikenet/Gets.h"
#include "slikenet/Kbhit.h"
#include "slikenet/GetTime.h"
using namespace SLNet;
/// A sample derived implementation that will automatically update the table with all connected systems
class ConnectionStatePlugin : public SQLite3ServerPlugin
{
public:
ConnectionStatePlugin() {lastTimeRemovedDeadRows=0;}
// Custom function to create the table we want
// Assumes the database was already added with AddDBHandle
bool CreateConnectionStateTable(SLNet::RakString dbIdentifier)
{
// dbHandles is a member variable of SQLite3Plugin and contains the mappings of identifiers to sql database pointers
unsigned int idx = dbHandles.GetIndexOf(dbIdentifier);
if (idx==(unsigned int)-1)
return false;
// Store the identifier for the connection state table for use in OnClosedConnection and OnNewConnection
connectionStateIdentifier=dbIdentifier;
// Create the table.
sqlite3_exec(
// Pointer to sqlite instance previously added with SQLite3Plugin::AddDBHandle()
dbHandles[idx].dbHandle,
// Query
"CREATE TABLE connectionState(systemAddress varchar(64),"
"rowCreationTime timestamp DATE DEFAULT (datetime('now','localtime')),"
"lastRowUpdateTime timestamp DATE DEFAULT (datetime('now','localtime')),"
"rakNetGUID varchar(64))",
// Ignore per-row callback, callback parameter, and error message destination
0,0,0);
return true;
}
// Implemented event callback from base class PluginInterface2
virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
{
// Call down to the base class in case it does anything in the future (right now it does nothing)
SQLite3ServerPlugin::OnClosedConnection(systemAddress, rakNetGUID, lostConnectionReason);
// Get the database index associated with the table used for this class
unsigned int idx = dbHandles.GetIndexOf(connectionStateIdentifier);
if (idx==(unsigned int)-1)
return;
// Remove dropped system by primary key system address
char systemAddressString[64];
systemAddress.ToString(true,systemAddressString,static_cast<size_t>(64));
SLNet::RakString query("DELETE FROM connectionState WHERE systemAddress='%s';",
SLNet::RakString(systemAddressString).SQLEscape().C_String());
sqlite3_exec(dbHandles[idx].dbHandle,query.C_String(),0,0,0);
}
// Implemented event callback from base class PluginInterface2
virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming)
{
// Call down to the base class in case it does anything in the future (right now it does nothing)
SQLite3ServerPlugin::OnNewConnection(systemAddress, rakNetGUID, isIncoming);
// Get the database index associated with the table used for this class
unsigned int idx = dbHandles.GetIndexOf(connectionStateIdentifier);
if (idx==(unsigned int)-1)
return;
// Store new system's system address and guid. rowCreationTime column is created automatically
char systemAddressString[64];
systemAddress.ToString(true,systemAddressString,static_cast<size_t>(64));
char guidString[128];
rakNetGUID.ToString(guidString, 64);
SLNet::RakString query(
"INSERT INTO connectionState (systemAddress,rakNetGUID) VALUES ('%s','%s');",
SLNet::RakString(systemAddressString).SQLEscape().C_String(),
SLNet::RakString(guidString).SQLEscape().C_String());
sqlite3_exec(dbHandles[idx].dbHandle,query.C_String(),0,0,0);
}
// After I wrote this I realized it's not needed. ID_CONNECTION_LOST does it for us :)
/*
virtual void Update(void)
{
// Call down to the base class or the results of thread processing won't update
SQLite3Plugin::Update();
// Once a second, remove all rows whose timestamp has not been updated in the last 30 seconds
SLNet::TimeMS curTime=SLNet::GetTimeMS();
if (curTime > lastTimeRemovedDeadRows+1000 || curTime < lastTimeRemovedDeadRows) // < is to check overflow
{
lastTimeRemovedDeadRows = curTime;
// Get the database index associated with the table used for this class
unsigned int idx = dbHandles.GetIndexOf(connectionStateIdentifier);
if (idx==(unsigned int)-1)
return;
sqlite3_exec(dbHandles[idx].dbHandle,"DELETE FROM connectionState WHERE lastRowUpdateTime<dateTime('now','localtime','-30 seconds');",0,0,0);
}
}
*/
SLNet::RakString connectionStateIdentifier;
SLNet::TimeMS lastTimeRemovedDeadRows;
};
int main(void)
{
printf("Demonstration of SQLite3Plugin.\n");
printf("Allows you to send SQL queries to a remote system running SQLite3.\n");
printf("System is a basis from which to add more functionality (security, etc.)\n");
printf("Difficulty: Intermediate\n\n");
SLNet::RakPeerInterface *rakClient= SLNet::RakPeerInterface::GetInstance();
SLNet::RakPeerInterface *rakServer= SLNet::RakPeerInterface::GetInstance();
// Client just needs the base class to do sends
SLNet::SQLite3ClientPlugin sqlite3ClientPlugin;
// Server uses our sample derived class to track logins
ConnectionStatePlugin sqlite3ServerPlugin;
// Default result handler to print what happens on the client
SQLite3PluginResultInterface_Printf sqlite3ResultHandler;
rakClient->AttachPlugin(&sqlite3ClientPlugin);
rakServer->AttachPlugin(&sqlite3ServerPlugin);
sqlite3ClientPlugin.AddResultHandler(&sqlite3ResultHandler);
// Create a database, and tell the plugin about it
sqlite3 *database;
// Here :memory: means create the database in memory only.
// Normally the first parameter refers to a path on the disk to the database file
if (sqlite3_open_v2(":memory:", &database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0)!=SQLITE_OK)
return 1;
static const char* DATABASE_IDENTIFIER="ConnectionStateDBInMemory";
sqlite3ServerPlugin.AddDBHandle(DATABASE_IDENTIFIER, database);
sqlite3ServerPlugin.CreateConnectionStateTable(DATABASE_IDENTIFIER);
// Start and connect RakNet as usual
SLNet::SocketDescriptor socketDescriptor(10000,0);
if (rakServer->Startup(1,&socketDescriptor, 1)!=RAKNET_STARTED)
{
printf("Start call failed!\n");
return 0;
}
rakServer->SetMaximumIncomingConnections(1);
socketDescriptor.port=0;
rakClient->Startup(1, &socketDescriptor, 1);
if (rakClient->Connect("127.0.0.1", 10000, 0, 0)!= SLNet::CONNECTION_ATTEMPT_STARTED)
{
printf("Connect call failed\n");
return 0;
}
// Wait for the connection to complete
RakSleep(500);
printf("Enter QUIT to quit, anything else is sent as a query.\n");
for(;;)
{
if (_kbhit())
{
printf("Enter query: ");
char query[512];
Gets(query,sizeof(query));
if (_stricmp(query, "QUIT")==0)
{
printf("Bye\n");
break;
}
else
{
// Send a query to the database through RakNet
// Result will be printed through SQLite3PluginResultInterface_Printf
sqlite3ClientPlugin._sqlite3_exec(DATABASE_IDENTIFIER, query, HIGH_PRIORITY, RELIABLE_ORDERED, 0, rakClient->GetSystemAddressFromIndex(0));
}
}
rakClient->DeallocatePacket(rakClient->Receive());
rakServer->DeallocatePacket(rakServer->Receive());
RakSleep(30);
}
rakClient->Shutdown(100,0);
rakServer->Shutdown(100,0);
SLNet::RakPeerInterface::DestroyInstance(rakClient);
SLNet::RakPeerInterface::DestroyInstance(rakServer);
sqlite3_close(database);
return 0;
}