public void ClubNameCheckReq(GameSession session, ClubNameCheckReqMessage message) { var plr = session.Player; if (plr == null) { return; } var ascii = Config.Instance.Game.NickRestrictions.AsciiOnly; if (!Namecheck.IsNameValid(message.Name, true) || ascii && message.Name.Any(c => c > 127) || !ascii && message.Name.Any(c => c > 255)) { session.SendAsync(new NickCheckAckMessage(true)); return; } if (GameServer.Instance.ClubManager.Any(c => c.ClanName == message.Name)) { session.SendAsync(new ClubNameCheckAckMessage(2)); return; } session.SendAsync(new ClubNameCheckAckMessage(0)); }
public async Task CheckNickHandler(GameSession session, NickCheckReqMessage message) { var plr = session.Player; if (plr == null) { return; } var ascii = Config.Instance.Game.NickRestrictions.AsciiOnly; if (!await AuthService.IsNickAvailableAsync(message.Nickname)) { await plr.Session.SendAsync(new ServerResultAckMessage(ServerResult.NicknameUnavailable)); } if (!Namecheck.IsNameValid(message.Nickname, true) || ascii && message.Nickname.Any(c => c > 127) || !ascii && message.Nickname.Any(c => c > 255)) { await session.SendAsync(new NickCheckAckMessage(true)); return; } await session.SendAsync(new NickCheckAckMessage(false)); }
public async Task UseChangeNameItem(GameSession session, ItemUseChangeNickReqMessage message) { var plr = session.Player; var item = plr.Inventory[message.ItemId]; var ascii = Config.Instance.Game.NickRestrictions.AsciiOnly; if (!await AuthService.IsNickAvailableAsync(message.Nickname)) { await plr.Session.SendAsync(new ServerResultAckMessage(ServerResult.NicknameUnavailable)); } if (!Namecheck.IsNameValid(message.Nickname, true) || ascii && message.Nickname.Any(c => c > 127) || !ascii && message.Nickname.Any(c => c > 255)) { await session.SendAsync(new NickCheckAckMessage(true)); return; } var nickname = new NicknameHistoryDto { AccountId = (int)plr.Account.Id, OldName = plr.Account.Nickname, NewNickname = message.Nickname }; switch (item.ItemNumber) { case 4000001: //Perm Change nickname.ExpireDate = -1; if (await ChangeNickname(session.Player, nickname, false)) { plr.Inventory.RemoveOrDecrease(item); } break; default: await session.SendAsync(new ServerResultAckMessage(ServerResult.FailedToRequestTask)); return; } }
public async Task ClubCreateReq2(GameSession session, ClubCreateReq2Message message) { var plr = session.Player; if (plr == null) { return; } var ascii = Config.Instance.Game.NickRestrictions.AsciiOnly; if (GameServer.Instance.ClubManager.Any(c => c.ClanName == message.Name || c.Players.ContainsKey(plr.Account.Id)) || !Namecheck.IsNameValid(message.Name, true) || ascii && message.Name.Any(c => c > 127) || !ascii && message.Name.Any(c => c > 255)) { Logger.ForAccount(plr).Information($"Couldnt create Clan : {message.Name}"); await session.SendAsync(new ClubCreateAck2Message(1)); } else { var clubDto = new ClubDto { Name = message.Name, Icon = "" }; using (var db = GameDatabase.Open()) { try { using (var transaction = DbUtil.BeginTransaction(db)) { await DbUtil.InsertAsync(db, clubDto, statement => statement.AttachToTransaction(transaction)); var clubPlayerInfo = new ClubPlayerInfo { AccountId = session.Player.Account.Id, Account = session.Player.Account.AccountDto, State = ClubState.Joined, Rank = ClubRank.Master }; var club = new Club(clubDto, new[] { clubPlayerInfo }); GameServer.Instance.ClubManager.Add(club); transaction.Commit(); var clubplrdto = new ClubPlayerDto { PlayerId = (int)session.Player.Account.Id, ClubId = club.Id, Rank = (byte)ClubRank.Master, State = (int)ClubState.Joined }; await DbUtil.InsertAsync(db, clubplrdto); session.Player.Club = club; } } catch (Exception ex) { Logger.Error(ex.ToString()); await session.SendAsync(new ClubCreateAck2Message(1)); return; } await session.SendAsync(new ClubCreateAck2Message(0)); await session.SendAsync(new ClubMyInfoAckMessage(plr.Map <Player, ClubMyInfoDto>())); Club.LogOn(plr); } } }
public async Task EULoginHandler(ProudSession session, LoginEUReqMessage message) { var ip = session.RemoteEndPoint.Address.ToString(); var account = new AccountDto(); using (var db = AuthDatabase.Open()) { if (message.Username != "" && message.Password != "") { Logger.Information($"Login from {ip}"); if (message.Username.Length > 5 && message.Password.Length > 5) { if (!Namecheck.IsNameValid(message.Username)) { await session.SendAsync(new LoginEUAckMessage(AuthLoginResult.WrongIdorPw)); Logger.Error("Wrong login for {ip}", ip); return; } var result = db.Find <AccountDto>(statement => statement .Where($"{nameof(AccountDto.Username):C} = @{nameof(message.Username)}") .Include <BanDto>(join => join.LeftOuterJoin()) .WithParameters(new { message.Username })); account = result.FirstOrDefault(); if (account == null && (Config.Instance.NoobMode || Config.Instance.AutoRegister)) { account = new AccountDto { Username = message.Username }; var newSalt = new byte[24]; using (var csprng = new RNGCryptoServiceProvider()) { csprng.GetBytes(newSalt); } var hash = new Rfc2898DeriveBytes(message.Password, newSalt, 24000).GetBytes(24); account.Password = Convert.ToBase64String(hash); account.Salt = Convert.ToBase64String(newSalt); await db.InsertAsync(account); } var salt = Convert.FromBase64String(account?.Salt ?? ""); var passwordGuess = new Rfc2898DeriveBytes(message.Password, salt, 24000).GetBytes(24); var actualPassword = Convert.FromBase64String(account?.Password ?? ""); var difference = (uint)passwordGuess.Length ^ (uint)actualPassword.Length; for (var i = 0; i < passwordGuess.Length && i < actualPassword.Length; i++) { difference |= (uint)(passwordGuess[i] ^ actualPassword[i]); } if ((difference != 0 || string.IsNullOrWhiteSpace(account?.Password ?? "")) && !Config.Instance.NoobMode) { await session.SendAsync(new LoginEUAckMessage(AuthLoginResult.WrongIdorPw)); Logger.Error("Wrong login for {ip}", ip); return; } if (account != null) { account.AuthToken = ""; account.newToken = ""; await db.UpdateAsync(account); } } else { await session.SendAsync(new LoginEUAckMessage(AuthLoginResult.WrongIdorPw)); Logger.Error("Wrong login for {ip}", ip); return; } } else if (message.token != "") { Logger.Information($"Login from {ip}"); var result = await db.FindAsync <AccountDto>(statement => statement .Where($"{nameof(AccountDto.LoginToken):C} = @{nameof(message.token)}") .Include <BanDto>(join => join.LeftOuterJoin()) .WithParameters(new { message.token })); account = result.FirstOrDefault(); if (account != null) { var lastlogin = DateTimeOffset.ParseExact(account.LastLogin, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.None); if ((DateTimeOffset.Now - lastlogin).Minutes >= 5) { await session.SendAsync(new LoginEUAckMessage(AuthLoginResult.Failed2)); Logger.Error("Wrong login for {ip}", ip); return; } } else { await session.SendAsync(new LoginEUAckMessage(AuthLoginResult.Failed2)); Logger.Error("Wrong login for {ip}", ip); return; } } else if (message.AuthToken != "" && message.NewToken != "") { Logger.Information("Session login from {ip}", ip); var result = await db.FindAsync <AccountDto>(statement => statement .Where($"{nameof(AccountDto.AuthToken):C} = @{nameof(message.AuthToken)}") .Include <BanDto>(join => join.LeftOuterJoin()) .WithParameters(new { message.AuthToken })); account = result.FirstOrDefault(); if (account != null) { if (account.AuthToken != message.AuthToken && account.newToken != message.NewToken) { await session.SendAsync(new LoginEUAckMessage(AuthLoginResult.Failed2)); Logger.Error("Wrong session login for {ip} ({AuthToken}, {newToken})", ip, account.AuthToken, account.newToken); return; } } else { await session.SendAsync(new LoginEUAckMessage(AuthLoginResult.Failed2)); Logger.Error("Wrong session login for {ip}", ip); return; } } if (account == null) { return; } var now = DateTimeOffset.Now.ToUnixTimeSeconds(); var ban = account.Bans.FirstOrDefault(b => b.Date + (b.Duration ?? 0) > now); if (ban != null) { var unbanDate = DateTimeOffset.FromUnixTimeSeconds(ban.Date + (ban.Duration ?? 0)); Logger.Error("{user} is banned until {until}", account.Username, unbanDate); await session.SendAsync(new LoginEUAckMessage(unbanDate)); return; } Logger.Information("Login success for {user}", account.Username); var entry = new LoginHistoryDto { AccountId = account.Id, Date = DateTimeOffset.Now.ToUnixTimeSeconds(), IP = ip }; await db.InsertAsync(entry); } var datetime = $"{DateTimeOffset.UtcNow:yyyyMMddHHmmss}"; var sessionId = Hash.GetUInt32 <CRC32>($"<{account.Username}+{account.Password}>"); var authsessionId = Hash.GetString <CRC32>($"<{account.Username}+{sessionId}+{datetime}>"); var newsessionId = Hash.GetString <CRC32>($"<{authsessionId}+{sessionId}>"); using (var db = AuthDatabase.Open()) { account.LastLogin = $"{DateTimeOffset.UtcNow:yyyyMMddHHmmss}"; account.LoginToken = ""; account.AuthToken = authsessionId; account.newToken = newsessionId; await db.UpdateAsync(account); } await session.SendAsync(new LoginEUAckMessage(AuthLoginResult.OK, (ulong)account.Id, sessionId, authsessionId, newsessionId, datetime)); }
private static async Task LoginAsync(IChannelHandlerContext context, string username, string password, string hwid, string secretkey) { // This var is used for sending the unban date to the launcher var unbandate = DateTimeOffset.Now; try { var endpoint = new IPEndPoint(((IPEndPoint)context.Channel.RemoteAddress).Address.MapToIPv4(), ((IPEndPoint)context.Channel.RemoteAddress).Port); if (username.Length >= 20) { Logger.Error("Login & Password to long for {0}", endpoint); var check = new CCMessage(); check.Write(false); check.Write("Username is more than 20 chars"); RmiSend(context, 16, check); return; } if (hwid == string.Empty) { Logger.Error("No HWID found on endpoint {0}", endpoint); var check = new CCMessage(); check.Write(false); check.Write("Cannot Generate HWID"); RmiSend(context, 16, check); return; } if (Config.Instance.AuthAPI.BlockedHWIDS.Contains(hwid)) { Logger.Error("Hwid ban(conf): {0}, Address: {1}", hwid, endpoint); goto hwidban; } if (Config.Instance.LauncherCheck) { // Launcher Check if (secretkey != Config.Instance.LauncherCheckKey) { Logger.Error("Wrong Launcher => User: "******" hwid: " + hwid + " from " + endpoint); var keycheck = new CCMessage(); keycheck.Write(false); keycheck.Write("Invalid Launcher"); RmiSend(context, 16, keycheck); return; } } using (var db = AuthDatabase.Open()) { Logger.Information("AuthAPI login from {0}, HWID: {1}", endpoint, hwid); if (username.Length < 4 || password.Length < 4) { Logger.Error("Too short credentials for {username} / {endpoint}", username, endpoint); var lengtherr = new CCMessage(); lengtherr.Write(false); lengtherr.Write("Invalid length of username/password"); RmiSend(context, 16, lengtherr); return; } if (!Namecheck.IsNameValid(username)) { Logger.Error("Invalid username for {username} / {endpoint}", username, endpoint); var nickerr = new CCMessage(); nickerr.Write(false); nickerr.Write("Username contains invalid characters"); RmiSend(context, 16, nickerr); return; } var result = await db.FindAsync <AccountDto>(statement => statement .Where($"{nameof(AccountDto.Username):C} = @{nameof(username)}") .Include <BanDto>(join => join.LeftOuterJoin()) .WithParameters(new { Username = username })); var account = result.FirstOrDefault(); var hwidResult = await db.FindAsync <HwidBanDto>(statement => statement .Where($"{nameof(HwidBanDto.Hwid):C} = @{nameof(hwid)}") .WithParameters(new { Hwid = hwid })); if (hwidResult?.Any() ?? false) { Logger.Error("Hwid ban(db): {0}, Address: {1}", hwid, endpoint); goto hwidban; } if (account == null && (Config.Instance.NoobMode || Config.Instance.AutoRegister)) { account = new AccountDto { Username = username }; var newSalt = new byte[24]; using (var csprng = new RNGCryptoServiceProvider()) { csprng.GetBytes(newSalt); } var hash = new byte[24]; using (var pbkdf2 = new Rfc2898DeriveBytes(password, newSalt, 24000)) { hash = pbkdf2.GetBytes(24); } account.Password = Convert.ToBase64String(hash); account.Salt = Convert.ToBase64String(newSalt); await db.InsertAsync(account); } var salt = Convert.FromBase64String(account?.Salt ?? ""); var passwordGuess = new Rfc2898DeriveBytes(password, salt, 24000).GetBytes(24); var actualPassword = Convert.FromBase64String(account?.Password ?? ""); var difference = (uint)passwordGuess.Length ^ (uint)actualPassword.Length; for (var i = 0; i < passwordGuess.Length && i < actualPassword.Length; i++) { difference |= (uint)(passwordGuess[i] ^ actualPassword[i]); } if ((difference != 0 || string.IsNullOrWhiteSpace(account?.Password ?? "")) && !Config.Instance.NoobMode) { Logger.Error("Wrong credentials for {username} / {endpoint}", username, endpoint); goto wrong; } if (account != null) { var now = DateTimeOffset.Now.ToUnixTimeSeconds(); var ban = account.Bans.FirstOrDefault(b => b.Date + (b.Duration ?? 0) > now); if (ban != null) { var unbanDate = DateTimeOffset.FromUnixTimeSeconds(ban.Date + (ban.Duration ?? 0)); unbandate = DateTimeOffset.FromUnixTimeSeconds(ban.Date + (ban.Duration ?? 0)); Logger.Error("{user} is banned until {until}", account.Username, unbanDate); goto ban; } account.LoginToken = AuthHash .GetHash256( $"{context.Channel.RemoteAddress}-{account.Username}-{account.Password}-{endpoint.Address.MapToIPv4()}") .ToLower(); account.LastLogin = $"{DateTimeOffset.UtcNow:yyyyMMddHHmmss}"; account.AuthToken = string.Empty; account.newToken = string.Empty; await db.UpdateAsync(account); var history = new AuthHistoryDto { Account = account, AccountId = account.Id, Date = DateTimeOffset.Now.ToUnixTimeSeconds(), HWID = hwid }; await db.InsertAsync(history); var ack = new CCMessage(); ack.Write(true); ack.Write(account.LoginToken); RmiSend(context, 16, ack); } Logger.Information("AuthAPI login success for {username}", username); return; } } catch (Exception e) { Logger.Error(e.ToString()); goto error; } wrong: { var error = new CCMessage(); error.Write(false); error.Write("Invalid username or password"); RmiSend(context, 16, error); } error: { var error = new CCMessage(); error.Write(false); error.Write("Login error"); RmiSend(context, 16, error); } ban: { var error = new CCMessage(); error.Write(false); error.Write("Account is banned until " + unbandate); RmiSend(context, 16, error); } hwidban: { var error = new CCMessage(); error.Write(false); error.Write("You have been blocked"); RmiSend(context, 16, error); } }
public override void ChannelRead(IChannelHandlerContext context, object messageData) { var buffer = messageData as IByteBuffer; var data = new byte[0]; if (buffer != null) { data = buffer.ToArray(); } var msg = new CCMessage(data, data.Length); short magic = 0; var message = new ByteArray(); if (msg.Read(ref magic) && magic == Magic && msg.Read(ref message)) { var receivedMessage = new CCMessage(message); CCMessage.MessageType coreId = 0; if (!receivedMessage.Read(ref coreId)) { return; } switch (coreId) { case CCMessage.MessageType.Rmi: short rmiId = 0; if (receivedMessage.Read(ref rmiId)) { switch (rmiId) { case 15: { var username = ""; var password = ""; var register = false; if (receivedMessage.Read(ref username) && receivedMessage.Read(ref password) && receivedMessage.Read(ref register)) { using (var db = AuthDatabase.Open()) { Logger.Information("Authentication login from {endpoint}", context.Channel.RemoteAddress.ToString()); if (username.Length > 5 && password.Length > 5 && Namecheck.IsNameValid(username)) { var result = db.Find <AccountDto>(statement => statement .Where($"{nameof(AccountDto.Username):C} = @{nameof(username)}") .Include <BanDto>(join => @join.LeftOuterJoin()) .WithParameters(new { Username = username })); var account = result.FirstOrDefault(); if (account == null && (Config.Instance.NoobMode || Config.Instance.AutoRegister)) { account = new AccountDto { Username = username }; var newSalt = new byte[24]; using (var csprng = new RNGCryptoServiceProvider()) { csprng.GetBytes(newSalt); } var hash = new Rfc2898DeriveBytes(password, newSalt, 24000).GetBytes(24); account.Password = Convert.ToBase64String(hash); account.Salt = Convert.ToBase64String(newSalt); db.InsertAsync(account); } var salt = Convert.FromBase64String(account?.Salt ?? ""); var passwordGuess = new Rfc2898DeriveBytes(password, salt, 24000).GetBytes(24); var actualPassword = Convert.FromBase64String(account?.Password ?? ""); var difference = (uint)passwordGuess.Length ^ (uint)actualPassword.Length; for (var i = 0; i < passwordGuess.Length && i < actualPassword.Length; i++) { difference |= (uint)(passwordGuess[i] ^ actualPassword[i]); } if ((difference != 0 || string.IsNullOrWhiteSpace(account?.Password ?? "")) && !Config.Instance.NoobMode) { Logger.Error( "Wrong authentication credentials for {username} / {endpoint}", username, context.Channel.RemoteAddress.ToString()); var ack = new CCMessage(); ack.Write(false); ack.Write("Login failed"); RmiSend(context, 16, ack); } else { if (account != null) { account.LoginToken = AuthHash .GetHash256( $"{context.Channel.RemoteAddress}-{account.Username}-{account.Password}") .ToLower(); account.LastLogin = $"{DateTimeOffset.UtcNow:yyyyMMddHHmmss}"; account.AuthToken = ""; account.newToken = ""; db.UpdateAsync(account); var ack = new CCMessage(); ack.Write(true); ack.Write(account.LoginToken); RmiSend(context, 16, ack); } Logger.Information("Authentication success for {username}", username); } } else { Logger.Error( "Wrong authentication credentials for {username} / {endpoint}", username, context.Channel.RemoteAddress.ToString()); var ack = new CCMessage(); ack.Write(false); ack.Write("Invalid length of username/password"); RmiSend(context, 16, ack); } } } else { Logger.Error("Wrong login for {endpoint}", context.Channel.RemoteAddress.ToString()); var ack = new CCMessage(); ack.Write(false); ack.Write("Invalid loginpacket"); RmiSend(context, 16, ack); } break; } case 17: context.CloseAsync(); break; default: Logger.Error("Received unknown rmiId{rmi} from {endpoint}", rmiId, context.Channel.RemoteAddress.ToString()); break; } } break; case CCMessage.MessageType.Notify: context.CloseAsync(); break; default: Logger.Error("Received unknown coreID{coreid} from {endpoint}", coreId, context.Channel.RemoteAddress.ToString()); break; } } else { Logger.Error("Received invalid packetstruct from {endpoint}", context.Channel.RemoteAddress.ToString()); context.CloseAsync(); } }
private static async void LoginAsync(IChannelHandlerContext context, string username, string password, string hwid, string test) { lock (_loginSync) { try { using (var db = AuthDatabase.Open()) { Logger.Information("Authentication login from {endpoint}", context.Channel.RemoteAddress.ToString()); if (test != "test") goto loginerror; var hwidban = db.Find<HwidBanDto>(statement => statement .Where( $"{nameof(HwidBanDto.Hwid):C} = @{nameof(hwid)}") .WithParameters(new { hwid })); if (hwidban.Any()) { Logger.Error("Hwid ban {0} for {1}", hwid, context.Channel.RemoteAddress); goto HwidBan; } if (username.Length > 5 && password.Length > 5 && Namecheck.IsNameValid(username)) { var result = db.Find<AccountDto>(statement => statement .Where( $"{nameof(AccountDto.Username):C} = @{nameof(username)}") .WithParameters(new { Username = username })); var account = result.FirstOrDefault(); if (account == null && (Config.Instance.NoobMode || Config.Instance.AutoRegister)) { account = new AccountDto { Username = username }; var newSalt = new byte[24]; using (var csprng = new RNGCryptoServiceProvider()) { csprng.GetBytes(newSalt); } var hash = new byte[24]; using (var pbkdf2 = new Rfc2898DeriveBytes(password, newSalt, 24000)) { hash = pbkdf2.GetBytes(24); } account.Password = Convert.ToBase64String(hash); account.Salt = Convert.ToBase64String(newSalt); db.InsertAsync(account); } var ban = db.Find<BanDto>(statement => statement .Where($"{nameof(BanDto.AccountId):C} = @{nameof(account.Id)}") .WithParameters(new { account.Id })).FirstOrDefault(); if (ban != null) { var unbanDate = DateTimeOffset.FromUnixTimeSeconds(ban.Date + (ban.Duration ?? 0)); Logger.Error("{user} is banned until {until}", account.Username, unbanDate); CCMessage error = new CCMessage(); error.Write(false); error.Write("You ban until " + unbanDate); RmiSend(context, 16, error); } var salt = Convert.FromBase64String(account?.Salt ?? ""); var passwordGuess = new Rfc2898DeriveBytes(password, salt, 24000).GetBytes(24); var actualPassword = Convert.FromBase64String(account?.Password ?? ""); var difference = (uint)passwordGuess.Length ^ (uint)actualPassword.Length; for (var i = 0; i < passwordGuess.Length && i < actualPassword.Length; i++) difference |= (uint)(passwordGuess[i] ^ actualPassword[i]); if ((difference != 0 || string.IsNullOrWhiteSpace(account?.Password ?? "")) && !Config.Instance.NoobMode) { Logger.Error( "Wrong authentication credentials for {username} / {endpoint}", username, context.Channel.RemoteAddress.ToString()); var ack = new CCMessage(); ack.Write(false); ack.Write("Login failed"); RmiSend(context, 16, ack); } else { if (account != null) { account.LoginToken = AuthHash .GetHash256( $"{context.Channel.RemoteAddress}-{account.Username}-{account.Password}") .ToLower(); account.LastLogin = $"{DateTimeOffset.UtcNow:yyyyMMddHHmmss}"; account.AuthToken = ""; account.newToken = ""; db.UpdateAsync(account); var ack = new CCMessage(); ack.Write(true); ack.Write(account.LoginToken); RmiSend(context, 16, ack); } var authhistory = new AuthHistoryDto() { AccountId = account.Id, Date = DateTime.UtcNow.ToString(), HWID = hwid }; db.Insert(authhistory); Logger.Information("Authentication success for {username}", username); if (account.LoginToken != "" && account.LoginToken.Length == 64) { account.IsConnect = true; db.Update(account); } } } else { Logger.Error( "Wrong authentication credentials for {username} / {endpoint}", username, context.Channel.RemoteAddress.ToString()); var ack = new CCMessage(); ack.Write(false); ack.Write("Invalid length of username/password"); RmiSend(context, 16, ack); } } } catch (Exception e) { Logger.Error(e.ToString()); var ack = new CCMessage(); ack.Write(false); ack.Write("Login Error"); RmiSend(context, 16, ack); } HwidBan: { var error = new CCMessage(); error.Write(false); error.Write("You Banned."); RmiSend(context, 16, error); return; } loginerror: { var error = new CCMessage(); error.Write(false); error.Write("Failed to login."); RmiSend(context, 16, error); return; } } }
public static async Task <bool> IsNickAvailableAsync(string nickname) { var minLength = Config.Instance.Game.NickRestrictions.MinLength; var maxLength = Config.Instance.Game.NickRestrictions.MaxLength; var whitespace = Config.Instance.Game.NickRestrictions.WhitespaceAllowed; var ascii = Config.Instance.Game.NickRestrictions.AsciiOnly; if (ascii) { if (nickname.Any(c => c > 127)) { return(false); } } else { if (nickname.Any(c => c > 255)) { return(false); } } if (!Namecheck.IsNameValid(nickname)) { return(false); } if (nickname.Length < minLength || nickname.Length > maxLength || ascii && Encoding.UTF8.GetByteCount(nickname) != nickname.Length) { return(false); } // check for repeating chars example: (AAAHello, HeLLLLo) var maxRepeat = Config.Instance.Game.NickRestrictions.MaxRepeat; if (maxRepeat > 0) { var counter = 1; var current = nickname[0]; for (var i = 1; i < nickname.Length; i++) { if (current != nickname[i]) { if (counter > maxRepeat) { return(false); } counter = 0; current = nickname[i]; } counter++; } } var now = DateTimeOffset.Now.ToUnixTimeSeconds(); using (var db = AuthDatabase.Open()) { var nickExists = (await DbUtil.FindAsync <AccountDto>(db, statement => statement .Where($"{nameof(AccountDto.Nickname):C} = @{nameof(nickname)}") .WithParameters(new { nickname }))) .Any(); var nickReserved = (await DbUtil.FindAsync <NicknameHistoryDto>(db, statement => statement .Where( $"{nameof(NicknameHistoryDto.OldName):C} = @{nameof(nickname)} AND ({nameof(NicknameHistoryDto.ExpireDate):C} = -1 OR {nameof(NicknameHistoryDto.ExpireDate):C} > @{nameof(now)})") .WithParameters(new { nickname, now }))) .Any(); return(!nickExists && !nickReserved); } }