tibia-client/modules/gamelib/protocollogin.lua

275 lines
7.4 KiB
Lua

-- @docclass
ProtocolLogin = extends(Protocol, "ProtocolLogin")
LoginServerError = 10
LoginServerTokenSuccess = 12
LoginServerTokenError = 13
LoginServerUpdate = 17
LoginServerMotd = 20
LoginServerUpdateNeeded = 30
LoginServerSessionKey = 40
LoginServerCharacterList = 100
LoginServerExtendedCharacterList = 101
-- Since 10.76
LoginServerRetry = 10
LoginServerErrorNew = 11
function ProtocolLogin:login(host, port, accountName, accountPassword, authenticatorToken, stayLogged)
if string.len(host) == 0 or port == nil or port == 0 then
signalcall(self.onLoginError, self, tr("You must enter a valid server address and port."))
return
end
self.accountName = accountName
self.accountPassword = accountPassword
self.authenticatorToken = authenticatorToken
self.stayLogged = stayLogged
self.connectCallback = self.sendLoginPacket
self:connect(host, port)
end
function ProtocolLogin:cancelLogin()
self:disconnect()
end
function ProtocolLogin:sendLoginPacket()
local msg = OutputMessage.create()
msg:addU8(ClientOpcodes.ClientEnterAccount)
msg:addU16(g_game.getOs())
msg:addU16(g_game.getProtocolVersion())
if g_game.getFeature(GameClientVersion) then
msg:addU32(g_game.getClientVersion())
end
if g_game.getFeature(GameContentRevision) then
msg:addU16(g_things.getContentRevision())
msg:addU16(0)
else
msg:addU32(g_things.getDatSignature())
end
msg:addU32(g_sprites.getSprSignature())
msg:addU32(PIC_SIGNATURE)
if g_game.getFeature(GamePreviewState) then
msg:addU8(0)
end
local offset = msg:getMessageSize()
if g_game.getFeature(GameLoginPacketEncryption) then
-- first RSA byte must be 0
msg:addU8(0)
-- xtea key
self:generateXteaKey()
local xteaKey = self:getXteaKey()
msg:addU32(xteaKey[1])
msg:addU32(xteaKey[2])
msg:addU32(xteaKey[3])
msg:addU32(xteaKey[4])
end
if g_game.getFeature(GameAccountNames) then
msg:addString(self.accountName)
else
msg:addU32(tonumber(self.accountName))
end
msg:addString(self.accountPassword)
if self.getLoginExtendedData then
local data = self:getLoginExtendedData()
msg:addString(data)
end
local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
assert(paddingBytes >= 0)
for i = 1, paddingBytes do
msg:addU8(math.random(0, 0xff))
end
if g_game.getFeature(GameLoginPacketEncryption) then
msg:encryptRsa()
end
if g_game.getFeature(GameOGLInformation) then
msg:addU8(1) --unknown
msg:addU8(1) --unknown
if g_game.getClientVersion() >= 1072 then
msg:addString(string.format('%s %s', g_graphics.getVendor(), g_graphics.getRenderer()))
else
msg:addString(g_graphics.getRenderer())
end
msg:addString(g_graphics.getVersion())
end
-- add RSA encrypted auth token
if g_game.getFeature(GameAuthenticator) then
offset = msg:getMessageSize()
-- first RSA byte must be 0
msg:addU8(0)
msg:addString(self.authenticatorToken)
if g_game.getFeature(GameSessionKey) then
msg:addU8(booleantonumber(self.stayLogged))
end
paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
assert(paddingBytes >= 0)
for i = 1, paddingBytes do
msg:addU8(math.random(0, 0xff))
end
msg:encryptRsa()
end
if g_game.getFeature(GameProtocolChecksum) then
self:enableChecksum()
end
self:send(msg)
if g_game.getFeature(GameLoginPacketEncryption) then
self:enableXteaEncryption()
end
self:recv()
end
function ProtocolLogin:onConnect()
self.gotConnection = true
self:connectCallback()
self.connectCallback = nil
end
function ProtocolLogin:onRecv(msg)
while not msg:eof() do
local opcode = msg:getU8()
if opcode == LoginServerErrorNew then
self:parseError(msg)
elseif opcode == LoginServerError then
self:parseError(msg)
elseif opcode == LoginServerMotd then
self:parseMotd(msg)
elseif opcode == LoginServerUpdateNeeded then
signalcall(self.onLoginError, self, tr("Client needs update."))
elseif opcode == LoginServerTokenSuccess then
local unknown = msg:getU8()
elseif opcode == LoginServerTokenError then
-- TODO: prompt for token here
local unknown = msg:getU8()
signalcall(self.onLoginError, self, tr("Invalid authentification token."))
elseif opcode == LoginServerCharacterList then
self:parseCharacterList(msg)
elseif opcode == LoginServerExtendedCharacterList then
self:parseExtendedCharacterList(msg)
elseif opcode == LoginServerUpdate then
local signature = msg:getString()
signalcall(self.onUpdateNeeded, self, signature)
elseif opcode == LoginServerSessionKey then
self:parseSessionKey(msg)
else
self:parseOpcode(opcode, msg)
end
end
self:disconnect()
end
function ProtocolLogin:parseError(msg)
local errorMessage = msg:getString()
signalcall(self.onLoginError, self, errorMessage)
end
function ProtocolLogin:parseMotd(msg)
local motd = msg:getString()
signalcall(self.onMotd, self, motd)
end
function ProtocolLogin:parseSessionKey(msg)
local sessionKey = msg:getString()
signalcall(self.onSessionKey, self, sessionKey)
end
function ProtocolLogin:parseCharacterList(msg)
local characters = {}
if g_game.getClientVersion() > 1010 then
local worlds = {}
local worldsCount = msg:getU8()
for i=1, worldsCount do
local world = {}
local worldId = msg:getU8()
world.worldName = msg:getString()
world.worldIp = msg:getString()
world.worldPort = msg:getU16()
world.previewState = msg:getU8()
worlds[worldId] = world
end
local charactersCount = msg:getU8()
for i=1, charactersCount do
local character = {}
local worldId = msg:getU8()
character.name = msg:getString()
character.worldName = worlds[worldId].worldName
character.worldIp = worlds[worldId].worldIp
character.worldPort = worlds[worldId].worldPort
character.previewState = worlds[worldId].previewState
characters[i] = character
end
else
local charactersCount = msg:getU8()
for i=1,charactersCount do
local character = {}
character.name = msg:getString()
character.worldName = msg:getString()
character.worldIp = iptostring(msg:getU32())
character.worldPort = msg:getU16()
if g_game.getFeature(GamePreviewState) then
character.previewState = msg:getU8()
end
characters[i] = character
end
end
local account = {}
if g_game.getProtocolVersion() > 1077 then
account.status = msg:getU8()
account.subStatus = msg:getU8()
account.premDays = msg:getU32()
if account.premDays ~= 0 and account.premDays ~= 65535 then
account.premDays = math.floor((account.premDays - os.time()) / 86400)
end
else
account.status = AccountStatus.Ok
account.premDays = msg:getU16()
account.subStatus = account.premDays > 0 and SubscriptionStatus.Premium or SubscriptionStatus.Free
end
signalcall(self.onCharacterList, self, characters, account)
end
function ProtocolLogin:parseExtendedCharacterList(msg)
local characters = msg:getTable()
local account = msg:getTable()
local otui = msg:getString()
signalcall(self.onCharacterList, self, characters, account, otui)
end
function ProtocolLogin:parseOpcode(opcode, msg)
signalcall(self.onOpcode, self, opcode, msg)
end
function ProtocolLogin:onError(msg, code)
local text = translateNetworkError(code, self:isConnecting(), msg)
signalcall(self.onLoginError, self, text)
end