From 2c9e13baaf7285052a3a7cad3654b4a347850387 Mon Sep 17 00:00:00 2001
From: Nikola Petrov <nikola@petrovv.com>
Date: Tue, 28 Jan 2025 15:49:36 +0100
Subject: [PATCH] Add checking of data

---
 CMakeLists.txt         |   1 +
 server/inc/checker.hpp |   3 ++
 server/inc/sql.hpp     |   6 +++
 server/src/checker.cpp | 112 +++++++++++++++++++++++++++++++++++++++++
 server/src/server.cpp  |   3 ++
 5 files changed, 125 insertions(+)
 create mode 100644 server/inc/checker.hpp
 create mode 100644 server/src/checker.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index aec95dc..ac0baf5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,6 +57,7 @@ target_link_libraries(app ${CMAKE_BINARY_DIR}/raylib/lib/libraylib.a)
 add_executable(server
   server/src/server.cpp
   server/src/sql.cpp
+  server/src/checker.cpp
 
   shared/src/TcpSocket.cpp
   shared/src/values/Dna.cpp
diff --git a/server/inc/checker.hpp b/server/inc/checker.hpp
new file mode 100644
index 0000000..fcab009
--- /dev/null
+++ b/server/inc/checker.hpp
@@ -0,0 +1,3 @@
+
+
+void checker();
\ No newline at end of file
diff --git a/server/inc/sql.hpp b/server/inc/sql.hpp
index 9a04e4b..9c39099 100644
--- a/server/inc/sql.hpp
+++ b/server/inc/sql.hpp
@@ -10,6 +10,12 @@ const char max_gen[] = "SELECT MAX(GEN) FROM prim_table WHERE USER_ID = ?;";
 
 const char get_unchecked[] = "SELECT USER_ID FROM prim_table WHERE CHECKED = 0 GROUP BY USER_ID;";
 
+const char get_gen[] = "SELECT HASH, POS, LIKED FROM prim_table WHERE USER_ID = ? AND GEN = ? ORDER BY POS ASC;";
+
+const char remove_gen[] = "DELETE FROM prim_table WHERE USER_ID = ? AND GEN >= ?;";
+
+const char set_checked[] = "UPDATE prim_table SET CHECKED = 1 WHERE USER_ID = ? AND GEN = ?;";
+
 constexpr char DB_NAME[] = "data.db";
 
 struct sqlite3;
diff --git a/server/src/checker.cpp b/server/src/checker.cpp
new file mode 100644
index 0000000..42c4d5e
--- /dev/null
+++ b/server/src/checker.cpp
@@ -0,0 +1,112 @@
+
+#include "sql.hpp"
+#include <cstring>
+#include <cstdio>
+#include <thread>
+#include <chrono>
+#include "values/DnaManager.hpp"
+
+void checker()
+{
+  using namespace std::chrono_literals;
+
+  sqlite3 *db;
+  int rc = sql::open(DB_NAME, &db);
+  if (rc)
+  {
+    fprintf(stderr, "Can't open database: %s\n", sql::errmsg(db));
+  }
+
+  sqlite3_stmt *get_unchecked_stmt;
+  sql::prepare_v2(db, get_unchecked, -1, &get_unchecked_stmt, NULL);
+
+  sqlite3_stmt *get_gen_stmt;
+  sql::prepare_v2(db, get_gen, -1, &get_gen_stmt, NULL);
+
+  sqlite3_stmt *remove_gen_stmt;
+  sql::prepare_v2(db, remove_gen, -1, &remove_gen_stmt, NULL);
+
+  sqlite3_stmt *set_checked_stmt;
+  sql::prepare_v2(db, set_checked, -1, &set_checked_stmt, NULL);
+
+  while (true)
+  {
+    int64_t user_id = 0;
+    if (sql::step(get_unchecked_stmt) != SQL_DONE)
+    {
+      int type = sql::column_type(get_unchecked_stmt, 0);
+      if (type != SQL_NULL)
+      {
+        user_id = sql::column_int64(get_unchecked_stmt, 0);
+      }
+    }
+    sql::reset(get_unchecked_stmt);
+    if (user_id == 0)
+    {
+      std::this_thread::sleep_for(60s);
+      continue;
+    }
+
+    DnaManagerData data;
+    data.id = user_id;
+    data.randSeed = mrand::getState(data.id);
+    data.queued = 0;
+    data.showed = 0;
+    data.generation = 0;
+    data.vector.resize(NUM_PER_GEN);
+
+    for (std::size_t i = 0; i < NUM_PER_GEN; i++)
+    {
+      DNA::newDna(&data.vector[i], &data.randSeed);
+    }
+    bool found_err = false;
+    while (found_err != true)
+    {
+      sql::bind_int64(get_gen_stmt, 1, data.id);
+      sql::bind_int64(get_gen_stmt, 2, data.generation);
+
+      bool new_gen = false;
+      while (sql::step(get_gen_stmt) != SQL_DONE)
+      {
+        int64_t hash = sql::column_int64(get_gen_stmt, 0);
+        int64_t pos = sql::column_int64(get_gen_stmt, 1);
+        int64_t liked = sql::column_int64(get_gen_stmt, 2);
+        UiUnit unit = DnaManager::next(&data);
+        int64_t cal_hash = mrand::computeCRC32(unit.dna, sizeof(Dna));
+        if ((unit.index != pos) || (hash != cal_hash))
+        {
+          found_err = true;
+          sql::bind_int64(remove_gen_stmt, 1, data.id);
+          sql::bind_int64(remove_gen_stmt, 2, data.generation);
+          sql::step(remove_gen_stmt);
+          sql::reset(remove_gen_stmt);
+          break;
+        }
+        unit.liked = (Liked)liked;
+        new_gen = DnaManager::like(unit, &data);
+      }
+
+      if (!found_err)
+      {
+        sql::bind_int64(set_checked_stmt, 1, data.id);
+        sql::bind_int64(set_checked_stmt, 2, data.generation);
+        sql::step(set_checked_stmt);
+        sql::reset(set_checked_stmt);
+      }
+      if (!new_gen)
+      {
+        found_err = true;
+        sql::bind_int64(remove_gen_stmt, 1, data.id);
+        sql::bind_int64(remove_gen_stmt, 2, data.generation);
+        sql::step(remove_gen_stmt);
+        sql::reset(remove_gen_stmt);
+      }
+      else
+      {
+        DnaManager::newGen(&data);
+      }
+
+      sql::reset(get_gen_stmt);
+    }
+  }
+}
\ No newline at end of file
diff --git a/server/src/server.cpp b/server/src/server.cpp
index 035ccc6..0d547c5 100644
--- a/server/src/server.cpp
+++ b/server/src/server.cpp
@@ -2,9 +2,11 @@
 #include "NetConst.hpp"
 #include "sql.hpp"
 #include "values/DnaManager.hpp"
+#include "checker.hpp"
 
 #include <iostream>
 #include <map>
+#include <thread>
 
 // use pthread rw_lock to lock db so you can safy clone .db file
 
@@ -105,6 +107,7 @@ void call(int sock, sockaddr_in newSocketInfo)
 int main()
 {
   sql::init();
+  std::thread t(checker);
   // Bind the server to a port.
   int err = TcpSocket::listent("0.0.0.0", 8888, call);
   if (err < 0)