221 lines
8.3 KiB
C++
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;
|
|
}
|