public Task SaveClanAsync(ClanSave clan, CancellationToken cancellationToken)
        {
            if (clan == null)
            {
                throw new ArgumentNullException(nameof(clan));
            }

            return(InternalSaveClanAsync(clan, false, cancellationToken));
        }
        private static MySqlCommand ToMySqlCommand(ClanSave save)
        {
            var command = new MySqlCommand(MySqlDbManagerQueries.InsertUpdateClan);

            command.Parameters.AddWithValue("ClanId", save.ClanId);
            command.Parameters.AddWithValue("Name", save.Name);
            command.Parameters.AddWithValue("Description", save.Description);
            command.Parameters.AddWithValue("ExpLevels", save.ExpLevels);
            command.Parameters.AddWithValue("Badge", save.Badge);
            command.Parameters.AddWithValue("InviteType", save.InviteType);
            command.Parameters.AddWithValue("TotalTrophies", save.TotalTrophies);
            command.Parameters.AddWithValue("RequiredTrophies", save.RequiredTrophies);
            command.Parameters.AddWithValue("WarsWon", save.WarsWon);
            command.Parameters.AddWithValue("WarsLost", save.WarsLost);
            command.Parameters.AddWithValue("WarsTried", save.WarsTried);
            command.Parameters.AddWithValue("Language", save.Language);
            command.Parameters.AddWithValue("WarFrequency", save.WarFrequency);
            command.Parameters.AddWithValue("Location", save.Location);
            command.Parameters.AddWithValue("PerkPoints", save.PerkPoints);
            command.Parameters.AddWithValue("WinStreak", save.WinStreak);
            command.Parameters.AddWithValue("WarLogsPublic", save.WarLogsPublic);

            var stream = new MemoryStream();

            using (var w = new MessageWriter(stream))
            {
                var entryList = save.Entries;
                if (entryList == null)
                {
                    w.Write(0);
                }
                else
                {
                    var list = new List <AllianceStreamEntry>(16);
                    foreach (var e in entryList)
                    {
                        if (e != null)
                        {
                            list.Add(e);
                        }
                    }

                    w.Write(list.Count);
                    for (int i = 0; i < list.Count; i++)
                    {
                        var entry = list[i];

                        w.Write(entry.Id);
                        entry.WriteStreamEntry(w);
                    }
                }

                command.Parameters.AddWithValue("entries", stream.ToArray());
            }
            return(command);
        }
        private static void FromMySqlDataReader(ClanSave save, DbDataReader reader)
        {
            save.ClanId = (long)reader["clan_id"];

            //save.DateCreated = (DateTime)reader["create_date"];
            //save.DateLastSave = (DateTime)reader["last_save_date"];

            save.Name             = (string)reader["name"];
            save.Description      = (string)reader["description"];
            save.ExpLevels        = (int)reader["exp_levels"];
            save.Badge            = (int)reader["badge"];
            save.InviteType       = (int)reader["invite_type"];
            save.TotalTrophies    = (int)reader["total_trophies"];
            save.RequiredTrophies = (int)reader["required_trophies"];
            save.WarsWon          = (int)reader["wars_won"];
            save.WarsLost         = (int)reader["wars_lost"];
            save.WarsTried        = (int)reader["wars_tried"];
            save.Location         = (int)reader["location"];
            save.PerkPoints       = (int)reader["perk_points"];
            save.WinStreak        = (int)reader["win_streak"];
            save.WarLogsPublic    = (bool)reader["war_logs_public"];

            var entries = (byte[])reader["entries"];
            var stream  = new MemoryStream(entries);

            using (var r = new MessageReader(stream))
            {
                var count = r.ReadInt32();
                var list  = new List <AllianceStreamEntry>(count);
                for (int i = 0; i < count; i++)
                {
                    var id    = r.ReadInt32();
                    var entry = StreamEntryFactory.CreateAllianceStreamEntry(id);

                    Debug.Assert(entry != null);

                    if (entry == null)
                    {
                        break;
                    }

                    entry.ReadStreamEntry(r);
                    list.Add(entry);
                }

                save.Entries = list;
            }
        }
        private static MySqlCommand ToMySqlCommand(ClanMember member, ClanSave clan)
        {
            var command = new MySqlCommand(MySqlDbManagerQueries.InsertUpdateClanMember);

            command.Parameters.AddWithValue("UserId", member.Id);
            command.Parameters.AddWithValue("ClanId", clan.ClanId);
            command.Parameters.AddWithValue("Role", (int)member.Role);
            command.Parameters.AddWithValue("TroopsDonated", member.TroopsDonated);
            command.Parameters.AddWithValue("TroopsReceived", member.TroopsReceived);
            command.Parameters.AddWithValue("Rank", member.Rank);
            command.Parameters.AddWithValue("PreviousRank", member.PreviousRank);
            command.Parameters.AddWithValue("NewMember", member.NewMember);
            command.Parameters.AddWithValue("WarCooldown", member.WarCooldown);
            command.Parameters.AddWithValue("WarPreference", member.WarPreference);

            return(command);
        }
        private async Task InternalSaveClanAsync(ClanSave clanSave, bool newClan, CancellationToken cancellationToken)
        {
            using (var sql = new MySqlConnection(_connectionString))
            {
                await sql.OpenAsync();

                using (var command = ToMySqlCommand(clanSave))
                {
                    command.Connection = sql;

                    var numRows = await command.ExecuteNonQueryAsync(cancellationToken);

                    // numRows affected 1 means that the ClanSave was inserted into the db,
                    // numRows affected 2 means that the ClanSave was updated.
                    // Since its new, it shouldn't have any members.
                    if (newClan && numRows == 1)
                    {
                        var newId = command.LastInsertedId;
                        Debug.Assert(newId != 0, "Number of rows affected was 1, however last insert id was 0.");

                        clanSave.ClanId = newId;
                        Server.Cache.Register(newId, clanSave);
                    }
                    else
                    {
                        Debug.Assert(clanSave.ClanId != 0);

                        // Count how many members are in the clan to
                        // figure out if the clans needs to be deleted from the db.
                        var count = 0;
                        using (var command2 = new MySqlCommand("DELETE FROM `clan_members` WHERE `clan_id` = @ClanId", sql))
                        {
                            command2.Parameters.AddWithValue("ClanId", clanSave.ClanId);

                            var numRows3 = await command2.ExecuteNonQueryAsync(cancellationToken);

                            // Iterates through our list of clan members and store in them
                            // in the clans_members table.
                            foreach (var c in clanSave.Members)
                            {
                                var ccommand = ToMySqlCommand(c, clanSave);
                                ccommand.Connection = sql;

                                // Make sure to await since we can't do multiple queries on the same connection.
                                await ccommand.ExecuteNonQueryAsync(cancellationToken);

                                count++;
                            }
                        }

                        // If there are no members left in the clan, we delete the clan from table.
                        var shouldDelete = count == 0;
                        if (shouldDelete)
                        {
                            using (var command2 = new MySqlCommand("DELETE FROM `clans` WHERE `clan_id` = @ClanId", sql))
                            {
                                command2.Parameters.AddWithValue("ClanId", clanSave.ClanId);

                                var numRows2 = await command2.ExecuteNonQueryAsync(cancellationToken);

                                if (numRows2 != 1)
                                {
                                    Server.Logs.Warn($"Tried to delete an empty clan however, number of rows affected was '{numRows2}'.");
                                }

                                Server.Cache.Unregister <ClanSave>(clanSave.ClanId);
                            }
                        }
                        else
                        {
                            Server.Cache.Register(clanSave.ClanId, clanSave);
                        }
                    }
                }
            }
        }