mirror of
				https://github.com/ErikasKontenis/SabrehavenServer.git
				synced 2025-10-31 03:56:22 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			301 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			301 lines
		
	
	
		
			8.3 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
 | |
| LoginServerProxyList = 110
 | |
| 
 | |
| -- 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())
 | |
|   if g_game.getCustomProtocolVersion() > 0 then
 | |
|     msg:addU16(g_game.getCustomProtocolVersion())  
 | |
|   else
 | |
|     msg:addU16(g_game.getProtocolVersion())
 | |
|   end
 | |
| 
 | |
|   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))
 | |
|     msg:addU32(tonumber(self.accountName))
 | |
|   end
 | |
| 
 | |
|   msg:addString(self.accountPassword)
 | |
| 
 | |
|   if self.getLoginExtendedData then
 | |
|     local data = self:getLoginExtendedData()
 | |
|     msg:addString(data)
 | |
|   else
 | |
|     msg:addString("OTCv8")
 | |
|     local version = g_app.getVersion():split(" ")[1]:gsub("%.", "")
 | |
|     if version:len() == 2 then
 | |
|       version = version .. "0" 
 | |
|     end
 | |
|     msg:addU16(tonumber(version))
 | |
|   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(GamePacketSizeU32) then
 | |
|     self:enableBigPackets()
 | |
|   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)
 | |
|     elseif opcode == LoginServerProxyList then
 | |
|       local proxies = {}
 | |
|       local proxiesCount = msg:getU8()
 | |
|       for i=1, proxiesCount do
 | |
|         local host = msg:getString()
 | |
|         local port = msg:getU16()
 | |
|         local priority = msg:getU16()        
 | |
|         table.insert(proxies, {host=host, port=port, priority=priority})
 | |
|       end      
 | |
|       signalcall(self.onProxyList, self, proxies)
 | |
|     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
 | 
