otclient-master\modules\gamelib/protocollogin.lua

Created Diff never expires
1 removal
275 lines
3 additions
277 lines
-- @docclass
-- @docclass
ProtocolLogin = extends(Protocol, "ProtocolLogin")
ProtocolLogin = extends(Protocol, "ProtocolLogin")


LoginServerError = 10
LoginServerError = 10
LoginServerTokenSuccess = 12
LoginServerTokenSuccess = 12
LoginServerTokenError = 13
LoginServerTokenError = 13
LoginServerUpdate = 17
LoginServerUpdate = 17
LoginServerMotd = 20
LoginServerMotd = 20
LoginServerUpdateNeeded = 30
LoginServerUpdateNeeded = 30
LoginServerSessionKey = 40
LoginServerSessionKey = 40
LoginServerCharacterList = 100
LoginServerCharacterList = 100
LoginServerExtendedCharacterList = 101
LoginServerExtendedCharacterList = 101


-- Since 10.76
-- Since 10.76
LoginServerRetry = 10
LoginServerRetry = 10
LoginServerErrorNew = 11
LoginServerErrorNew = 11


function ProtocolLogin:login(host, port, accountName, accountPassword, authenticatorToken, stayLogged)
function ProtocolLogin:login(host, port, accountName, accountPassword, authenticatorToken, stayLogged)
if string.len(host) == 0 or port == nil or port == 0 then
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."))
signalcall(self.onLoginError, self, tr("You must enter a valid server address and port."))
return
return
end
end


self.accountName = accountName
self.accountName = accountName
self.accountPassword = accountPassword
self.accountPassword = accountPassword
self.authenticatorToken = authenticatorToken
self.authenticatorToken = authenticatorToken
self.stayLogged = stayLogged
self.stayLogged = stayLogged
self.connectCallback = self.sendLoginPacket
self.connectCallback = self.sendLoginPacket


self:connect(host, port)
self:connect(host, port)
end
end


function ProtocolLogin:cancelLogin()
function ProtocolLogin:cancelLogin()
self:disconnect()
self:disconnect()
end
end


function ProtocolLogin:sendLoginPacket()
function ProtocolLogin:sendLoginPacket()
local msg = OutputMessage.create()
local msg = OutputMessage.create()
msg:addU8(ClientOpcodes.ClientEnterAccount)
msg:addU8(ClientOpcodes.ClientEnterAccount)
msg:addU16(g_game.getOs())
msg:addU16(g_game.getOs())


msg:addU16(g_game.getProtocolVersion())
msg:addU16(g_game.getProtocolVersion())


if g_game.getFeature(GameClientVersion) then
if g_game.getFeature(GameClientVersion) then
msg:addU32(g_game.getClientVersion())
msg:addU32(g_game.getClientVersion())
end
end


if g_game.getFeature(GameContentRevision) then
if g_game.getFeature(GameContentRevision) then
msg:addU16(g_things.getContentRevision())
msg:addU16(g_things.getContentRevision())
msg:addU16(0)
msg:addU16(0)
else
else
msg:addU32(g_things.getDatSignature())
msg:addU32(g_things.getDatSignature())
end
end
msg:addU32(g_sprites.getSprSignature())
msg:addU32(g_sprites.getSprSignature())
msg:addU32(PIC_SIGNATURE)
msg:addU32(PIC_SIGNATURE)


if g_game.getFeature(GamePreviewState) then
if g_game.getFeature(GamePreviewState) then
msg:addU8(0)
msg:addU8(0)
end
end


local offset = msg:getMessageSize()
local offset = msg:getMessageSize()
if g_game.getFeature(GameLoginPacketEncryption) then
if g_game.getFeature(GameLoginPacketEncryption) then
-- first RSA byte must be 0
-- first RSA byte must be 0
msg:addU8(0)
msg:addU8(0)


-- xtea key
-- xtea key
self:generateXteaKey()
self:generateXteaKey()
local xteaKey = self:getXteaKey()
local xteaKey = self:getXteaKey()
msg:addU32(xteaKey[1])
msg:addU32(xteaKey[1])
msg:addU32(xteaKey[2])
msg:addU32(xteaKey[2])
msg:addU32(xteaKey[3])
msg:addU32(xteaKey[3])
msg:addU32(xteaKey[4])
msg:addU32(xteaKey[4])
end
end


if g_game.getFeature(GameAccountNames) then
if g_game.getFeature(GameAccountNames) then
msg:addString(self.accountName)
msg:addString(self.accountName)
else
else
msg:addU32(tonumber(self.accountName))
msg:addU32(tonumber(self.accountName))
end
end


msg:addString(self.accountPassword)
msg:addString(self.accountPassword)


if self.getLoginExtendedData then
if self.getLoginExtendedData then
local data = self:getLoginExtendedData()
local data = self:getLoginExtendedData()
msg:addString(data)
msg:addString(data)
end
end


local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
local paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
assert(paddingBytes >= 0)
assert(paddingBytes >= 0)
for i = 1, paddingBytes do
for i = 1, paddingBytes do
msg:addU8(math.random(0, 0xff))
msg:addU8(math.random(0, 0xff))
end
end


if g_game.getFeature(GameLoginPacketEncryption) then
if g_game.getFeature(GameLoginPacketEncryption) then
msg:encryptRsa()
msg:encryptRsa()
end
end


if g_game.getFeature(GameOGLInformation) then
if g_game.getFeature(GameOGLInformation) then
msg:addU8(1) --unknown
msg:addU8(1) --unknown
msg:addU8(1) --unknown
msg:addU8(1) --unknown


if g_game.getClientVersion() >= 1072 then
if g_game.getClientVersion() >= 1072 then
msg:addString(string.format('%s %s', g_graphics.getVendor(), g_graphics.getRenderer()))
msg:addString(string.format('%s %s', g_graphics.getVendor(), g_graphics.getRenderer()))
else
else
msg:addString(g_graphics.getRenderer())
msg:addString(g_graphics.getRenderer())
end
end
msg:addString(g_graphics.getVersion())
msg:addString(g_graphics.getVersion())
end
end


-- add RSA encrypted auth token
-- add RSA encrypted auth token
if g_game.getFeature(GameAuthenticator) then
if g_game.getFeature(GameAuthenticator) then
offset = msg:getMessageSize()
offset = msg:getMessageSize()


-- first RSA byte must be 0
-- first RSA byte must be 0
msg:addU8(0)
msg:addU8(0)
msg:addString(self.authenticatorToken)
msg:addString(self.authenticatorToken)


if g_game.getFeature(GameSessionKey) then
if g_game.getFeature(GameSessionKey) then
msg:addU8(booleantonumber(self.stayLogged))
msg:addU8(booleantonumber(self.stayLogged))
end
end


paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
paddingBytes = g_crypt.rsaGetSize() - (msg:getMessageSize() - offset)
assert(paddingBytes >= 0)
assert(paddingBytes >= 0)
for i = 1, paddingBytes do
for i = 1, paddingBytes do
msg:addU8(math.random(0, 0xff))
msg:addU8(math.random(0, 0xff))
end
end


msg:encryptRsa()
msg:encryptRsa()
end
end


if g_game.getFeature(GameProtocolChecksum) then
if g_game.getFeature(GameProtocolChecksum) then
self:enableChecksum()
self:enableChecksum()
end
end


self:send(msg)
self:send(msg)
if g_game.getFeature(GameLoginPacketEncryption) then
if g_game.getFeature(GameLoginPacketEncryption) then
self:enableXteaEncryption()
self:enableXteaEncryption()
end
end
self:recv()
self:recv()
end
end


function ProtocolLogin:onConnect()
function ProtocolLogin:onConnect()
self.gotConnection = true
self.gotConnection = true
self:connectCallback()
self:connectCallback()
self.connectCallback = nil
self.connectCallback = nil
end
end


function ProtocolLogin:onRecv(msg)
function ProtocolLogin:onRecv(msg)
while not msg:eof() do
while not msg:eof() do
local opcode = msg:getU8()
local opcode = msg:getU8()
if opcode == LoginServerErrorNew then
if opcode == LoginServerErrorNew then
self:parseError(msg)
self:parseError(msg)
elseif opcode == LoginServerError then
elseif opcode == LoginServerError then
self:parseError(msg)
self:parseError(msg)
elseif opcode == LoginServerMotd then
elseif opcode == LoginServerMotd then
self:parseMotd(msg)
self:parseMotd(msg)
elseif opcode == LoginServerUpdateNeeded then
elseif opcode == LoginServerUpdateNeeded then
signalcall(self.onLoginError, self, tr("Client needs update."))
signalcall(self.onLoginError, self, tr("Client needs update."))
elseif opcode == LoginServerTokenSuccess then
elseif opcode == LoginServerTokenSuccess then
local unknown = msg:getU8()
local unknown = msg:getU8()
elseif opcode == LoginServerTokenError then
elseif opcode == LoginServerTokenError then
-- TODO: prompt for token here
-- TODO: prompt for token here
local unknown = msg:getU8()
local unknown = msg:getU8()
signalcall(self.onLoginError, self, tr("Invalid authentification token."))
signalcall(self.onLoginError, self, tr("Invalid authentification token."))
elseif opcode == LoginServerCharacterList then
elseif opcode == LoginServerCharacterList then
self:parseCharacterList(msg)
self:parseCharacterList(msg)
elseif opcode == LoginServerExtendedCharacterList then
elseif opcode == LoginServerExtendedCharacterList then
self:parseExtendedCharacterList(msg)
self:parseExtendedCharacterList(msg)
elseif opcode == LoginServerUpdate then
elseif opcode == LoginServerUpdate then
local signature = msg:getString()
local signature = msg:getString()
signalcall(self.onUpdateNeeded, self, signature)
signalcall(self.onUpdateNeeded, self, signature)
elseif opcode == LoginServerSessionKey then
elseif opcode == LoginServerSessionKey then
self:parseSessionKey(msg)
self:parseSessionKey(msg)
else
else
self:parseOpcode(opcode, msg)
self:parseOpcode(opcode, msg)
end
end
end
end
self:disconnect()
self:disconnect()
end
end


function ProtocolLogin:parseError(msg)
function ProtocolLogin:parseError(msg)
local errorMessage = msg:getString()
local errorMessage = msg:getString()
signalcall(self.onLoginError, self, errorMessage)
signalcall(self.onLoginError, self, errorMessage)
end
end


function ProtocolLogin:parseMotd(msg)
function ProtocolLogin:parseMotd(msg)
local motd = msg:getString()
local motd = msg:getString()
signalcall(self.onMotd, self, motd)
signalcall(self.onMotd, self, motd)
end
end


function ProtocolLogin:parseSessionKey(msg)
function ProtocolLogin:parseSessionKey(msg)
local sessionKey = msg:getString()
local sessionKey = msg:getString()
signalcall(self.onSessionKey, self, sessionKey)
signalcall(self.onSessionKey, self, sessionKey)
end
end


function ProtocolLogin:parseCharacterList(msg)
function ProtocolLogin:parseCharacterList(msg)
local characters = {}
local characters = {}


if g_game.getClientVersion() > 1010 then
if g_game.getClientVersion() > 1010 then
local worlds = {}
local worlds = {}


local worldsCount = msg:getU8()
local worldsCount = msg:getU8()
for i=1, worldsCount do
for i=1, worldsCount do
local world = {}
local world = {}
local worldId = msg:getU8()
local worldId = msg:getU8()
world.worldName = msg:getString()
world.worldName = msg:getString()
world.worldIp = msg:getString()
world.worldIp = msg:getString()
world.worldIp = "127.0.0.1"
world.worldPort = msg:getU16()
world.worldPort = msg:getU16()
world.previewState = msg:getU8()
world.previewState = msg:getU8()
worlds[worldId] = world
worlds[worldId] = world
end
end


local charactersCount = msg:getU8()
local charactersCount = msg:getU8()
for i=1, charactersCount do
for i=1, charactersCount do
local character = {}
local character = {}
local worldId = msg:getU8()
local worldId = msg:getU8()
character.name = msg:getString()
character.name = msg:getString()
character.worldName = worlds[worldId].worldName
character.worldName = worlds[worldId].worldName
character.worldIp = worlds[worldId].worldIp
character.worldIp = worlds[worldId].worldIp
character.worldIp = "127.0.0.1"
character.worldPort = worlds[worldId].worldPort
character.worldPort = worlds[worldId].worldPort
character.previewState = worlds[worldId].previewState
character.previewState = worlds[worldId].previewState
characters[i] = character
characters[i] = character
end
end


else
else
local charactersCount = msg:getU8()
local charactersCount = msg:getU8()
for i=1,charactersCount do
for i=1,charactersCount do
local character = {}
local character = {}
character.name = msg:getString()
character.name = msg:getString()
character.worldName = msg:getString()
character.worldName = msg:getString()
character.worldIp = iptostring(msg:getU32())
character.worldIp = iptostring(msg:getU32())
character.worldIp = "127.0.0.1"
character.worldPort = msg:getU16()
character.worldPort = msg:getU16()


if g_game.getFeature(GamePreviewState) then
if g_game.getFeature(GamePreviewState) then
character.previewState = msg:getU8()
character.previewState = msg:getU8()
end
end


characters[i] = character
characters[i] = character
end
end
end
end


local account = {}
local account = {}
if g_game.getProtocolVersion() > 1077 then
if g_game.getProtocolVersion() > 1077 then
account.status = msg:getU8()
account.status = msg:getU8()
account.subStatus = msg:getU8()
account.subStatus = msg:getU8()


account.premDays = msg:getU32()
account.premDays = msg:getU32()
if account.premDays ~= 0 and account.premDays ~= 65535 then
if account.premDays ~= 0 and account.premDays ~= 65535 then
account.premDays = math.floor((account.premDays - os.time()) / 86400)
account.premDays = math.floor((account.premDays - os.time()) / 86400)
end
end
else
else
account.status = AccountStatus.Ok
account.status = AccountStatus.Ok
account.premDays = msg:getU16()
account.premDays = msg:getU16()
account.subStatus = account.premDays > 0 and SubscriptionStatus.Premium or SubscriptionStatus.Free
account.subStatus = account.premDays > 0 and SubscriptionStatus.Premium or SubscriptionStatus.Free
end
end


signalcall(self.onCharacterList, self, characters, account)
signalcall(self.onCharacterList, self, characters, account)
end
end


function ProtocolLogin:parseExtendedCharacterList(msg)
function ProtocolLogin:parseExtendedCharacterList(msg)
local characters = msg:getTable()
local characters = msg:getTable()
local account = msg:getTable()
local account = msg:getTable()
local otui = msg:getString()
local otui = msg:getString()
signalcall(self.onCharacterList, self, characters, account, otui)
signalcall(self.onCharacterList, self, characters, account, otui)
end
end


function ProtocolLogin:parseOpcode(opcode, msg)
function ProtocolLogin:parseOpcode(opcode, msg)
signalcall(self.onOpcode, self, opcode, msg)
signalcall(self.onOpcode, self, opcode, msg)
end
end


function ProtocolLogin:onError(msg, code)
function ProtocolLogin:onError(msg, code)
local text = translateNetworkError(code, self:isConnecting(), msg)
local text = translateNetworkError(code, self:isConnecting(), msg)
signalcall(self.onLoginError, self, text)
signalcall(self.onLoginError, self, text)
end
end