SQLite3Sample.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * Copyright (c) 2014, Oculus VR, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. /// \file
  11. /// \brief A sample for the SQLite3Plugin, that creates a table to track connections on the server
  12. /// The SQLite3Plugin is used with SQLite version 3 to transmit over the network calls to sqlite3_exec
  13. #include "RakPeerInterface.h"
  14. #include "SQLite3ServerPlugin.h"
  15. #include "SQLite3ClientPlugin.h"
  16. #include "BitStream.h"
  17. #include "RakSleep.h"
  18. #include "Gets.h"
  19. #include "Kbhit.h"
  20. #include "GetTime.h"
  21. using namespace RakNet;
  22. /// A sample derived implementation that will automatically update the table with all connected systems
  23. class ConnectionStatePlugin : public SQLite3ServerPlugin
  24. {
  25. public:
  26. ConnectionStatePlugin() {lastTimeRemovedDeadRows=0;}
  27. // Custom function to create the table we want
  28. // Assumes the database was already added with AddDBHandle
  29. bool CreateConnectionStateTable(RakNet::RakString dbIdentifier)
  30. {
  31. // dbHandles is a member variable of SQLite3Plugin and contains the mappings of identifiers to sql database pointers
  32. unsigned int idx = dbHandles.GetIndexOf(dbIdentifier);
  33. if (idx==(unsigned int)-1)
  34. return false;
  35. // Store the identifier for the connection state table for use in OnClosedConnection and OnNewConnection
  36. connectionStateIdentifier=dbIdentifier;
  37. // Create the table.
  38. sqlite3_exec(
  39. // Pointer to sqlite instance previously added with SQLite3Plugin::AddDBHandle()
  40. dbHandles[idx].dbHandle,
  41. // Query
  42. "CREATE TABLE connectionState(systemAddress varchar(64),"
  43. "rowCreationTime timestamp DATE DEFAULT (datetime('now','localtime')),"
  44. "lastRowUpdateTime timestamp DATE DEFAULT (datetime('now','localtime')),"
  45. "rakNetGUID varchar(64))",
  46. // Ignore per-row callback, callback parameter, and error message destination
  47. 0,0,0);
  48. return true;
  49. }
  50. // Implemented event callback from base class PluginInterface2
  51. virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
  52. {
  53. // Call down to the base class in case it does anything in the future (right now it does nothing)
  54. SQLite3ServerPlugin::OnClosedConnection(systemAddress, rakNetGUID, lostConnectionReason);
  55. // Get the database index associated with the table used for this class
  56. unsigned int idx = dbHandles.GetIndexOf(connectionStateIdentifier);
  57. if (idx==(unsigned int)-1)
  58. return;
  59. // Remove dropped system by primary key system address
  60. char systemAddressString[64];
  61. systemAddress.ToString(true,systemAddressString);
  62. RakNet::RakString query("DELETE FROM connectionState WHERE systemAddress='%s';",
  63. RakNet::RakString(systemAddressString).SQLEscape().C_String());
  64. sqlite3_exec(dbHandles[idx].dbHandle,query.C_String(),0,0,0);
  65. }
  66. // Implemented event callback from base class PluginInterface2
  67. virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming)
  68. {
  69. // Call down to the base class in case it does anything in the future (right now it does nothing)
  70. SQLite3ServerPlugin::OnNewConnection(systemAddress, rakNetGUID, isIncoming);
  71. // Get the database index associated with the table used for this class
  72. unsigned int idx = dbHandles.GetIndexOf(connectionStateIdentifier);
  73. if (idx==(unsigned int)-1)
  74. return;
  75. // Store new system's system address and guid. rowCreationTime column is created automatically
  76. char systemAddressString[64];
  77. systemAddress.ToString(true,systemAddressString);
  78. char guidString[128];
  79. rakNetGUID.ToString(guidString);
  80. RakNet::RakString query(
  81. "INSERT INTO connectionState (systemAddress,rakNetGUID) VALUES ('%s','%s');",
  82. RakNet::RakString(systemAddressString).SQLEscape().C_String(),
  83. RakNet::RakString(guidString).SQLEscape().C_String());
  84. sqlite3_exec(dbHandles[idx].dbHandle,query.C_String(),0,0,0);
  85. }
  86. // After I wrote this I realized it's not needed. ID_CONNECTION_LOST does it for us :)
  87. /*
  88. virtual void Update(void)
  89. {
  90. // Call down to the base class or the results of thread processing won't update
  91. SQLite3Plugin::Update();
  92. // Once a second, remove all rows whose timestamp has not been updated in the last 30 seconds
  93. RakNet::TimeMS curTime=RakNet::GetTimeMS();
  94. if (curTime > lastTimeRemovedDeadRows+1000 || curTime < lastTimeRemovedDeadRows) // < is to check overflow
  95. {
  96. lastTimeRemovedDeadRows = curTime;
  97. // Get the database index associated with the table used for this class
  98. unsigned int idx = dbHandles.GetIndexOf(connectionStateIdentifier);
  99. if (idx==(unsigned int)-1)
  100. return;
  101. sqlite3_exec(dbHandles[idx].dbHandle,"DELETE FROM connectionState WHERE lastRowUpdateTime<dateTime('now','localtime','-30 seconds');",0,0,0);
  102. }
  103. }
  104. */
  105. RakNet::RakString connectionStateIdentifier;
  106. RakNet::TimeMS lastTimeRemovedDeadRows;
  107. };
  108. int main(void)
  109. {
  110. printf("Demonstration of SQLite3Plugin.\n");
  111. printf("Allows you to send SQL queries to a remote system running SQLite3.\n");
  112. printf("System is a basis from which to add more functionality (security, etc.)\n");
  113. printf("Difficulty: Intermediate\n\n");
  114. RakNet::RakPeerInterface *rakClient=RakNet::RakPeerInterface::GetInstance();
  115. RakNet::RakPeerInterface *rakServer=RakNet::RakPeerInterface::GetInstance();
  116. // Client just needs the base class to do sends
  117. RakNet::SQLite3ClientPlugin sqlite3ClientPlugin;
  118. // Server uses our sample derived class to track logins
  119. ConnectionStatePlugin sqlite3ServerPlugin;
  120. // Default result handler to print what happens on the client
  121. SQLite3PluginResultInterface_Printf sqlite3ResultHandler;
  122. rakClient->AttachPlugin(&sqlite3ClientPlugin);
  123. rakServer->AttachPlugin(&sqlite3ServerPlugin);
  124. sqlite3ClientPlugin.AddResultHandler(&sqlite3ResultHandler);
  125. // Create a database, and tell the plugin about it
  126. sqlite3 *database;
  127. // Here :memory: means create the database in memory only.
  128. // Normally the first parameter refers to a path on the disk to the database file
  129. if (sqlite3_open_v2(":memory:", &database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0)!=SQLITE_OK)
  130. return 1;
  131. static const char* DATABASE_IDENTIFIER="ConnectionStateDBInMemory";
  132. sqlite3ServerPlugin.AddDBHandle(DATABASE_IDENTIFIER, database);
  133. sqlite3ServerPlugin.CreateConnectionStateTable(DATABASE_IDENTIFIER);
  134. // Start and connect RakNet as usual
  135. RakNet::SocketDescriptor socketDescriptor(10000,0);
  136. if (rakServer->Startup(1,&socketDescriptor, 1)!=RAKNET_STARTED)
  137. {
  138. printf("Start call failed!\n");
  139. return 0;
  140. }
  141. rakServer->SetMaximumIncomingConnections(1);
  142. socketDescriptor.port=0;
  143. rakClient->Startup(1, &socketDescriptor, 1);
  144. if (rakClient->Connect("127.0.0.1", 10000, 0, 0)!=RakNet::CONNECTION_ATTEMPT_STARTED)
  145. {
  146. printf("Connect call failed\n");
  147. return 0;
  148. }
  149. // Wait for the connection to complete
  150. RakSleep(500);
  151. printf("Enter QUIT to quit, anything else is sent as a query.\n");
  152. while (1)
  153. {
  154. if (kbhit())
  155. {
  156. printf("Enter query: ");
  157. char query[512];
  158. Gets(query,sizeof(query));
  159. if (stricmp(query, "QUIT")==0)
  160. {
  161. printf("Bye\n");
  162. break;
  163. }
  164. else
  165. {
  166. // Send a query to the database through RakNet
  167. // Result will be printed through SQLite3PluginResultInterface_Printf
  168. sqlite3ClientPlugin._sqlite3_exec(DATABASE_IDENTIFIER, query, HIGH_PRIORITY, RELIABLE_ORDERED, 0, rakClient->GetSystemAddressFromIndex(0));
  169. }
  170. }
  171. rakClient->DeallocatePacket(rakClient->Receive());
  172. rakServer->DeallocatePacket(rakServer->Receive());
  173. RakSleep(30);
  174. }
  175. rakClient->Shutdown(100,0);
  176. rakServer->Shutdown(100,0);
  177. RakNet::RakPeerInterface::DestroyInstance(rakClient);
  178. RakNet::RakPeerInterface::DestroyInstance(rakServer);
  179. sqlite3_close(database);
  180. return 0;
  181. }
粤ICP备19079148号