Berniemack Posted August 6, 2025 Posted August 6, 2025 So, I may have had this working before, honestly ive been staying up so late these days I cant remember if I dreamed that this worked at one point or if I actually had it working. Either way, into the issue, I believe I am completing the login handshake just fine. the code runs properly as expected when i step through manually and we send the AuthResponsePacket heres my console messages: Quote info: OpenConquer.AccountServer.LoginHandshakeService[0] LoginHandshakeService listening on port 9959 info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down. info: Microsoft.Hosting.Lifetime[0] Hosting environment: Development info: Microsoft.Hosting.Lifetime[0] Content root path: D:\repos\OpenConquer\OpenConquer.AccountServer info: OpenConquer.AccountServer.LoginHandshakeService[0] Accepted login connection from redacted:50747 info: OpenConquer.AccountServer.LoginHandshakeService[0] Beginning handshake for redacted:50747 info: OpenConquer.AccountServer.Session.LoginClientSession[0] Starting handshake for redacted:50747 info: OpenConquer.AccountServer.Session.LoginClientSession[0] SeedResponsePacket sent (Seed=81136454) info: OpenConquer.AccountServer.Session.LoginClientSession[0] Received login request (Len=276 Id=1060) info: OpenConquer.AccountServer.Session.LoginClientSession[0] Parsed LoginRequest for storm info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (23ms) [Parameters=[@__username_0='?' (Size = 4000)], CommandType='Text', CommandTimeout='30'] SELECT `a`.`uid`, `a`.`answer`, `a`.`Email`, `a`.`hash`, `a`.`password`, `a`.`permission`, `a`.`question`, `a`.`timestamp`, `a`.`username` FROM `accounts` AS `a` WHERE `a`.`username` = @__username_0 LIMIT 1 info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (3ms) [Parameters=[@__p_0='?' (DbType = UInt32)], CommandType='Text', CommandTimeout='30'] SELECT `a`.`uid`, `a`.`answer`, `a`.`Email`, `a`.`hash`, `a`.`password`, `a`.`permission`, `a`.`question`, `a`.`timestamp`, `a`.`username` FROM `accounts` AS `a` WHERE `a`.`uid` = @__p_0 LIMIT 1 info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (5ms) [Parameters=[@p2='?' (DbType = UInt32), @p0='?' (DbType = UInt32), @p1='?' (DbType = UInt32)], CommandType='Text', CommandTimeout='30'] SET AUTOCOMMIT = 1; UPDATE `accounts` SET `hash` = @p0, `timestamp` = @p1 WHERE `uid` = @p2; SELECT ROW_COUNT(); info: OpenConquer.AccountServer.Session.LoginClientSession[0] AuthResponse ␦ Port=5816, ExternalIp='redacted' info: OpenConquer.AccountServer.Session.LoginClientSession[0] ␦ AuthResponsePacket bytes: 36-00-1F-04-01-00-00-00-09-00-00-00-B8-16-00-00-00-00-00-00-31-39-32-2E-31-36-38-2E-31-2E-35-38-00-00-00-00 info: OpenConquer.AccountServer.Session.LoginClientSession[0] Sent AuthResponse (Key=9) for storm info: OpenConquer.AccountServer.Session.LoginClientSession[0] Handshake complete, closed login session for redacted:50747 info: OpenConquer.AccountServer.LoginHandshakeService[0] Completed handshake for redacted:50747 ignore the closed login session message I commented out my Disconnect call to see if I was just disconnecting early but no. I even tried awaiting another packet from the client to see if I was missing a step or something but nothing ever came from the client after I sent the AuthResponsePacket. After the following code it just never connects to the gameserver private async Task<AuthResponsePacket> BuildResponseAsync(string user, string pass, CancellationToken ct) { uint loginSessionKey = _keyProvider.NextKey(); uint accountSessionHash = (uint)Random.Shared.Next(1, 1000000); Account? acct = await _accounts.GetByUsernameAsync(user).ConfigureAwait(false); if (acct is null) { acct = new Account { Username = user, Password = pass, Permission = PlayerPermission.Player, Hash = accountSessionHash, Timestamp = (uint)DateTimeOffset.UtcNow.ToUnixTimeSeconds() }; acct = await _accounts.CreateAsync(acct, ct).ConfigureAwait(false); if (acct is null || acct.UID == 0) { return AuthResponsePacket.CreateInvalid(); } } else if (acct.Password != pass || acct.Permission == PlayerPermission.Error) { return AuthResponsePacket.CreateInvalid(); } else if (acct.Permission == PlayerPermission.Banned) { return new AuthResponsePacket { Key = AuthResponsePacket.RESPONSE_BANNED }; } else { acct.Hash = accountSessionHash; await _accounts.UpdateHashAsync(acct.UID, accountSessionHash, ct).ConfigureAwait(false); } return new AuthResponsePacket { UID = loginSessionKey, Key = acct.UID, Port = (uint)_gamePort, ExternalIp = _externalIp }; } private async Task RespondAsync(LoginRequestPacket req, uint seed, CancellationToken ct) { string password = DecryptPassword(req, seed); var resp = await BuildResponseAsync(req.Username, password, ct).ConfigureAwait(false); _logger.LogInformation("AuthResponse: Port={Port}, ExternalIp='{IP}'", resp.Port, resp.ExternalIp); var outBuf = PacketWriter.Serialize(resp); _logger.LogInformation("AuthResponsePacket bytes: {Hex}", BitConverter.ToString(outBuf)); await SendToClientAsync(outBuf, ct).ConfigureAwait(false); _logger.LogInformation("Sent AuthResponse (Key={Key}) for {User}", resp.Key, req.Username); } public async Task HandleHandshakeAsync(CancellationToken ct) { var endpoint = _tcpClient.Client.RemoteEndPoint; _logger.LogInformation("Starting handshake for {Endpoint}", endpoint); try { uint seed = await SendSeedAsync(ct); var (pktLen, pktId, fullPacket) = await ReadAndDecryptRequestAsync(ct); _logger.LogInformation("Received login request (Len={Len} Id={Id})", pktLen, pktId); var req = LoginRequestPacket.Parse(fullPacket); _logger.LogInformation("Parsed LoginRequest for {User}", req.Username); await RespondAsync(req, seed, ct).ConfigureAwait(false); await Task.Delay(100, ct); //Disconnect(); _logger.LogInformation("Handshake complete, closed login session for {Endpoint}", endpoint); } catch (OperationCanceledException) { _logger.LogInformation("Handshake canceled for {Endpoint}", endpoint); Disconnect(); } catch (Exception ex) { _logger.LogError(ex, "Handshake failed for {Endpoint}", endpoint); Disconnect(); } } I dont think its the Gameserver code as an issue or a firewall issue because I can ping the gameserver from powershell and it will get the connection and try to parse the ping. but the client just wont connect. using conquerloader to launch the client with these settings [Loader] IPAddress=redacted <--- port forwarded address yes I have checked that on both ports for login and gameport are port forwarded LoginPort=9959 GamePort=5816 Website=http://www.elitepvpers.de Force=TRUE Wondering if anyone sees a blatant flaw in my implementation that im not seeing, or could just give me a few ideas of where or what to check, I would greatly appreciate it, been racking my brain on this one since yesterday. Quote
Berniemack Posted August 6, 2025 Author Posted August 6, 2025 Heres an example of me pinging the gameserver so I know its listening and receiving the client is just never reaching out to it properly for some reason. info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (8ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT `l`.`profession`, `l`.`level`, `l`.`Agility`, `l`.`Health`, `l`.`Mana`, `l`.`Spirit`, `l`.`Strength`, `l`.`Vitality` FROM `LevelStats` AS `l` ORDER BY `l`.`profession`, `l`.`level` info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT `e`.`curve_type`, `e`.`level`, `e`.`experience` FROM `ExperienceCurves` AS `e` ORDER BY `e`.`curve_type`, `e`.`level` info: OpenConquer.GameServer.Calculations.Implementation.ExperienceService[0] ExperienceService initialized: loaded 720 level-stat entries across 6 professions, 141 curve entries info: OpenConquer.GameServer.GameHandshakeService[0] GameHandshakeService listening on port 5816 info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl+C to shut down. info: Microsoft.Hosting.Lifetime[0] Hosting environment: Development info: Microsoft.Hosting.Lifetime[0] Content root path: D:\repos\OpenConquer\OpenConquer.GameServer info: OpenConquer.GameServer.GameHandshakeService[0] Accepted game connection from redacted:53377 fail: OpenConquer.GameServer.GameHandshakeService[0] Error during game handshake for redacted:53377 System.InvalidOperationException: Unable to resolve service for type 'OpenConquer.Protocol.Packets.Parsers.PacketParserRegistry' while attempting to activate 'OpenConquer.GameServer.Session.GameClientSession'. at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider) at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters) at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance[T](IServiceProvider provider, Object[] parameters) at OpenConquer.GameServer.GameHandshakeService.HandleClientAsync(TcpClient client, CancellationToken ct) in D:\repos\OpenConquer\OpenConquer.GameServer\GameHandshakeService.cs:line 65 info: OpenConquer.GameServer.GameHandshakeService[0] Closed game connection for redacted:53377 it would expect to error out as I am building it out to expect specific packets only (i'll flesh that out to swallow invalid packets or something properly later) but this shows that the code on the gameserver is working as I expect it to, if the darn client would just send it a packet. Quote
Berniemack Posted August 6, 2025 Author Posted August 6, 2025 I figured it out in case any one was wondering or stumbles into this in the future, despite albetros saying that the auth response packet length should be 54 bytes on the wire they actually only write 36, changed my length from 54 to 36 and now were cooking again boys! So note to anyone in the future working on a 5517 server the auth response packet length should be 36 Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.