LobbyDatabase.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. /*
  2. * LobbyServer.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "LobbyDatabase.h"
  12. #include "SQLiteConnection.h"
  13. void LobbyDatabase::createTables()
  14. {
  15. static const std::string createChatMessages = R"(
  16. CREATE TABLE IF NOT EXISTS chatMessages (
  17. id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  18. senderName TEXT,
  19. channelType TEXT,
  20. channelName TEXT,
  21. messageText TEXT,
  22. creationTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
  23. );
  24. )";
  25. static const std::string createTableGameRooms = R"(
  26. CREATE TABLE IF NOT EXISTS gameRooms (
  27. id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  28. roomID TEXT,
  29. hostAccountID TEXT,
  30. description TEXT NOT NULL DEFAULT '',
  31. status INTEGER NOT NULL DEFAULT 0,
  32. playerLimit INTEGER NOT NULL,
  33. creationTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
  34. );
  35. )";
  36. static const std::string createTableGameRoomPlayers = R"(
  37. CREATE TABLE IF NOT EXISTS gameRoomPlayers (
  38. id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  39. roomID TEXT,
  40. accountID TEXT
  41. );
  42. )";
  43. static const std::string createTableAccounts = R"(
  44. CREATE TABLE IF NOT EXISTS accounts (
  45. id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  46. accountID TEXT,
  47. displayName TEXT,
  48. online INTEGER NOT NULL,
  49. lastLoginTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
  50. creationTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
  51. );
  52. )";
  53. static const std::string createTableAccountCookies = R"(
  54. CREATE TABLE IF NOT EXISTS accountCookies (
  55. id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  56. accountID TEXT,
  57. cookieUUID TEXT,
  58. creationTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
  59. );
  60. )";
  61. static const std::string createTableGameRoomInvites = R"(
  62. CREATE TABLE IF NOT EXISTS gameRoomInvites (
  63. id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  64. roomID TEXT,
  65. accountID TEXT
  66. );
  67. )";
  68. database->prepare(createChatMessages)->execute();
  69. database->prepare(createTableGameRoomPlayers)->execute();
  70. database->prepare(createTableGameRooms)->execute();
  71. database->prepare(createTableAccounts)->execute();
  72. database->prepare(createTableAccountCookies)->execute();
  73. database->prepare(createTableGameRoomInvites)->execute();
  74. }
  75. void LobbyDatabase::clearOldData()
  76. {
  77. static const std::string removeActiveAccounts = R"(
  78. UPDATE accounts
  79. SET online = 0
  80. WHERE online <> 0
  81. )";
  82. //FIXME: set different status for rooms that never reached in game state
  83. static const std::string removeActiveRooms = R"(
  84. UPDATE gameRooms
  85. SET status = 5
  86. WHERE status <> 5
  87. )";
  88. database->prepare(removeActiveAccounts)->execute();
  89. database->prepare(removeActiveRooms)->execute();
  90. }
  91. void LobbyDatabase::prepareStatements()
  92. {
  93. // INSERT INTO
  94. insertChatMessageStatement = database->prepare(R"(
  95. INSERT INTO chatMessages(senderName, messageText, channelType, channelName) VALUES( ?, ?, ?, ?);
  96. )");
  97. insertAccountStatement = database->prepare(R"(
  98. INSERT INTO accounts(accountID, displayName, online) VALUES(?,?,0);
  99. )");
  100. insertAccessCookieStatement = database->prepare(R"(
  101. INSERT INTO accountCookies(accountID, cookieUUID) VALUES(?,?);
  102. )");
  103. insertGameRoomStatement = database->prepare(R"(
  104. INSERT INTO gameRooms(roomID, hostAccountID, status, playerLimit) VALUES(?, ?, 0, 8);
  105. )");
  106. insertGameRoomPlayersStatement = database->prepare(R"(
  107. INSERT INTO gameRoomPlayers(roomID, accountID) VALUES(?,?);
  108. )");
  109. insertGameRoomInvitesStatement = database->prepare(R"(
  110. INSERT INTO gameRoomInvites(roomID, accountID) VALUES(?,?);
  111. )");
  112. // DELETE FROM
  113. deleteGameRoomPlayersStatement = database->prepare(R"(
  114. DELETE FROM gameRoomPlayers WHERE roomID = ? AND accountID = ?
  115. )");
  116. // UPDATE
  117. setAccountOnlineStatement = database->prepare(R"(
  118. UPDATE accounts
  119. SET online = ?
  120. WHERE accountID = ?
  121. )");
  122. setGameRoomStatusStatement = database->prepare(R"(
  123. UPDATE gameRooms
  124. SET status = ?
  125. WHERE roomID = ?
  126. )");
  127. updateAccountLoginTimeStatement = database->prepare(R"(
  128. UPDATE accounts
  129. SET lastLoginTime = CURRENT_TIMESTAMP
  130. WHERE accountID = ?
  131. )");
  132. updateRoomDescriptionStatement = database->prepare(R"(
  133. UPDATE gameRooms
  134. SET description = ?
  135. WHERE roomID = ?
  136. )");
  137. updateRoomPlayerLimitStatement = database->prepare(R"(
  138. UPDATE gameRooms
  139. SET playerLimit = ?
  140. WHERE roomID = ?
  141. )");
  142. // SELECT FROM
  143. getRecentMessageHistoryStatement = database->prepare(R"(
  144. SELECT senderName, displayName, messageText, strftime('%s',CURRENT_TIMESTAMP)- strftime('%s',cm.creationTime) AS secondsElapsed
  145. FROM chatMessages cm
  146. LEFT JOIN accounts on accountID = senderName
  147. WHERE secondsElapsed < 60*60*18 AND channelType = ? AND channelName = ?
  148. ORDER BY cm.creationTime DESC
  149. LIMIT 100
  150. )");
  151. getFullMessageHistoryStatement = database->prepare(R"(
  152. SELECT senderName, displayName, messageText, strftime('%s',CURRENT_TIMESTAMP)- strftime('%s',cm.creationTime) AS secondsElapsed
  153. FROM chatMessages cm
  154. LEFT JOIN accounts on accountID = senderName
  155. WHERE channelType = ? AND channelName = ?
  156. ORDER BY cm.creationTime DESC
  157. )");
  158. getIdleGameRoomStatement = database->prepare(R"(
  159. SELECT roomID
  160. FROM gameRooms
  161. WHERE hostAccountID = ? AND status = 0
  162. LIMIT 1
  163. )");
  164. getGameRoomStatusStatement = database->prepare(R"(
  165. SELECT status
  166. FROM gameRooms
  167. WHERE roomID = ?
  168. )");
  169. getAccountGameHistoryStatement = database->prepare(R"(
  170. SELECT gr.roomID, hostAccountID, displayName, description, status, playerLimit, strftime('%s',CURRENT_TIMESTAMP)- strftime('%s',gr.creationTime) AS secondsElapsed
  171. FROM gameRoomPlayers grp
  172. LEFT JOIN gameRooms gr ON gr.roomID = grp.roomID
  173. LEFT JOIN accounts a ON gr.hostAccountID = a.accountID
  174. WHERE grp.accountID = ? AND status IN (4,5)
  175. ORDER BY secondsElapsed ASC
  176. )");
  177. getAccountGameRoomStatement = database->prepare(R"(
  178. SELECT grp.roomID
  179. FROM gameRoomPlayers grp
  180. LEFT JOIN gameRooms gr ON gr.roomID = grp.roomID
  181. WHERE accountID = ? AND status IN (1, 2, 3)
  182. LIMIT 1
  183. )");
  184. getActiveAccountsStatement = database->prepare(R"(
  185. SELECT accountID, displayName
  186. FROM accounts
  187. WHERE online = 1
  188. )");
  189. getActiveGameRoomsStatement = database->prepare(R"(
  190. SELECT roomID, hostAccountID, displayName, description, status, playerLimit, strftime('%s',CURRENT_TIMESTAMP)- strftime('%s',gr.creationTime) AS secondsElapsed
  191. FROM gameRooms gr
  192. LEFT JOIN accounts a ON gr.hostAccountID = a.accountID
  193. WHERE status IN (1, 2, 3)
  194. ORDER BY secondsElapsed ASC
  195. )");
  196. countRoomUsedSlotsStatement = database->prepare(R"(
  197. SELECT a.accountID, a.displayName
  198. FROM gameRoomPlayers grp
  199. LEFT JOIN accounts a ON a.accountID = grp.accountID
  200. WHERE roomID = ?
  201. )");
  202. countRoomTotalSlotsStatement = database->prepare(R"(
  203. SELECT playerLimit
  204. FROM gameRooms
  205. WHERE roomID = ?
  206. )");
  207. getAccountDisplayNameStatement = database->prepare(R"(
  208. SELECT displayName
  209. FROM accounts
  210. WHERE accountID = ?
  211. )");
  212. isAccountCookieValidStatement = database->prepare(R"(
  213. SELECT COUNT(accountID)
  214. FROM accountCookies
  215. WHERE accountID = ? AND cookieUUID = ?
  216. )");
  217. isPlayerInGameRoomStatement = database->prepare(R"(
  218. SELECT COUNT(accountID)
  219. FROM gameRoomPlayers grp
  220. LEFT JOIN gameRooms gr ON gr.roomID = grp.roomID
  221. WHERE accountID = ? AND grp.roomID = ?
  222. )");
  223. isPlayerInAnyGameRoomStatement = database->prepare(R"(
  224. SELECT COUNT(accountID)
  225. FROM gameRoomPlayers grp
  226. LEFT JOIN gameRooms gr ON gr.roomID = grp.roomID
  227. WHERE accountID = ? AND status IN (1, 2, 3)
  228. )");
  229. isAccountIDExistsStatement = database->prepare(R"(
  230. SELECT COUNT(accountID)
  231. FROM accounts
  232. WHERE accountID = ?
  233. )");
  234. isAccountNameExistsStatement = database->prepare(R"(
  235. SELECT COUNT(displayName)
  236. FROM accounts
  237. WHERE displayName = ?
  238. )");
  239. }
  240. LobbyDatabase::~LobbyDatabase() = default;
  241. LobbyDatabase::LobbyDatabase(const boost::filesystem::path & databasePath)
  242. {
  243. database = SQLiteInstance::open(databasePath, true);
  244. createTables();
  245. clearOldData();
  246. prepareStatements();
  247. }
  248. void LobbyDatabase::insertChatMessage(const std::string & sender, const std::string & channelType, const std::string & channelName, const std::string & messageText)
  249. {
  250. insertChatMessageStatement->executeOnce(sender, messageText, channelType, channelName);
  251. }
  252. bool LobbyDatabase::isPlayerInGameRoom(const std::string & accountID)
  253. {
  254. bool result = false;
  255. isPlayerInAnyGameRoomStatement->setBinds(accountID);
  256. if(isPlayerInAnyGameRoomStatement->execute())
  257. isPlayerInAnyGameRoomStatement->getColumns(result);
  258. isPlayerInAnyGameRoomStatement->reset();
  259. return result;
  260. }
  261. bool LobbyDatabase::isPlayerInGameRoom(const std::string & accountID, const std::string & roomID)
  262. {
  263. bool result = false;
  264. isPlayerInGameRoomStatement->setBinds(accountID, roomID);
  265. if(isPlayerInGameRoomStatement->execute())
  266. isPlayerInGameRoomStatement->getColumns(result);
  267. isPlayerInGameRoomStatement->reset();
  268. return result;
  269. }
  270. std::vector<LobbyChatMessage> LobbyDatabase::getRecentMessageHistory(const std::string & channelType, const std::string & channelName)
  271. {
  272. std::vector<LobbyChatMessage> result;
  273. getRecentMessageHistoryStatement->setBinds(channelType, channelName);
  274. while(getRecentMessageHistoryStatement->execute())
  275. {
  276. LobbyChatMessage message;
  277. getRecentMessageHistoryStatement->getColumns(message.accountID, message.displayName, message.messageText, message.age);
  278. result.push_back(message);
  279. }
  280. getRecentMessageHistoryStatement->reset();
  281. return result;
  282. }
  283. std::vector<LobbyChatMessage> LobbyDatabase::getFullMessageHistory(const std::string & channelType, const std::string & channelName)
  284. {
  285. std::vector<LobbyChatMessage> result;
  286. getFullMessageHistoryStatement->setBinds(channelType, channelName);
  287. while(getFullMessageHistoryStatement->execute())
  288. {
  289. LobbyChatMessage message;
  290. getFullMessageHistoryStatement->getColumns(message.accountID, message.displayName, message.messageText, message.age);
  291. result.push_back(message);
  292. }
  293. getFullMessageHistoryStatement->reset();
  294. return result;
  295. }
  296. void LobbyDatabase::setAccountOnline(const std::string & accountID, bool isOnline)
  297. {
  298. setAccountOnlineStatement->executeOnce(isOnline ? 1 : 0, accountID);
  299. }
  300. void LobbyDatabase::setGameRoomStatus(const std::string & roomID, LobbyRoomState roomStatus)
  301. {
  302. setGameRoomStatusStatement->executeOnce(vstd::to_underlying(roomStatus), roomID);
  303. }
  304. void LobbyDatabase::insertPlayerIntoGameRoom(const std::string & accountID, const std::string & roomID)
  305. {
  306. insertGameRoomPlayersStatement->executeOnce(roomID, accountID);
  307. }
  308. void LobbyDatabase::deletePlayerFromGameRoom(const std::string & accountID, const std::string & roomID)
  309. {
  310. deleteGameRoomPlayersStatement->executeOnce(roomID, accountID);
  311. }
  312. void LobbyDatabase::deleteGameRoomInvite(const std::string & targetAccountID, const std::string & roomID)
  313. {
  314. deleteGameRoomInvitesStatement->executeOnce(roomID, targetAccountID);
  315. }
  316. void LobbyDatabase::insertGameRoomInvite(const std::string & targetAccountID, const std::string & roomID)
  317. {
  318. insertGameRoomInvitesStatement->executeOnce(roomID, targetAccountID);
  319. }
  320. void LobbyDatabase::insertGameRoom(const std::string & roomID, const std::string & hostAccountID)
  321. {
  322. insertGameRoomStatement->executeOnce(roomID, hostAccountID);
  323. }
  324. void LobbyDatabase::insertAccount(const std::string & accountID, const std::string & displayName)
  325. {
  326. insertAccountStatement->executeOnce(accountID, displayName);
  327. }
  328. void LobbyDatabase::insertAccessCookie(const std::string & accountID, const std::string & accessCookieUUID)
  329. {
  330. insertAccessCookieStatement->executeOnce(accountID, accessCookieUUID);
  331. }
  332. void LobbyDatabase::updateAccountLoginTime(const std::string & accountID)
  333. {
  334. updateAccountLoginTimeStatement->executeOnce(accountID);
  335. }
  336. void LobbyDatabase::updateRoomPlayerLimit(const std::string & gameRoomID, int playerLimit)
  337. {
  338. updateRoomPlayerLimitStatement->executeOnce(playerLimit, gameRoomID);
  339. }
  340. void LobbyDatabase::updateRoomDescription(const std::string & gameRoomID, const std::string & description)
  341. {
  342. updateRoomDescriptionStatement->executeOnce(description, gameRoomID);
  343. }
  344. std::string LobbyDatabase::getAccountDisplayName(const std::string & accountID)
  345. {
  346. std::string result;
  347. getAccountDisplayNameStatement->setBinds(accountID);
  348. if(getAccountDisplayNameStatement->execute())
  349. getAccountDisplayNameStatement->getColumns(result);
  350. getAccountDisplayNameStatement->reset();
  351. return result;
  352. }
  353. LobbyCookieStatus LobbyDatabase::getAccountCookieStatus(const std::string & accountID, const std::string & accessCookieUUID)
  354. {
  355. bool result = false;
  356. isAccountCookieValidStatement->setBinds(accountID, accessCookieUUID);
  357. if(isAccountCookieValidStatement->execute())
  358. isAccountCookieValidStatement->getColumns(result);
  359. isAccountCookieValidStatement->reset();
  360. return result ? LobbyCookieStatus::VALID : LobbyCookieStatus::INVALID;
  361. }
  362. LobbyInviteStatus LobbyDatabase::getAccountInviteStatus(const std::string & accountID, const std::string & roomID)
  363. {
  364. assert(0);
  365. return {};
  366. }
  367. LobbyRoomState LobbyDatabase::getGameRoomStatus(const std::string & roomID)
  368. {
  369. LobbyRoomState result;
  370. getGameRoomStatusStatement->setBinds(roomID);
  371. if(getGameRoomStatusStatement->execute())
  372. getGameRoomStatusStatement->getColumns(result);
  373. else
  374. result = LobbyRoomState::CLOSED;
  375. getGameRoomStatusStatement->reset();
  376. return result;
  377. }
  378. uint32_t LobbyDatabase::getGameRoomFreeSlots(const std::string & roomID)
  379. {
  380. uint32_t usedSlots = 0;
  381. uint32_t totalSlots = 0;
  382. countRoomUsedSlotsStatement->setBinds(roomID);
  383. if(countRoomUsedSlotsStatement->execute())
  384. countRoomUsedSlotsStatement->getColumns(usedSlots);
  385. countRoomUsedSlotsStatement->reset();
  386. countRoomTotalSlotsStatement->setBinds(roomID);
  387. if(countRoomTotalSlotsStatement->execute())
  388. countRoomTotalSlotsStatement->getColumns(totalSlots);
  389. countRoomTotalSlotsStatement->reset();
  390. if (totalSlots > usedSlots)
  391. return totalSlots - usedSlots;
  392. return 0;
  393. }
  394. bool LobbyDatabase::isAccountNameExists(const std::string & displayName)
  395. {
  396. bool result = false;
  397. isAccountNameExistsStatement->setBinds(displayName);
  398. if(isAccountNameExistsStatement->execute())
  399. isAccountNameExistsStatement->getColumns(result);
  400. isAccountNameExistsStatement->reset();
  401. return result;
  402. }
  403. bool LobbyDatabase::isAccountIDExists(const std::string & accountID)
  404. {
  405. bool result = false;
  406. isAccountIDExistsStatement->setBinds(accountID);
  407. if(isAccountIDExistsStatement->execute())
  408. isAccountIDExistsStatement->getColumns(result);
  409. isAccountIDExistsStatement->reset();
  410. return result;
  411. }
  412. std::vector<LobbyGameRoom> LobbyDatabase::getActiveGameRooms()
  413. {
  414. std::vector<LobbyGameRoom> result;
  415. while(getActiveGameRoomsStatement->execute())
  416. {
  417. LobbyGameRoom entry;
  418. getActiveGameRoomsStatement->getColumns(entry.roomID, entry.hostAccountID, entry.hostAccountDisplayName, entry.description, entry.roomState, entry.playerLimit, entry.age);
  419. result.push_back(entry);
  420. }
  421. getActiveGameRoomsStatement->reset();
  422. for (auto & room : result)
  423. {
  424. countRoomUsedSlotsStatement->setBinds(room.roomID);
  425. while(countRoomUsedSlotsStatement->execute())
  426. {
  427. LobbyAccount account;
  428. countRoomUsedSlotsStatement->getColumns(account.accountID, account.displayName);
  429. room.participants.push_back(account);
  430. }
  431. countRoomUsedSlotsStatement->reset();
  432. }
  433. return result;
  434. }
  435. std::vector<LobbyGameRoom> LobbyDatabase::getAccountGameHistory(const std::string & accountID)
  436. {
  437. std::vector<LobbyGameRoom> result;
  438. getAccountGameHistoryStatement->setBinds(accountID);
  439. while(getAccountGameHistoryStatement->execute())
  440. {
  441. LobbyGameRoom entry;
  442. getAccountGameHistoryStatement->getColumns(entry.roomID, entry.hostAccountID, entry.hostAccountDisplayName, entry.description, entry.roomState, entry.playerLimit, entry.age);
  443. result.push_back(entry);
  444. }
  445. getAccountGameHistoryStatement->reset();
  446. for (auto & room : result)
  447. {
  448. countRoomUsedSlotsStatement->setBinds(room.roomID);
  449. while(countRoomUsedSlotsStatement->execute())
  450. {
  451. LobbyAccount account;
  452. countRoomUsedSlotsStatement->getColumns(account.accountID, account.displayName);
  453. room.participants.push_back(account);
  454. }
  455. countRoomUsedSlotsStatement->reset();
  456. }
  457. return result;
  458. }
  459. std::vector<LobbyAccount> LobbyDatabase::getActiveAccounts()
  460. {
  461. std::vector<LobbyAccount> result;
  462. while(getActiveAccountsStatement->execute())
  463. {
  464. LobbyAccount entry;
  465. getActiveAccountsStatement->getColumns(entry.accountID, entry.displayName);
  466. result.push_back(entry);
  467. }
  468. getActiveAccountsStatement->reset();
  469. return result;
  470. }
  471. std::string LobbyDatabase::getIdleGameRoom(const std::string & hostAccountID)
  472. {
  473. std::string result;
  474. getIdleGameRoomStatement->setBinds(hostAccountID);
  475. if(getIdleGameRoomStatement->execute())
  476. getIdleGameRoomStatement->getColumns(result);
  477. getIdleGameRoomStatement->reset();
  478. return result;
  479. }
  480. std::string LobbyDatabase::getAccountGameRoom(const std::string & accountID)
  481. {
  482. std::string result;
  483. getAccountGameRoomStatement->setBinds(accountID);
  484. if(getAccountGameRoomStatement->execute())
  485. getAccountGameRoomStatement->getColumns(result);
  486. getAccountGameRoomStatement->reset();
  487. return result;
  488. }