public void create()
                {
                    var sql = new Core.Database.Sql();

                    sql.Append(CheckAssociationExists, player_id, associate_id);

                    // Check if the relationship exists before creating it
                    sqlite.Query(sql, sqlConnection, check_results =>
                    {
                        if (check_results.Count > 0)
                        {
                            return;
                        }

                        sql = new Core.Database.Sql();

                        sql.Append(InsertAssociation, player_id, associate_id, level);
                        sqlite.Insert(sql, sqlConnection, result =>
                        {
                            if (result == null)
                            {
                                return;
                            }
                            id = (int)sqlConnection.LastInsertRowId;
                        });
                    });
                }
            Kin createKin(ulong kinUserId)
            {
                Kin kin = new Kin(id);

                var sql = new Core.Database.Sql();

                sql.Append(SelectPlayer, kinUserId);

                sqlite.Query(sql, sqlConnection, list =>
                {
                    if (list == null)
                    {
                        return;
                    }

                    foreach (var user in list)
                    {
                        kin.kin_id      = Convert.ToInt32(user["id"]);
                        kin.kin_name    = Convert.ToString(user["name"]);
                        kin.kin_user_id = kinUserId;
                        break;
                    }

                    kin.create();
                });

                return(kin);
            }
            public void decreaseAssociationsLevel()
            {
                if (associations.Count == 0)
                {
                    return;
                }

                List <ulong> to_remove = new List <ulong>();
                var          sql       = new Core.Database.Sql();

                foreach (ulong key in associations.Keys)
                {
                    Association association  = associations[key];
                    int         new_affinity = association.level - affinityDecRate;
                    if (new_affinity >= 1)
                    {
                        association.level = association.level - affinityDecRate;
                        sql.Append(UpdateAssociation, association.level, association.id);
                    }
                    else if (new_affinity <= 0)
                    {
                        sql.Append(DeleteAssociation, association.id);
                        to_remove.Add(key);
                    }
                }

                foreach (ulong keyToRemove in to_remove)
                {
                    associations.Remove(keyToRemove);
                }

                sqlite.ExecuteNonQuery(sql, sqlConnection);
            }
                public void create()
                {
                    var sql = new Core.Database.Sql();

                    sql.Append(InsertKin, self_id, kin_id);
                    sqlite.Insert(sql, sqlConnection);
                }
            void syncPlagueLevel()
            {
                var sql = new Core.Database.Sql();

                sql.Append(UpdatePlayerPlagueLevel, plagueLevel, (pristine ? 1 : 0), player.userID);
                sqlite.Update(sql, sqlConnection);
            }
            void createAssociation(ulong associate_user_id, Func <Association, bool> callback)
            {
                Association association = new Association();

                var sql = new Core.Database.Sql();

                sql.Append(SelectPlayer, associate_user_id);
                sqlite.Query(sql, sqlConnection, list =>
                {
                    if (list == null)
                    {
                        return;
                    }
                    if (list.Count == 0)
                    {
                        callback(null);
                        return;
                    }
                    ;

                    foreach (var user in list)
                    {
                        association.player_id         = id;
                        association.associate_id      = Convert.ToInt32(user["id"]);
                        association.associate_user_id = associate_user_id;
                        association.associate_name    = Convert.ToString(user["name"]);
                        association.level             = 0;
                        break;
                    }

                    association.create();
                    callback(association);
                });
            }
            /*
             * Retrieves a player from database and restore its store or creates a new database entry
             */
            public PlayerState(BasePlayer newPlayer, Func <PlayerState, bool> callback)
            {
                player = newPlayer;
                Interface.Oxide.LogInfo("Loading player: " + player.displayName);

                var sql = new Core.Database.Sql();

                sql.Append(InsertPlayer, player.userID, player.displayName);
                sqlite.Insert(sql, sqlConnection, create_results =>
                {
                    if (create_results == 1)
                    {
                        Interface.Oxide.LogInfo("New user created!");
                    }

                    sql = new Core.Database.Sql();
                    sql.Append(SelectPlayer, player.userID);

                    sqlite.Query(sql, sqlConnection, results =>
                    {
                        if (results == null)
                        {
                            return;
                        }

                        if (results.Count > 0)
                        {
                            foreach (var entry in results)
                            {
                                id              = Convert.ToInt32(entry["id"]);
                                plagueLevel     = Convert.ToInt32(entry["plague_level"]);
                                kinChangesCount = Convert.ToInt32(entry["kin_changes_count"]);
                                pristine        = Convert.ToBoolean(entry["pristine"]);
                                break;
                            }
                        }
                        else
                        {
                            Interface.Oxide.LogInfo("Something wrong has happened: Could not find the player with the given user_id!");
                        }

                        associations = new Dictionary <ulong, Association>();
                        kins         = new Dictionary <ulong, Kin>();
                        kinRequests  = new List <ulong>();

                        loadAssociations();
                        loadKinList();
                        //loadKinRequestList();
                        callback?.Invoke(this);
                    });
                });
            }
            public static void setupDatabase(RustPlugin plugin)
            {
                sqlConnection = sqlite.OpenDb($"Plagued.db", plugin);

                var sql = new Core.Database.Sql();

                sql.Append(@"CREATE TABLE IF NOT EXISTS players (
                                 id INTEGER PRIMARY KEY   AUTOINCREMENT,
                                 user_id TEXT UNIQUE NOT NULL,
                                 name TEXT,
                                 plague_level INTEGER,
                                 kin_changes_count INTEGER,
                                 pristine INTEGER
                               );");

                sql.Append(@"CREATE TABLE IF NOT EXISTS associations (
                                id INTEGER PRIMARY KEY   AUTOINCREMENT,
                                player_id integer NOT NULL,
                                associate_id integer NOT NULL,
                                level INTEGER,
                                FOREIGN KEY (player_id) REFERENCES players(id),
                                FOREIGN KEY (associate_id) REFERENCES players(id)
                            );");

                sql.Append(@"CREATE TABLE IF NOT EXISTS kin (
                                self_id integer NOT NULL,
                                kin_id integer NOT NULL,
                                timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
                                FOREIGN KEY (self_id) REFERENCES players(id),
                                FOREIGN KEY (kin_id) REFERENCES players(id),
                                PRIMARY KEY (self_id,kin_id)
                            );");

                sql.Append(@"CREATE TABLE IF NOT EXISTS kin_request (
                                requester_id integer NOT NULL,
                                target_id integer NOT NULL,
                                timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
                                FOREIGN KEY (requester_id) REFERENCES players(id),
                                FOREIGN KEY (target_id) REFERENCES players(id),
                                PRIMARY KEY (requester_id,target_id)
                            );");


                sqlite.Insert(sql, sqlConnection);
            }
            public bool forceRemoveKin(ulong kinUserID)
            {
                if (isKinByUserID(kinUserID))
                {
                    kinChangesCount++;
                    Kin kin = kins[kinUserID];

                    var sql = new Core.Database.Sql();
                    sql.Append(DeleteKin, kin.self_id, kin.kin_id);
                    sqlite.ExecuteNonQuery(sql, sqlConnection);

                    kins.Remove(kinUserID);

                    return(true);
                }

                return(false);
            }
            void loadKinRequestList()
            {
                var sql = new Core.Database.Sql();

                sql.Append(SelectKinRequestList, id);
                sqlite.Query(sql, sqlConnection, results =>
                {
                    if (results == null)
                    {
                        return;
                    }

                    foreach (var kinRequest in results)
                    {
                        kinRequests.Add((ulong)Convert.ToInt64(kinRequest["user_id"]));
                    }
                });
            }
            void loadKinList()
            {
                var sql = new Core.Database.Sql();

                sql.Append(SelectKinList, id);
                sqlite.Query(sql, sqlConnection, results =>
                {
                    if (results == null)
                    {
                        return;
                    }

                    foreach (var kinResult in results)
                    {
                        Kin kin = new Kin(id);
                        kin.load(kinResult);
                        kins[kin.kin_user_id] = kin;
                    }
                });
            }
            void loadAssociations()
            {
                var sql = new Core.Database.Sql();

                sql.Append(SelectAssociations, id);
                sqlite.Query(sql, sqlConnection, results =>
                {
                    if (results == null)
                    {
                        return;
                    }

                    foreach (var association_result in results)
                    {
                        Association association = new Association();
                        association.load(association_result);
                        associations[association.associate_user_id] = association;
                    }
                });
            }
            /*
             * Increases the affinity of all the associations in the list and increases the plague penalty if some associations are over the plague threshold
             * It also decreases the plague treshold if all the associates are kin or under the threshold
             */
            public void increasePlaguePenalty(List <BasePlayer> associates)
            {
                int contagionVectorsCount = 0;
                var sql = new Core.Database.Sql();

                foreach (BasePlayer associate in associates)
                {
                    if (isKinByUserID(associate.userID))
                    {
                        continue;
                    }

                    Association association = increaseAssociateAffinity(associate);

                    if (association == null)
                    {
                        continue;
                    }

                    sql.Append(UpdateAssociation, association.level, association.id);

                    if (association.level >= plagueMinAffinity)
                    {
                        contagionVectorsCount++;
                    }
                }

                sqlite.Update(sql, sqlConnection);


                if (contagionVectorsCount > 0)
                {
                    increasePlagueLevel(contagionVectorsCount);
                }
                else
                {
                    decreasePlagueLevel();
                }

                //Puts(player.displayName + " -> " + plagueLevel);
            }