2012-06-26 00:13:30 +02:00
|
|
|
-- @docclass
|
2014-06-06 18:10:14 +02:00
|
|
|
ProtocolLogin = extends(Protocol, "ProtocolLogin")
|
2012-06-05 23:27:37 +02:00
|
|
|
|
2012-07-18 01:49:21 +02:00
|
|
|
LoginServerError = 10
|
2015-01-18 15:14:07 +01:00
|
|
|
LoginServerTokenSuccess = 12
|
|
|
|
LoginServerTokenError = 13
|
2013-01-18 23:39:11 +01:00
|
|
|
LoginServerUpdate = 17
|
2012-07-18 01:49:21 +02:00
|
|
|
LoginServerMotd = 20
|
|
|
|
LoginServerUpdateNeeded = 30
|
2015-01-27 23:44:37 +01:00
|
|
|
LoginServerSessionKey = 40
|
2012-07-18 01:49:21 +02:00
|
|
|
LoginServerCharacterList = 100
|
2012-08-30 07:59:10 +02:00
|
|
|
LoginServerExtendedCharacterList = 101
|
2012-07-18 01:49:21 +02:00
|
|
|
|
2015-03-09 16:46:26 +01:00
|
|
|
-- Since 10.76
|
|
|
|
LoginServerRetry = 10
|
|
|
|
LoginServerErrorNew = 11
|
|
|
|
|
2015-01-27 23:44:37 +01:00
|
|
|
function ProtocolLogin:login(host, port, accountName, accountPassword, authenticatorToken, stayLogged)
|
2012-07-26 08:10:28 +02:00
|
|
|
if string.len(host) == 0 or port == nil or port == 0 then
|
2013-02-21 21:31:47 +01:00
|
|
|
signalcall(self.onLoginError, self, tr("You must enter a valid server address and port."))
|
2012-07-26 08:10:28 +02:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
self.accountName = accountName
|
|
|
|
self.accountPassword = accountPassword
|
2015-01-18 15:14:07 +01:00
|
|
|
self.authenticatorToken = authenticatorToken
|
2015-01-27 23:44:37 +01:00
|
|
|
self.stayLogged = stayLogged
|
2013-02-28 03:45:03 +01:00
|
|
|
self.connectCallback = self.sendLoginPacket
|
2012-07-26 08:10:28 +02:00
|
|
|
|
|
|
|
self:connect(host, port)
|
|
|
|
end
|
2012-07-18 01:49:21 +02:00
|
|
|
|
2012-07-26 08:10:28 +02:00
|
|
|
function ProtocolLogin:cancelLogin()
|
|
|
|
self:disconnect()
|
|
|
|
end
|
|
|
|
|
2013-02-19 02:18:10 +01:00
|
|
|
function ProtocolLogin:sendLoginPacket()
|
2012-06-05 23:27:37 +02:00
|
|
|
local msg = OutputMessage.create()
|
2013-12-23 23:42:58 +01:00
|
|
|
msg:addU8(ClientOpcodes.ClientEnterAccount)
|
|
|
|
msg:addU16(g_game.getOs())
|
|
|
|
|
2012-12-28 12:05:45 +01:00
|
|
|
msg:addU16(g_game.getProtocolVersion())
|
|
|
|
|
2014-12-29 18:08:13 +01:00
|
|
|
if g_game.getFeature(GameClientVersion) then
|
2012-12-28 12:05:45 +01:00
|
|
|
msg:addU32(g_game.getClientVersion())
|
|
|
|
end
|
2012-06-05 23:27:37 +02:00
|
|
|
|
2014-12-29 18:08:13 +01:00
|
|
|
if g_game.getFeature(GameContentRevision) then
|
|
|
|
msg:addU16(g_things.getContentRevision())
|
|
|
|
msg:addU16(0)
|
|
|
|
else
|
|
|
|
msg:addU32(g_things.getDatSignature())
|
|
|
|
end
|
2012-06-21 19:54:20 +02:00
|
|
|
msg:addU32(g_sprites.getSprSignature())
|
2012-07-18 01:49:21 +02:00
|
|
|
msg:addU32(PIC_SIGNATURE)
|
2012-06-05 23:27:37 +02:00
|
|
|
|
2014-12-29 18:08:13 +01:00
|
|
|
if g_game.getFeature(GamePreviewState) then
|
|
|
|
msg:addU8(0)
|
2013-01-03 23:42:02 +01:00
|
|
|
end
|
|
|
|
|
2013-01-28 23:52:03 +01:00
|
|
|
local offset = msg:getMessageSize()
|
2014-12-29 18:08:13 +01:00
|
|
|
if g_game.getFeature(GameLoginPacketEncryption) then
|
2013-12-23 23:42:58 +01:00
|
|
|
-- first RSA byte must be 0
|
|
|
|
msg:addU8(0)
|
2014-12-29 18:08:13 +01:00
|
|
|
|
2013-10-04 04:09:54 +02:00
|
|
|
-- 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
|
2014-01-18 15:09:26 +01:00
|
|
|
|
2012-06-05 23:27:37 +02:00
|
|
|
if g_game.getFeature(GameAccountNames) then
|
2012-07-26 08:10:28 +02:00
|
|
|
msg:addString(self.accountName)
|
2012-06-05 23:27:37 +02:00
|
|
|
else
|
2012-07-26 08:10:28 +02:00
|
|
|
msg:addU32(tonumber(self.accountName))
|
2012-06-05 23:27:37 +02:00
|
|
|
end
|
|
|
|
|
2013-01-28 23:52:03 +01:00
|
|
|
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)
|
2015-01-18 15:14:07 +01:00
|
|
|
for i = 1, paddingBytes do
|
|
|
|
msg:addU8(math.random(0, 0xff))
|
|
|
|
end
|
|
|
|
|
2014-12-29 18:08:13 +01:00
|
|
|
if g_game.getFeature(GameLoginPacketEncryption) then
|
2013-10-04 04:09:54 +02:00
|
|
|
msg:encryptRsa()
|
|
|
|
end
|
2014-01-18 15:09:26 +01:00
|
|
|
|
2014-12-29 18:08:13 +01:00
|
|
|
if g_game.getFeature(GameOGLInformation) then
|
|
|
|
msg:addU8(1) --unknown
|
|
|
|
msg:addU8(1) --unknown
|
2015-01-18 15:14:07 +01:00
|
|
|
|
|
|
|
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
|
2014-12-29 18:08:13 +01:00
|
|
|
msg:addString(g_graphics.getVersion())
|
|
|
|
end
|
|
|
|
|
2015-01-18 15:14:07 +01:00
|
|
|
-- 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)
|
|
|
|
|
2015-01-27 23:44:37 +01:00
|
|
|
if g_game.getFeature(GameSessionKey) then
|
|
|
|
msg:addU8(booleantonumber(self.stayLogged))
|
|
|
|
end
|
|
|
|
|
2015-01-18 15:14:07 +01:00
|
|
|
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
|
|
|
|
|
2013-01-28 23:52:03 +01:00
|
|
|
if g_game.getFeature(GameProtocolChecksum) then
|
|
|
|
self:enableChecksum()
|
|
|
|
end
|
2012-06-05 23:27:37 +02:00
|
|
|
|
2012-07-26 08:10:28 +02:00
|
|
|
self:send(msg)
|
2014-12-29 18:08:13 +01:00
|
|
|
if g_game.getFeature(GameLoginPacketEncryption) then
|
2013-10-04 04:09:54 +02:00
|
|
|
self:enableXteaEncryption()
|
|
|
|
end
|
2012-07-26 08:10:28 +02:00
|
|
|
self:recv()
|
2012-06-05 23:27:37 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function ProtocolLogin:onConnect()
|
2013-02-22 22:49:36 +01:00
|
|
|
self.gotConnection = true
|
2013-02-28 03:45:03 +01:00
|
|
|
self:connectCallback()
|
|
|
|
self.connectCallback = nil
|
2012-06-05 23:27:37 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function ProtocolLogin:onRecv(msg)
|
|
|
|
while not msg:eof() do
|
|
|
|
local opcode = msg:getU8()
|
2015-03-09 16:46:26 +01:00
|
|
|
if opcode == LoginServerErrorNew then
|
|
|
|
self:parseError(msg)
|
|
|
|
elseif opcode == LoginServerError then
|
2012-06-05 23:27:37 +02:00
|
|
|
self:parseError(msg)
|
|
|
|
elseif opcode == LoginServerMotd then
|
|
|
|
self:parseMotd(msg)
|
|
|
|
elseif opcode == LoginServerUpdateNeeded then
|
2013-02-21 21:31:47 +01:00
|
|
|
signalcall(self.onLoginError, self, tr("Client needs update."))
|
2015-01-27 23:44:37 +01:00
|
|
|
elseif opcode == LoginServerTokenSuccess then
|
|
|
|
local unknown = msg:getU8()
|
2015-01-18 15:14:07 +01:00
|
|
|
elseif opcode == LoginServerTokenError then
|
|
|
|
-- TODO: prompt for token here
|
2015-01-27 23:44:37 +01:00
|
|
|
local unknown = msg:getU8()
|
2015-01-18 15:14:07 +01:00
|
|
|
signalcall(self.onLoginError, self, tr("Invalid authentification token."))
|
2012-06-05 23:27:37 +02:00
|
|
|
elseif opcode == LoginServerCharacterList then
|
|
|
|
self:parseCharacterList(msg)
|
2012-08-30 07:59:10 +02:00
|
|
|
elseif opcode == LoginServerExtendedCharacterList then
|
|
|
|
self:parseExtendedCharacterList(msg)
|
2013-01-18 23:39:11 +01:00
|
|
|
elseif opcode == LoginServerUpdate then
|
|
|
|
local signature = msg:getString()
|
|
|
|
signalcall(self.onUpdateNeeded, self, signature)
|
2015-01-27 23:44:37 +01:00
|
|
|
elseif opcode == LoginServerSessionKey then
|
|
|
|
self:parseSessionKey(msg)
|
2012-06-05 23:27:37 +02:00
|
|
|
else
|
|
|
|
self:parseOpcode(opcode, msg)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
self:disconnect()
|
|
|
|
end
|
|
|
|
|
|
|
|
function ProtocolLogin:parseError(msg)
|
|
|
|
local errorMessage = msg:getString()
|
2013-02-21 21:31:47 +01:00
|
|
|
signalcall(self.onLoginError, self, errorMessage)
|
2012-06-05 23:27:37 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function ProtocolLogin:parseMotd(msg)
|
|
|
|
local motd = msg:getString()
|
|
|
|
signalcall(self.onMotd, self, motd)
|
|
|
|
end
|
|
|
|
|
2015-01-27 23:44:37 +01:00
|
|
|
function ProtocolLogin:parseSessionKey(msg)
|
|
|
|
local sessionKey = msg:getString()
|
|
|
|
signalcall(self.onSessionKey, self, sessionKey)
|
|
|
|
end
|
|
|
|
|
2012-06-05 23:27:37 +02:00
|
|
|
function ProtocolLogin:parseCharacterList(msg)
|
|
|
|
local characters = {}
|
2013-11-10 06:13:51 +01:00
|
|
|
|
2014-08-03 00:02:28 +02:00
|
|
|
if g_game.getClientVersion() > 1010 then
|
2013-11-10 06:13:51 +01:00
|
|
|
local worlds = {}
|
|
|
|
|
|
|
|
local worldsCount = msg:getU8()
|
|
|
|
for i=1, worldsCount do
|
|
|
|
local world = {}
|
2014-01-18 15:09:26 +01:00
|
|
|
local worldId = msg:getU8()
|
2013-11-10 06:13:51 +01:00
|
|
|
world.worldName = msg:getString()
|
2013-11-10 06:17:49 +01:00
|
|
|
world.worldIp = msg:getString()
|
2013-11-10 06:13:51 +01:00
|
|
|
world.worldPort = msg:getU16()
|
2014-12-29 18:08:13 +01:00
|
|
|
world.previewState = msg:getU8()
|
2013-11-10 06:13:51 +01:00
|
|
|
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
|
2014-12-29 18:08:13 +01:00
|
|
|
character.previewState = worlds[worldId].previewState
|
2013-11-10 06:13:51 +01:00
|
|
|
characters[i] = character
|
2012-12-28 12:05:45 +01:00
|
|
|
end
|
2013-02-19 02:18:10 +01:00
|
|
|
|
2013-11-10 06:13:51 +01:00
|
|
|
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()
|
|
|
|
|
2014-12-29 18:08:13 +01:00
|
|
|
if g_game.getFeature(GamePreviewState) then
|
|
|
|
character.previewState = msg:getU8()
|
2013-11-10 06:13:51 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
characters[i] = character
|
|
|
|
end
|
2012-06-05 23:27:37 +02:00
|
|
|
end
|
2012-08-30 07:59:10 +02:00
|
|
|
|
|
|
|
local account = {}
|
2018-01-01 05:10:57 +01:00
|
|
|
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
|
|
|
|
|
2012-08-30 07:59:10 +02:00
|
|
|
signalcall(self.onCharacterList, self, characters, account)
|
|
|
|
end
|
|
|
|
|
|
|
|
function ProtocolLogin:parseExtendedCharacterList(msg)
|
|
|
|
local characters = msg:getTable()
|
|
|
|
local account = msg:getTable()
|
2012-08-31 06:56:10 +02:00
|
|
|
local otui = msg:getString()
|
|
|
|
signalcall(self.onCharacterList, self, characters, account, otui)
|
2012-06-05 23:27:37 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
function ProtocolLogin:parseOpcode(opcode, msg)
|
|
|
|
signalcall(self.onOpcode, self, opcode, msg)
|
|
|
|
end
|
2013-02-21 21:31:47 +01:00
|
|
|
|
|
|
|
function ProtocolLogin:onError(msg, code)
|
2013-02-27 20:24:32 +01:00
|
|
|
local text = translateNetworkError(code, self:isConnecting(), msg)
|
2013-02-22 22:49:36 +01:00
|
|
|
signalcall(self.onLoginError, self, text)
|
2018-01-01 05:10:57 +01:00
|
|
|
end
|