/// <summary>
        /// Updates a player in the database.
        /// </summary>
        /// <returns><c>true</c>, if user was updated, <c>false</c> otherwise.</returns>
        /// <param name="name">The players name.</param>
        /// <param name="password">Password.</param>
        /// <param name="op">If set to <c>true</c> op.</param>
        public static bool UpdatePlayer(string name, string password, bool?op = null)
        {
            if (name == null && op == null)
            {
                throw new InvalidOperationException("You have not specified anything to be updated");
            }

#if DAPPER
            using (var ctx = DatabaseFactory.CreateConnection())
            {
                using (var txn = ctx.BeginTransaction())
                {
                    var player = ctx.SingleOrDefault <DbPlayer>(new { Name = name }, transaction: txn);
                    if (player == null)
                    {
                        throw new InvalidOperationException("Cannot update a non-existent player");
                    }

                    if (password != null)
                    {
                        player.SetRawPassword(password);
                    }
                    if (op.HasValue)
                    {
                        player.Operator = op.Value;
                    }

                    ctx.Update(player, transaction: txn);

                    txn.Commit();
                    return(true);
                }
            }
#else
            using (var ctx = new TContext())
            {
                var player = ctx.Players.SingleOrDefault(p => p.Name == name);
                if (player == null)
                {
                    throw new InvalidOperationException("Cannot update a non-existent player");
                }

                if (password != null)
                {
                    player.SetRawPassword(password);
                }
                if (op.HasValue)
                {
                    player.Operator = op.Value;
                }

                ctx.SaveChanges();

                return(true);
            }
#endif
        }
        /// <summary>
        /// Creates a user.
        /// </summary>
        /// <returns><c>true</c>, if user was created, <c>false</c> otherwise.</returns>
        /// <param name="name">The players name.</param>
        /// <param name="password">Password.</param>
        /// <param name="op">If set to <c>true</c> op.</param>
        public static DbPlayer CreatePlayer(string name, string password, bool op = false)
        {
#if DAPPER
            using (var ctx = DatabaseFactory.CreateConnection())
            {
                using (var txn = ctx.BeginTransaction())
                {
                    var player = new DbPlayer(name, password)
                    {
                        Operator  = op,
                        DateAdded = DateTime.UtcNow
                    };
                    player.Id = ctx.Insert(player, transaction: txn);

                    var hc = new HookContext();
                    var ha = new Events.HookArgs.PlayerRegistered()
                    {
                        Connection  = ctx,
                        Transaction = txn,
                        Player      = player
                    };

                    Events.HookPoints.PlayerRegistered.Invoke(ref hc, ref ha);

                    if (hc.Result == HookResult.DEFAULT)
                    {
                        txn.Commit();
                        return(player);
                    }
                    else
                    {
                        txn.Rollback();
                        return(null);
                    }
                }
            }
#else
            using (var ctx = new TContext())
            {
                DbPlayer player;
                ctx.Players.Add(player = new DbPlayer(name, password)
                {
                    Operator     = op,
                    DateAddedUTC = DateTime.UtcNow
                });

                ctx.SaveChanges();

                return(player);
            }
#endif
        }
        /// <summary>
        /// Removes a player from the database by name
        /// </summary>
        /// <returns><c>true</c>, if the player was deleted, <c>false</c> otherwise.</returns>
        /// <param name="name">The players name.</param>
        public static bool DeletePlayer(string name)
        {
#if DAPPER
            using (var ctx = DatabaseFactory.CreateConnection())
            {
                using (var txn = ctx.BeginTransaction())
                {
                    var res = ctx.Execute($"delete from {TableMapper.TypeToName<DbPlayer>()} where Name = @Name", new { Name = name }, transaction: txn) > 0;
                    txn.Commit();
                    return(res);
                }
            }
#else
            using (var ctx = new TContext())
            {
                var matches = ctx.Players.Where(x => x.Name == name);
                ctx.Players.RemoveRange(matches);

                ctx.SaveChanges();

                return(ctx.Players.Any(x => x.Name == name));
            }
#endif
        }
        public static Character UpdateCharacter(
            TContext ctx,
            CharacterMode mode,
            string auth,
            string clientUUID,
            int health,
            int maxHealth,
            int mana,
            int maxMana,
            int spawnX,
            int spawnY,
            int hair,
            byte hairDye,
            int hideVisual,
            byte difficulty,
            uint hairColor,
            uint skinColor,
            uint eyeColor,
            uint shirtColor,
            uint underShirtColor,
            uint pantsColor,
            uint shoeColor, 
            int anglerQuests
        )
        {
            int? userId = null;
            if (mode == CharacterMode.AUTH)
            {
                var user = AuthenticatedUsers.GetUser(auth);
                userId = user.Id;
            }
            else if (mode != CharacterMode.UUID)
                return null;

            Character chr;
            if (mode == CharacterMode.AUTH)
            {
                chr = ctx.Characters.Single(x => x.UserId == userId.Value);
            }
            else
            {
                chr = ctx.Characters.Single(x => x.UUID == clientUUID);
            }

            chr.Health = health;
            chr.MaxHealth = maxHealth;
            chr.Mana = mana;
            chr.MaxMana = maxMana;
            chr.SpawnX = spawnX;
            chr.SpawnY = spawnY;
            chr.Hair = hair;
            chr.HairDye = hairDye;
            chr.HideVisual = hideVisual;
            chr.Difficulty = difficulty;
            chr.HairColor = hairColor;
            chr.SkinColor = skinColor;
            chr.EyeColor = eyeColor;
            chr.ShirtColor = shirtColor;
            chr.UnderShirtColor = underShirtColor;
            chr.PantsColor = pantsColor;
            chr.ShoeColor = shoeColor;
            chr.AnglerQuests = anglerQuests;

            ctx.SaveChanges();

            return chr;
        }
        public static Character NewCharacter(
            TContext ctx,
            CharacterMode mode,
            string auth,
            string clientUUID,
            int health,
            int maxHealth,
            int mana,
            int maxMana,
            int spawnX,
            int spawnY,
            int hair,
            byte hairDye,
            int hideVisual,
            byte difficulty,
            uint hairColor,
            uint skinColor,
            uint eyeColor,
            uint shirtColor,
            uint underShirtColor,
            uint pantsColor,
            uint shoeColor, 
            int anglerQuests
        )
        {
            int? userId = null;
            if (mode == CharacterMode.AUTH)
            {
                var user = AuthenticatedUsers.GetUser(auth);
                userId = user.Id;
            }
            else if (mode != CharacterMode.UUID)
                return null;

            Character chr = new Character()
            {
                UserId = userId,
                UUID = clientUUID,
                Health = health,
                MaxHealth = maxHealth,
                Mana = mana,
                MaxMana = maxMana,
                SpawnX = spawnX,
                SpawnY = spawnY,
                Hair = hair,
                HairDye = hairDye,
                HideVisual = hideVisual,
                Difficulty = difficulty,
                HairColor = hairColor,
                SkinColor = skinColor,
                EyeColor = eyeColor,
                ShirtColor = shirtColor,
                UnderShirtColor = underShirtColor,
                PantsColor = pantsColor,
                ShoeColor = shoeColor,
                AnglerQuests = anglerQuests
            };
            ctx.Characters.Add(chr);

            ctx.SaveChanges();

            return chr;
        }
        public static void SaveAll()
        {
            if ((DateTime.Now - _lastSave).TotalSeconds >= SaveInterval)
            {
                //Don't perform any unnecessary writes
                var hasPlayers = Netplay.anyClients;
                if (!hasPlayers && !_hadPlayers && !EnsureSave)
                    return;

                EnsureSave = false;
                try
                {
#if ENTITY_FRAMEWORK_7
                    using (var ctx = new TContext())
#elif DAPPER
                    using(var ctx = DatabaseFactory.CreateConnection())
#endif
                    {
                        using (var txn = ctx.BeginTransaction())
                        {
                            foreach (var ply in Terraria.Main.player)
                            {
                                if (ply != null && ply.active && ply.GetSSCReadyForSave())
                                {
                                    SavePlayerData(ctx, txn, false, ply);
                                }
                            }
                            txn.Commit();
                        }

#if ENTITY_FRAMEWORK_7
                        ctx.SaveChanges();
#endif
                    }
                }
                catch (Exception e)
                {
                    ProgramLog.Log(e);
                }

                _hadPlayers = hasPlayers;
                _lastSave = DateTime.Now;
            }
        }
        /// <summary>
        /// Updates a player in the database.
        /// </summary>
        /// <returns><c>true</c>, if user was updated, <c>false</c> otherwise.</returns>
        /// <param name="name">The players name.</param>
        /// <param name="password">Password.</param>
        /// <param name="op">If set to <c>true</c> op.</param>
        public static bool UpdatePlayer(string name, string password, bool? op = null)
        {
            if (name == null && op == null) throw new InvalidOperationException("You have not specified anything to be updated");

#if DAPPER
            using (var ctx = DatabaseFactory.CreateConnection())
            {
                using (var txn = ctx.BeginTransaction())
                {
                    var player = ctx.SingleOrDefault<DbPlayer>(new { Name = name }, transaction: txn);
                    if (player == null) throw new InvalidOperationException("Cannot update a non-existent player");

                    if (password != null) player.SetRawPassword(password);
                    if (op.HasValue) player.Operator = op.Value;

                    ctx.Update(player, transaction: txn);

                    txn.Commit();
                    return true;
                }
            }
#else
            using (var ctx = new TContext())
            {
                var player = ctx.Players.SingleOrDefault(p => p.Name == name);
                if (player == null) throw new InvalidOperationException("Cannot update a non-existent player");

                if (password != null) player.SetRawPassword(password);
                if (op.HasValue) player.Operator = op.Value;

                ctx.SaveChanges();

                return true;
            }
#endif
        }
        /// <summary>
        /// Creates a user.
        /// </summary>
        /// <returns><c>true</c>, if user was created, <c>false</c> otherwise.</returns>
        /// <param name="name">The players name.</param>
        /// <param name="password">Password.</param>
        /// <param name="op">If set to <c>true</c> op.</param>
        public static DbPlayer CreatePlayer(string name, string password, bool op = false)
        {
#if DAPPER
            using (var ctx = DatabaseFactory.CreateConnection())
            {
                using (var txn = ctx.BeginTransaction())
                {
                    var player = new DbPlayer(name, password)
                    {
                        Operator = op,
                        DateAdded = DateTime.UtcNow
                    };
                    player.Id = ctx.Insert(player, transaction: txn);

                    var hc = new HookContext();
                    var ha = new Events.HookArgs.PlayerRegistered()
                    {
                        Connection = ctx,
                        Transaction = txn,
                        Player = player
                    };

                    Events.HookPoints.PlayerRegistered.Invoke(ref hc, ref ha);

                    if (hc.Result == HookResult.DEFAULT)
                    {
                        txn.Commit();
                        return player;
                    }
                    else
                    {
                        txn.Rollback();
                        return null;
                    }
                }
            }
#else
            using (var ctx = new TContext())
            {
                DbPlayer player;
                ctx.Players.Add(player = new DbPlayer(name, password)
                {
                    Operator = op,
                    DateAddedUTC = DateTime.UtcNow
                });

                ctx.SaveChanges();

                return player;
            }
#endif
        }
        /// <summary>
        /// Removes a player from the database by name
        /// </summary>
        /// <returns><c>true</c>, if the player was deleted, <c>false</c> otherwise.</returns>
        /// <param name="name">The players name.</param>
        public static bool DeletePlayer(string name)
        {
#if DAPPER
            using (var ctx = DatabaseFactory.CreateConnection())
            {
                using (var txn = ctx.BeginTransaction())
                {
                    var res = ctx.Execute($"delete from {TableMapper.TypeToName<DbPlayer>()} where Name = @Name", new { Name = name }, transaction: txn) > 0;
                    txn.Commit();
                    return res;
                }
            }
#else
            using (var ctx = new TContext())
            {
                var matches = ctx.Players.Where(x => x.Name == name);
                ctx.Players.RemoveRange(matches);

                ctx.SaveChanges();

                return ctx.Players.Any(x => x.Name == name);
            }
#endif
        }
        public static void SaveAll()
        {
            if ((DateTime.Now - _lastSave).TotalSeconds >= SaveInterval)
            {
                //Don't perform any unnecessary writes
                var hasPlayers = Netplay.anyClients;
                if (!hasPlayers && !_hadPlayers && !EnsureSave)
                    return;

                EnsureSave = false;
                try
                {
                    using (var ctx = new TContext())
                    {
                        foreach (var ply in Terraria.Main.player)
                        {
                            if (ply != null && ply.active && ply.GetSSCReadyForSave())
                            {
                                SavePlayerData(ctx, false, ply);
                            }
                        }

                        ctx.SaveChanges();
                    }
                }
                catch (Exception e)
                {
                    ProgramLog.Log(e);
                }

                _hadPlayers = hasPlayers;
                _lastSave = DateTime.Now;
            }
        }
        static void CreateGroup(string name, bool guest, string parent, byte r, byte g, byte b, string[] nodes, TContext ctx,
                                string chatPrefix = null,
                                string chatSuffix = null)
        {
            var grp = new Group()
            {
                Name = name,
                ApplyToGuests = guest,
                Parent = parent,
                Chat_Red = r,
                Chat_Green = g,
                Chat_Blue = b,
                Chat_Prefix = chatPrefix,
                Chat_Suffix = chatSuffix
            };
            ctx.Groups.Add(grp);

            ctx.SaveChanges(); //Save to get the ID

            foreach (var nd in nodes)
            {
                var node = ctx.Nodes.SingleOrDefault(x => x.Node == nd && x.Permission == Permission.Permitted);
                if (node == null)
                {
                    ctx.Nodes.Add(node = new NodePermission()
                        {
                            Node = nd,
                            Permission = Permission.Permitted
                        });

                    ctx.SaveChanges();
                }

                ctx.GroupNodes.Add(new GroupNode()
                    {
                        GroupId = grp.Id,
                        NodeId = node.Id
                    });
            }

            ctx.SaveChanges();
        }