RankingServerDBTest.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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. // Common includes
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include "Kbhit.h"
  14. #include "GetTime.h"
  15. #include "FunctionThread.h"
  16. #include "RankingServer_PostgreSQL.h"
  17. #ifdef _WIN32
  18. #include <windows.h> // Sleep
  19. #else
  20. #include <unistd.h> // usleep
  21. #endif
  22. // localtime
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <time.h>
  26. // Database queries are asynchronous so the results are read in Functor::HandleResult. The default implementation passes it to a callback
  27. class DBResultHandler : public RankingServerCBInterface
  28. {
  29. virtual void SubmitMatch_CB(SubmitMatch_PostgreSQLImpl *callResult, bool wasCancelled, void *context)
  30. {
  31. if (wasCancelled)
  32. printf("SubmitMatch call canceled:\n");
  33. else if (callResult->dbQuerySuccess==false)
  34. {
  35. printf("SubmitMatch call DB failure:\n");
  36. printf("%s", callResult->rankingServer->GetLastError());
  37. }
  38. else
  39. {
  40. printf("SubmitMatch Completed (No data is returned)");
  41. }
  42. printf("\n");
  43. }
  44. virtual void GetRatingForParticipant_CB(GetRatingForParticipant_PostgreSQLImpl *callResult, bool wasCancelled, void *context)
  45. {
  46. if (wasCancelled)
  47. printf("GetRatingForParticipant call cancelled:\n");
  48. else if (callResult->dbQuerySuccess==false)
  49. {
  50. printf("GetRatingForParticipant call DB failure:\n");
  51. printf("%s", callResult->rankingServer->GetLastError());
  52. }
  53. else
  54. {
  55. printf("GetRatingForParticipant result:\n");
  56. printf("[in] participantDbId.primaryKey=%i\n", callResult->participantDbId.primaryKey);
  57. printf("[in] participantDbId.secondaryKey=%i\n", callResult->participantDbId.secondaryKey);
  58. printf("[in] gameDbId.primaryKey=%i\n", callResult->gameDbId.primaryKey);
  59. printf("[in] gameDbId.secondaryKey=%i\n", callResult->gameDbId.secondaryKey);
  60. printf("[out] participantFound=%i\n", callResult->participantFound); // False means no such participant
  61. if (callResult->participantFound)
  62. printf("[out] rating=%f\n", callResult->rating);
  63. }
  64. printf("\n");
  65. }
  66. virtual void GetRatingForParticipants_CB(GetRatingForParticipants_PostgreSQLImpl *callResult, bool wasCancelled, void *context)
  67. {
  68. if (wasCancelled)
  69. printf("GetRatingForParticipants call canceled:\n");
  70. else if (callResult->dbQuerySuccess==false)
  71. {
  72. printf("GetRatingForParticipants call DB failure:\n");
  73. printf("%s", callResult->rankingServer->GetLastError());
  74. }
  75. else
  76. {
  77. printf("GetRatingForParticipants result:\n");
  78. printf("[in] gameDbId.primaryKey=%i\n", callResult->gameDbId.primaryKey);
  79. printf("[in] gameDbId.secondaryKey=%i\n", callResult->gameDbId.secondaryKey);
  80. printf("[out] %i participants found.\n", callResult->outputList.Size());
  81. unsigned i;
  82. for (i=0; i < callResult->outputList.Size(); i++)
  83. {
  84. printf("[out] %i. participantDbId.primaryKey=%i\n", i+1, callResult->outputList[i].participantDbId.primaryKey);
  85. printf("[out] %i. participantDbId.secondaryKey=%i\n", i+1, callResult->outputList[i].participantDbId.secondaryKey);
  86. printf("[out] %i. newRating (most recent rating)=%f\n", i+1, callResult->outputList[i].newRating);
  87. }
  88. }
  89. printf("\n");
  90. }
  91. virtual void GetHistoryForParticipant_CB(GetHistoryForParticipant_PostgreSQLImpl *callResult, bool wasCancelled, void *context)
  92. {
  93. if (wasCancelled)
  94. printf("GetHistoryForParticipant call cancelled:\n");
  95. else if (callResult->dbQuerySuccess==false)
  96. {
  97. printf("GetHistoryForParticipant call DB failure:\n");
  98. printf("%s", callResult->rankingServer->GetLastError());
  99. }
  100. else
  101. {
  102. printf("GetHistoryForParticipant result:\n");
  103. printf("[in] participantDbId.primaryKey=%i\n", callResult->participantDbId.primaryKey);
  104. printf("[in] participantDbId.secondaryKey=%i\n", callResult->participantDbId.secondaryKey);
  105. printf("[in] gameDbId.primaryKey=%i\n", callResult->gameDbId.primaryKey);
  106. printf("[in] gameDbId.secondaryKey=%i\n", callResult->gameDbId.secondaryKey);
  107. printf("[out] %i matches found for this participant.\n", callResult->matchHistoryList.Size());
  108. unsigned i;
  109. for (i=0; i < callResult->matchHistoryList.Size(); i++)
  110. {
  111. printf("[out] %i. participantBDbId.primaryKey (opponent) =%i\n", i+1, callResult->matchHistoryList[i]->participantBDbId.primaryKey);
  112. printf("[out] %i. participantBDbId.secondaryKey (opponent) =%i\n", i+1, callResult->matchHistoryList[i]->participantBDbId.secondaryKey);
  113. printf("[out] %i. participantAScore (us) =%f\n", i+1, callResult->matchHistoryList[i]->participantAScore);
  114. printf("[out] %i. participantBScore (opponent) =%f\n", i+1, callResult->matchHistoryList[i]->participantBScore);
  115. printf("[out] %i. participantAOldRating (us) =%f\n", i+1, callResult->matchHistoryList[i]->participantAOldRating);
  116. printf("[out] %i. participantANewRating (us) =%f\n", i+1, callResult->matchHistoryList[i]->participantANewRating);
  117. printf("[out] %i. participantBOldRating (opponent) =%f\n", i+1, callResult->matchHistoryList[i]->participantBOldRating);
  118. printf("[out] %i. participantBNewRating (opponent) =%f\n", i+1, callResult->matchHistoryList[i]->participantBNewRating);
  119. printf("[out] %i. Match Notes=%s\n", i+1, callResult->matchHistoryList[i]->matchNotes.C_String());
  120. printf("[out] %i. matchBinaryDataLength=%i\n", i+1, callResult->matchHistoryList[i]->matchBinaryDataLength);
  121. // Copied from the docs
  122. struct tm *newtime;
  123. newtime = _localtime64(& callResult->matchHistoryList[i]->matchTime);
  124. char buff[30];
  125. asctime_s( buff, sizeof(buff), newtime );
  126. printf("[out] %i. matchTime (converted) =\n%s\n", i+1, buff );
  127. }
  128. }
  129. printf("\n");
  130. }
  131. };
  132. void main(int argc, char **argv)
  133. {
  134. printf("A sample on using the provided implementation\nof the Ranking Server specification based on PostgreSQL\n");
  135. printf("Unlike the other samples, this is a server-only\nprocess so does not involve networking with RakNet.\n");
  136. printf("The goal of this class is to allow you to save\nand retrieve a history of matches for a given participant and game ID\n");
  137. printf("Difficulty: Intermediate\n\n");
  138. // A function thread is class that spawns a thread that operates on functors.
  139. // A functor is an instance of a class that has two pre-defined functions: One to perform processing, another to get the result.
  140. // One per application is enough to not block
  141. RakNet::FunctionThread ft;
  142. // Start one thread. Starting more than one may be advantageous for multi-core processors. However, in this scenario we are
  143. // blocking on database calls rather than processing.
  144. ft.StartThreads(1);
  145. // The real functionality of the RankingServer is contained in the functors defined in RankingServer_PostgreSQL.h/.cpp.
  146. // However, this class contains some utility functions and members, such as the functionThread and a pointer to
  147. // the postgreSQL interface.
  148. RankingServer_PostgreSQL rankingServer;
  149. // RankingServer_PostgreSQL internally uses a functionThread so that database queries can happen asynchronously,
  150. // as opposed to blocking and slowing down the game.
  151. // If you don't assign one it will create one automatically and start it with one thread.
  152. rankingServer.AssignFunctionThread(&ft);
  153. // The default implementation of the functors pass Functor::HandleResult through to a callback, instantiated here.
  154. // Alternatively, you could derive from the *_PostgreSQLImpl functors found in RankingServer_PostgreSQL.h/.cpp
  155. // and override the behavior of Functor::HandleResult
  156. DBResultHandler resultHandler;
  157. rankingServer.AssignCallback(&resultHandler);
  158. printf("Enter database password:\n");
  159. char connectionString[256],password[128];
  160. char username[256];
  161. strcpy(username, "postgres");
  162. gets(password);
  163. strcpy(connectionString, "user=");
  164. strcat(connectionString, username);
  165. strcat(connectionString, " password=");
  166. strcat(connectionString, password);
  167. // database=blah
  168. if (rankingServer.Connect(connectionString)==false)
  169. {
  170. printf("Database connection failed.\n");
  171. return;
  172. }
  173. printf("Database connection suceeded.\n");
  174. printf("(D)rop tables\n"
  175. "(C)reate tables.\n"
  176. "(S)ubmit a match\n"
  177. "Get (R)ating for one participant.\n"
  178. "Get ratings for (A)ll participants\n"
  179. "Get match (H)istory for one participant\n"
  180. "(Q)uit\n");
  181. char inputStr[512];
  182. char ch;
  183. while (1)
  184. {
  185. if (kbhit())
  186. {
  187. ch=getch();
  188. if (ch=='q')
  189. break;
  190. else if (ch=='c')
  191. {
  192. if (rankingServer.CreateRankingServerTables()==false)
  193. printf("%s", rankingServer.GetLastError());
  194. else
  195. printf("Tables created.\n");
  196. }
  197. else if (ch=='d')
  198. {
  199. if (rankingServer.DestroyRankingServerTables()==false)
  200. printf("%s", rankingServer.GetLastError());
  201. else
  202. printf("Tables dropped.\n");
  203. }
  204. else if (ch=='s')
  205. {
  206. // We could do new and delete, but this ensures the class is allocated and deallocated in the same DLL, should we use one.
  207. // By default deallocation takes place in HandleResult()
  208. SubmitMatch_PostgreSQLImpl *functor = SubmitMatch_PostgreSQLImpl::Alloc();
  209. printf("Submit a match\n");
  210. /// Every participant is represented by a pair of numbers.
  211. /// The primary ID can be used to reference a database primary key referring to another table holding all participant
  212. /// The secondary ID can be used to indicate type
  213. /// such as indicating the type of participant (player, squad, guild, clan, etc).
  214. printf("Enter the primary ID of participant A (int): ");
  215. gets(inputStr);
  216. if (inputStr[0]==0) strcpy(inputStr, "0");
  217. functor->participantADbId.primaryKey = atoi(inputStr);
  218. printf("Enter the secondary ID of participant A (int): ");
  219. gets(inputStr);
  220. if (inputStr[0]==0) strcpy(inputStr, "1");
  221. functor->participantADbId.secondaryKey = atoi(inputStr);
  222. printf("Enter the primary ID of participant B (int): ");
  223. gets(inputStr);
  224. if (inputStr[0]==0) strcpy(inputStr, "2");
  225. functor->participantBDbId.primaryKey = atoi(inputStr);
  226. printf("Enter the secondary ID of participant B (int): ");
  227. gets(inputStr);
  228. if (inputStr[0]==0) strcpy(inputStr, "3");
  229. functor->participantBDbId.secondaryKey = atoi(inputStr);
  230. // Score is tracked in case you want to check the database for cheaters, or to modify ratings, etc.
  231. // If you don't care about it, just use any number.
  232. printf("Enter the score of participant A (float): ");
  233. gets(inputStr);
  234. if (inputStr[0]==0) strcpy(inputStr, "10.0");
  235. functor->participantAScore = (float) atof(inputStr);
  236. printf("Enter the score of participant B (float): ");
  237. gets(inputStr);
  238. if (inputStr[0]==0) strcpy(inputStr, "21.0");
  239. functor->participantBScore = (float) atof(inputStr);
  240. // The rating of the player before the match started. Elo.h/.cpp is provided as one way to calculate ratings
  241. printf("Enter the pre-match rating of participant A (float): ");
  242. gets(inputStr);
  243. if (inputStr[0]==0) strcpy(inputStr, "12.0");
  244. functor->participantAOldRating = (float) atof(inputStr);
  245. printf("Enter the post-match rating of participant A (float): ");
  246. gets(inputStr);
  247. if (inputStr[0]==0) strcpy(inputStr, "5.0");
  248. functor->participantANewRating = (float) atof(inputStr);
  249. printf("Enter the pre-match rating of participant B (float): ");
  250. gets(inputStr);
  251. if (inputStr[0]==0) strcpy(inputStr, "20.0");
  252. functor->participantBOldRating = (float) atof(inputStr);
  253. printf("Enter the post-match rating of participant B (float): ");
  254. gets(inputStr);
  255. if (inputStr[0]==0) strcpy(inputStr, "30.0");
  256. functor->participantBNewRating = (float) atof(inputStr);
  257. /// Games are identified by a pair of integers the same way participants are
  258. /// The primary key could be the game title (such as Asteroids) while the secondary key could
  259. /// Indicate a submode (such as cooperative or deathmatch)
  260. printf("Enter the primary ID of the game (int): ");
  261. gets(inputStr);
  262. if (inputStr[0]==0) strcpy(inputStr, "12");
  263. functor->gameDbId.primaryKey = atoi(inputStr);
  264. printf("Enter the secondary ID of the game (int): ");
  265. gets(inputStr);
  266. if (inputStr[0]==0) strcpy(inputStr, "13");
  267. functor->gameDbId.secondaryKey = atoi(inputStr);
  268. /// A string is stored with each match.
  269. printf("Enter the match notes: ");
  270. gets(inputStr);
  271. if (inputStr[0]==0) strcpy(inputStr, "Default Match Notes");
  272. functor->matchNotes=inputStr;
  273. printf("Store test binary data? (y/n): ");
  274. gets(inputStr);
  275. if (inputStr[0]!='n')
  276. {
  277. const char * data = "Default Match Binary Data";
  278. functor->matchBinaryDataLength=(int) strlen(data)+1;
  279. // As before, calling a function in the external code in case it was built to a DLL
  280. functor->matchBinaryData = SubmitMatch_PostgreSQLImpl::AllocBytes(functor->matchBinaryDataLength);
  281. memcpy(functor->matchBinaryData, data, functor->matchBinaryDataLength);
  282. }
  283. /// Puts this functor on the processing queue. It will process sometime later in a thread.
  284. rankingServer.PushFunctor(functor);
  285. }
  286. else if (ch=='r')
  287. {
  288. // We could do new and delete, but this ensures the class is allocated and deallocated in the same DLL, should we use one.
  289. // By default deallocation takes place in HandleResult()
  290. GetRatingForParticipant_PostgreSQLImpl *functor = GetRatingForParticipant_PostgreSQLImpl::Alloc();
  291. printf("Get the most recent rating for a participant\n");
  292. printf("Enter the primary ID of participant A (int): ");
  293. gets(inputStr);
  294. if (inputStr[0]==0) strcpy(inputStr, "0");
  295. functor->participantDbId.primaryKey = atoi(inputStr);
  296. printf("Enter the secondary ID of participant A (int): ");
  297. gets(inputStr);
  298. if (inputStr[0]==0) strcpy(inputStr, "1");
  299. functor->participantDbId.secondaryKey = atoi(inputStr);
  300. printf("Enter the primary ID of the game (int): ");
  301. gets(inputStr);
  302. if (inputStr[0]==0) strcpy(inputStr, "12");
  303. functor->gameDbId.primaryKey = atoi(inputStr);
  304. printf("Enter the secondary ID of the game (int): ");
  305. gets(inputStr);
  306. if (inputStr[0]==0) strcpy(inputStr, "13");
  307. functor->gameDbId.secondaryKey = atoi(inputStr);
  308. /// Puts this functor on the processing queue. It will process sometime later in a thread.
  309. rankingServer.PushFunctor(functor);
  310. }
  311. else if (ch=='a')
  312. {
  313. // We could do new and delete, but this ensures the class is allocated and deallocated in the same DLL, should we use one.
  314. // By default deallocation takes place in HandleResult()
  315. GetRatingForParticipants_PostgreSQLImpl *functor = GetRatingForParticipants_PostgreSQLImpl::Alloc();
  316. printf("Get the most recent rating for all participants\n");
  317. printf("Enter the primary ID of the game (int): ");
  318. gets(inputStr);
  319. if (inputStr[0]==0) strcpy(inputStr, "12");
  320. functor->gameDbId.primaryKey = atoi(inputStr);
  321. printf("Enter the secondary ID of the game (int): ");
  322. gets(inputStr);
  323. if (inputStr[0]==0) strcpy(inputStr, "13");
  324. functor->gameDbId.secondaryKey = atoi(inputStr);
  325. /// Puts this functor on the processing queue. It will process sometime later in a thread.
  326. rankingServer.PushFunctor(functor);
  327. }
  328. else if (ch=='h')
  329. {
  330. // We could do new and delete, but this ensures the class is allocated and deallocated in the same DLL, should we use one.
  331. // By default deallocation takes place in HandleResult()
  332. GetHistoryForParticipant_PostgreSQLImpl *functor = GetHistoryForParticipant_PostgreSQLImpl::Alloc();
  333. printf("Get the match history for a participant\n");
  334. printf("Enter the primary ID of participant A (int): ");
  335. gets(inputStr);
  336. if (inputStr[0]==0) strcpy(inputStr, "0");
  337. functor->participantDbId.primaryKey = atoi(inputStr);
  338. printf("Enter the secondary ID of participant A (int): ");
  339. gets(inputStr);
  340. if (inputStr[0]==0) strcpy(inputStr, "1");
  341. functor->participantDbId.secondaryKey = atoi(inputStr);
  342. printf("Enter the primary ID of the game (int): ");
  343. gets(inputStr);
  344. if (inputStr[0]==0) strcpy(inputStr, "12");
  345. functor->gameDbId.primaryKey = atoi(inputStr);
  346. printf("Enter the secondary ID of the game (int): ");
  347. gets(inputStr);
  348. if (inputStr[0]==0) strcpy(inputStr, "13");
  349. functor->gameDbId.secondaryKey = atoi(inputStr);
  350. /// Puts this functor on the processing queue. It will process sometime later in a thread.
  351. rankingServer.PushFunctor(functor);
  352. }
  353. }
  354. // Causes Functor::HandleResult calls. If this is forgotten you won't get processing result calls.
  355. ft.CallResultHandlers();
  356. Sleep(30);
  357. }
  358. }
粤ICP备19079148号