forked from facebookarchive/RakNet
-
Notifications
You must be signed in to change notification settings - Fork 68
Expand file tree
/
Copy pathSQLite3Sample.cpp
More file actions
220 lines (186 loc) · 8.29 KB
/
SQLite3Sample.cpp
File metadata and controls
220 lines (186 loc) · 8.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/*
* 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;
}