mirror of
				https://github.com/ErikasKontenis/SabrehavenServer.git
				synced 2025-10-31 03:56:22 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			423 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			423 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| CharacterList = { }
 | |
| 
 | |
| -- private variables
 | |
| local charactersWindow
 | |
| local loadBox
 | |
| local characterList
 | |
| local errorBox
 | |
| local waitingWindow
 | |
| local autoReconnectButton
 | |
| local updateWaitEvent
 | |
| local resendWaitEvent
 | |
| local loginEvent
 | |
| local autoReconnectEvent
 | |
| local lastLogout = 0
 | |
| 
 | |
| -- private functions
 | |
| local function tryLogin(charInfo, tries)
 | |
|   tries = tries or 1
 | |
| 
 | |
|   if tries > 50 then
 | |
|     return
 | |
|   end
 | |
| 
 | |
|   if g_game.isOnline() then
 | |
|     if tries == 1 then
 | |
|       g_game.safeLogout()
 | |
|     end
 | |
|     loginEvent = scheduleEvent(function() tryLogin(charInfo, tries+1) end, 100)
 | |
|     return
 | |
|   end
 | |
| 
 | |
|   CharacterList.hide()
 | |
|   g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName, G.authenticatorToken, G.sessionKey)
 | |
|   g_logger.info("Login to " .. charInfo.worldHost .. ":" .. charInfo.worldPort)
 | |
|   loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to game server...'))
 | |
|   connect(loadBox, { onCancel = function()
 | |
|                                   loadBox = nil
 | |
|                                   g_game.cancelLogin()
 | |
|                                   CharacterList.show()
 | |
|                                 end })
 | |
| 
 | |
|   -- save last used character
 | |
|   g_settings.set('last-used-character', charInfo.characterName)
 | |
|   g_settings.set('last-used-world', charInfo.worldName)
 | |
| end
 | |
| 
 | |
| local function updateWait(timeStart, timeEnd)
 | |
|   if waitingWindow then
 | |
|     local time = g_clock.seconds()
 | |
|     if time <= timeEnd then
 | |
|       local percent = ((time - timeStart) / (timeEnd - timeStart)) * 100
 | |
|       local timeStr = string.format("%.0f", timeEnd - time)
 | |
| 
 | |
|       local progressBar = waitingWindow:getChildById('progressBar')
 | |
|       progressBar:setPercent(percent)
 | |
| 
 | |
|       local label = waitingWindow:getChildById('timeLabel')
 | |
|       label:setText(tr('Trying to reconnect in %s seconds.', timeStr))
 | |
| 
 | |
|       updateWaitEvent = scheduleEvent(function() updateWait(timeStart, timeEnd) end, 1000 * progressBar:getPercentPixels() / 100 * (timeEnd - timeStart))
 | |
|       return true
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   if updateWaitEvent then
 | |
|     updateWaitEvent:cancel()
 | |
|     updateWaitEvent = nil
 | |
|   end
 | |
| end
 | |
| 
 | |
| local function resendWait()
 | |
|   if waitingWindow then
 | |
|     waitingWindow:destroy()
 | |
|     waitingWindow = nil
 | |
| 
 | |
|     if updateWaitEvent then
 | |
|       updateWaitEvent:cancel()
 | |
|       updateWaitEvent = nil
 | |
|     end
 | |
| 
 | |
|     if charactersWindow then
 | |
|       local selected = characterList:getFocusedChild()
 | |
|       if selected then
 | |
|         local charInfo = { worldHost = selected.worldHost,
 | |
|                            worldPort = selected.worldPort,
 | |
|                            worldName = selected.worldName,
 | |
|                            characterName = selected.characterName }
 | |
|         tryLogin(charInfo)
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 | |
| 
 | |
| local function onLoginWait(message, time)
 | |
|   CharacterList.destroyLoadBox()
 | |
| 
 | |
|   waitingWindow = g_ui.displayUI('waitinglist')
 | |
| 
 | |
|   local label = waitingWindow:getChildById('infoLabel')
 | |
|   label:setText(message)
 | |
| 
 | |
|   updateWaitEvent = scheduleEvent(function() updateWait(g_clock.seconds(), g_clock.seconds() + time) end, 0)
 | |
|   resendWaitEvent = scheduleEvent(resendWait, time * 1000)
 | |
| end
 | |
| 
 | |
| function onGameLoginError(message)
 | |
|   CharacterList.destroyLoadBox()
 | |
|   errorBox = displayErrorBox(tr("Login Error"), message)
 | |
|   errorBox.onOk = function()
 | |
|     errorBox = nil
 | |
|     CharacterList.showAgain()
 | |
|   end
 | |
|   scheduleAutoReconnect()
 | |
| end
 | |
| 
 | |
| function onGameLoginToken(unknown)
 | |
|   CharacterList.destroyLoadBox()
 | |
|   -- TODO: make it possible to enter a new token here / prompt token
 | |
|   errorBox = displayErrorBox(tr("Two-Factor Authentification"), 'A new authentification token is required.\nPlease login again.')
 | |
|   errorBox.onOk = function()
 | |
|     errorBox = nil
 | |
|     EnterGame.show()
 | |
|   end
 | |
| end
 | |
| 
 | |
| function onGameConnectionError(message, code)
 | |
|   CharacterList.destroyLoadBox()
 | |
|   if (not g_game.isOnline() or code ~= 2) and not errorBox then -- code 2 is normal disconnect, end of file
 | |
|     local text = translateNetworkError(code, g_game.getProtocolGame() and g_game.getProtocolGame():isConnecting(), message)
 | |
|     errorBox = displayErrorBox(tr("Connection Error"), text)
 | |
|     errorBox.onOk = function()
 | |
|       errorBox = nil
 | |
|       CharacterList.showAgain()
 | |
|     end
 | |
|   end
 | |
|   scheduleAutoReconnect()
 | |
| end
 | |
| 
 | |
| function onGameUpdateNeeded(signature)
 | |
|   CharacterList.destroyLoadBox()
 | |
|   errorBox = displayErrorBox(tr("Update needed"), tr('Enter with your account again to update your client.'))
 | |
|   errorBox.onOk = function()
 | |
|     errorBox = nil
 | |
|     CharacterList.showAgain()
 | |
|   end  
 | |
| end
 | |
| 
 | |
| function onGameEnd()
 | |
|   scheduleAutoReconnect()
 | |
|   CharacterList.showAgain()
 | |
| end
 | |
| 
 | |
| function onLogout()
 | |
|   lastLogout = g_clock.millis()
 | |
| end
 | |
| 
 | |
| function scheduleAutoReconnect()
 | |
|   if lastLogout + 2000 > g_clock.millis() then
 | |
|     return
 | |
|   end
 | |
|   if autoReconnectEvent then
 | |
|     removeEvent(autoReconnectEvent)    
 | |
|   end
 | |
|   autoReconnectEvent = scheduleEvent(executeAutoReconnect, 2500)
 | |
| end
 | |
| 
 | |
| function executeAutoReconnect()  
 | |
|   if not autoReconnectButton or not autoReconnectButton:isOn() or g_game.isOnline() then
 | |
|     return
 | |
|   end
 | |
|   if errorBox then
 | |
|     errorBox:destroy()
 | |
|     errorBox = nil
 | |
|   end
 | |
|   CharacterList.doLogin()
 | |
| end
 | |
| 
 | |
| -- public functions
 | |
| function CharacterList.init()
 | |
|   if USE_NEW_ENERGAME then return end
 | |
|   connect(g_game, { onLoginError = onGameLoginError })
 | |
|   connect(g_game, { onLoginToken = onGameLoginToken })
 | |
|   connect(g_game, { onUpdateNeeded = onGameUpdateNeeded })
 | |
|   connect(g_game, { onConnectionError = onGameConnectionError })
 | |
|   connect(g_game, { onGameStart = CharacterList.destroyLoadBox })
 | |
|   connect(g_game, { onLoginWait = onLoginWait })
 | |
|   connect(g_game, { onGameEnd = onGameEnd })
 | |
|   connect(g_game, { onLogout = onLogout })
 | |
| 
 | |
|   if G.characters then
 | |
|     CharacterList.create(G.characters, G.characterAccount)
 | |
|   end
 | |
| end
 | |
| 
 | |
| function CharacterList.terminate()
 | |
|  if USE_NEW_ENERGAME then return end
 | |
|   disconnect(g_game, { onLoginError = onGameLoginError })
 | |
|   disconnect(g_game, { onLoginToken = onGameLoginToken })
 | |
|   disconnect(g_game, { onUpdateNeeded = onGameUpdateNeeded })
 | |
|   disconnect(g_game, { onConnectionError = onGameConnectionError })
 | |
|   disconnect(g_game, { onGameStart = CharacterList.destroyLoadBox })
 | |
|   disconnect(g_game, { onLoginWait = onLoginWait })
 | |
|   disconnect(g_game, { onGameEnd = onGameEnd })
 | |
|   disconnect(g_game, { onLogout = onLogout })
 | |
| 
 | |
|   if charactersWindow then
 | |
|     characterList = nil
 | |
|     charactersWindow:destroy()
 | |
|     charactersWindow = nil
 | |
|   end
 | |
| 
 | |
|   if loadBox then
 | |
|     g_game.cancelLogin()
 | |
|     loadBox:destroy()
 | |
|     loadBox = nil
 | |
|   end
 | |
| 
 | |
|   if waitingWindow then
 | |
|     waitingWindow:destroy()
 | |
|     waitingWindow = nil
 | |
|   end
 | |
| 
 | |
|   if updateWaitEvent then
 | |
|     removeEvent(updateWaitEvent)
 | |
|     updateWaitEvent = nil
 | |
|   end
 | |
| 
 | |
|   if resendWaitEvent then
 | |
|     removeEvent(resendWaitEvent)
 | |
|     resendWaitEvent = nil
 | |
|   end
 | |
| 
 | |
|   if loginEvent then
 | |
|     removeEvent(loginEvent)
 | |
|     loginEvent = nil
 | |
|   end
 | |
| 
 | |
|   CharacterList = nil
 | |
| end
 | |
| 
 | |
| function CharacterList.create(characters, account, otui)
 | |
|   if not otui then otui = 'characterlist' end
 | |
|   if charactersWindow then
 | |
|     charactersWindow:destroy()
 | |
|   end
 | |
| 
 | |
|   charactersWindow = g_ui.displayUI(otui)
 | |
|   characterList = charactersWindow:getChildById('characters')
 | |
|   autoReconnectButton = charactersWindow:getChildById('autoReconnect')
 | |
| 
 | |
|   -- characters
 | |
|   G.characters = characters
 | |
|   G.characterAccount = account
 | |
| 
 | |
|   characterList:destroyChildren()
 | |
|   local accountStatusLabel = charactersWindow:getChildById('accountStatusLabel')
 | |
|   local focusLabel
 | |
|   for i,characterInfo in ipairs(characters) do
 | |
|     local widget = g_ui.createWidget('CharacterWidget', characterList)
 | |
|     for key,value in pairs(characterInfo) do
 | |
|       local subWidget = widget:getChildById(key)
 | |
|       if subWidget then
 | |
|         if key == 'outfit' then -- it's an exception
 | |
|           subWidget:setOutfit(value)
 | |
|         else
 | |
|           local text = value
 | |
|           if subWidget.baseText and subWidget.baseTranslate then
 | |
|             text = tr(subWidget.baseText, text)
 | |
|           elseif subWidget.baseText then
 | |
|             text = string.format(subWidget.baseText, text)
 | |
|           end
 | |
|           subWidget:setText(text)
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     -- these are used by login
 | |
|     widget.characterName = characterInfo.name
 | |
|     widget.worldName = characterInfo.worldName
 | |
|     widget.worldHost = characterInfo.worldIp
 | |
|     widget.worldPort = characterInfo.worldPort
 | |
| 
 | |
|     connect(widget, { onDoubleClick = function () CharacterList.doLogin() return true end } )
 | |
| 
 | |
|     if i == 1 or (g_settings.get('last-used-character') == widget.characterName and g_settings.get('last-used-world') == widget.worldName) then
 | |
|       focusLabel = widget
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   if focusLabel then
 | |
|     characterList:focusChild(focusLabel, KeyboardFocusReason)
 | |
|     addEvent(function() characterList:ensureChildVisible(focusLabel) end)
 | |
|   end
 | |
|   
 | |
|   characterList.onChildFocusChange = function()
 | |
|     removeEvent(autoReconnectEvent)
 | |
|     autoReconnectEvent = nil
 | |
|   end
 | |
| 
 | |
|   -- account
 | |
|   local status = ''
 | |
|   if account.status == AccountStatus.Frozen then
 | |
|     status = tr(' (Frozen)')
 | |
|   elseif account.status == AccountStatus.Suspended then
 | |
|     status = tr(' (Suspended)')
 | |
|   end
 | |
| 
 | |
|   if account.subStatus == SubscriptionStatus.Free and account.premDays < 1 then
 | |
|     accountStatusLabel:setText(('%s%s'):format(tr('Free Account'), status))
 | |
|   else
 | |
|     if account.premDays == 0 or account.premDays == 65535 then
 | |
|       accountStatusLabel:setText(('%s%s'):format(tr('Gratis Premium Account'), status))
 | |
|     else
 | |
|       accountStatusLabel:setText(('%s%s'):format(tr('Premium Account (%s) days left', account.premDays), status))
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   if account.premDays > 0 and account.premDays <= 7 then
 | |
|     accountStatusLabel:setOn(true)
 | |
|   else
 | |
|     accountStatusLabel:setOn(false)
 | |
|   end
 | |
|   
 | |
|   autoReconnectButton.onClick = function(widget)
 | |
|     local autoReconnect = not g_settings.getBoolean('autoReconnect', true)
 | |
|     autoReconnectButton:setOn(autoReconnect)
 | |
|     g_settings.set('autoReconnect', autoReconnect)
 | |
|   end
 | |
| end
 | |
| 
 | |
| function CharacterList.destroy()
 | |
|   CharacterList.hide(true)
 | |
| 
 | |
|   if charactersWindow then
 | |
|     characterList = nil
 | |
|     charactersWindow:destroy()
 | |
|     charactersWindow = nil
 | |
|   end
 | |
| end
 | |
| 
 | |
| function CharacterList.show()
 | |
|   if loadBox or errorBox or not charactersWindow then return end
 | |
|   charactersWindow:show()
 | |
|   charactersWindow:raise()
 | |
|   charactersWindow:focus()
 | |
|   
 | |
|   local autoReconnect = g_settings.getBoolean('autoReconnect', true)
 | |
|   autoReconnectButton:setOn(autoReconnect)
 | |
| end
 | |
| 
 | |
| function CharacterList.hide(showLogin)
 | |
|   removeEvent(autoReconnectEvent)
 | |
|   autoReconnectEvent = nil
 | |
| 
 | |
|   showLogin = showLogin or false
 | |
|   charactersWindow:hide()
 | |
| 
 | |
|   if showLogin and EnterGame and not g_game.isOnline() then
 | |
|     EnterGame.show()
 | |
|   end
 | |
| end
 | |
| 
 | |
| function CharacterList.showAgain()
 | |
|   if characterList and characterList:hasChildren() then
 | |
|     CharacterList.show()
 | |
|   end
 | |
| end
 | |
| 
 | |
| function CharacterList.isVisible()
 | |
|   if charactersWindow and charactersWindow:isVisible() then
 | |
|     return true
 | |
|   end
 | |
|   return false
 | |
| end
 | |
| 
 | |
| function CharacterList.doLogin()
 | |
|   removeEvent(autoReconnectEvent)
 | |
|   autoReconnectEvent = nil
 | |
| 
 | |
|   local selected = characterList:getFocusedChild()
 | |
|   if selected then
 | |
|     local charInfo = { worldHost = selected.worldHost,
 | |
|                        worldPort = selected.worldPort,
 | |
|                        worldName = selected.worldName,
 | |
|                        characterName = selected.characterName }
 | |
|     charactersWindow:hide()
 | |
|     if loginEvent then
 | |
|       removeEvent(loginEvent)
 | |
|       loginEvent = nil
 | |
|     end
 | |
|     tryLogin(charInfo)
 | |
|   else
 | |
|     displayErrorBox(tr('Error'), tr('You must select a character to login!'))
 | |
|   end
 | |
| end
 | |
| 
 | |
| function CharacterList.destroyLoadBox()
 | |
|   if loadBox then
 | |
|     loadBox:destroy()
 | |
|     loadBox = nil
 | |
|   end
 | |
| end
 | |
| 
 | |
| function CharacterList.cancelWait()
 | |
|   if waitingWindow then
 | |
|     waitingWindow:destroy()
 | |
|     waitingWindow = nil
 | |
|   end
 | |
| 
 | |
|   if updateWaitEvent then
 | |
|     removeEvent(updateWaitEvent)
 | |
|     updateWaitEvent = nil
 | |
|   end
 | |
| 
 | |
|   if resendWaitEvent then
 | |
|     removeEvent(resendWaitEvent)
 | |
|     resendWaitEvent = nil
 | |
|   end
 | |
| 
 | |
|   CharacterList.destroyLoadBox()
 | |
|   CharacterList.showAgain()
 | |
| end
 | 
