mirror of
https://github.com/OTCv8/otclientv8.git
synced 2025-10-19 14:13:27 +02:00
Version 2.1 - imbuements, wrap/unwrap, 4 byte header, packet compression and other features
This commit is contained in:
@@ -1,26 +1,27 @@
|
||||
local entergameWindow
|
||||
local characterGroup
|
||||
local outfitGroup
|
||||
local protocol
|
||||
local infoBox
|
||||
local loadingBox
|
||||
|
||||
function init()
|
||||
if not USE_NEW_ENERGAME then return end
|
||||
entergameWindow = g_ui.displayUI('entergamev2')
|
||||
|
||||
--entergameWindow.news:hide()
|
||||
--entergameWindow.quick:hide()
|
||||
entergameWindow.news:hide()
|
||||
entergameWindow.quick:hide()
|
||||
entergameWindow.registration:hide()
|
||||
entergameWindow.characters:hide()
|
||||
entergameWindow.createcharacter:hide()
|
||||
entergameWindow.settings:hide()
|
||||
|
||||
-- entergame
|
||||
entergameWindow.entergame.register.onClick = function()
|
||||
entergameWindow.registration:show()
|
||||
entergameWindow.entergame:hide()
|
||||
end
|
||||
entergameWindow.entergame.mainPanel.button.onClick = function()
|
||||
entergameWindow.entergame:hide()
|
||||
entergameWindow.characters:show()
|
||||
g_game.setClientVersion(1099) -- for tests
|
||||
end
|
||||
entergameWindow.entergame.mainPanel.button.onClick = login
|
||||
|
||||
-- registration
|
||||
entergameWindow.registration.back.onClick = function()
|
||||
@@ -29,44 +30,455 @@ function init()
|
||||
end
|
||||
|
||||
-- characters
|
||||
--- outfits
|
||||
entergameWindow.characters.mainPanel.showOutfits.onClick = function()
|
||||
local status = not (entergameWindow.characters.mainPanel.showOutfits:isOn())
|
||||
g_settings.set('showOutfits', status)
|
||||
entergameWindow.characters.mainPanel.showOutfits:setOn(status)
|
||||
if status then
|
||||
entergameWindow.characters.mainPanel.outfitsPanel:show()
|
||||
entergameWindow.characters.mainPanel.outfitsScroll:show()
|
||||
entergameWindow.characters.mainPanel.charactersPanel:hide()
|
||||
entergameWindow.characters.mainPanel.charactersScroll:hide()
|
||||
else
|
||||
entergameWindow.characters.mainPanel.outfitsPanel:hide()
|
||||
entergameWindow.characters.mainPanel.outfitsScroll:hide()
|
||||
entergameWindow.characters.mainPanel.charactersPanel:show()
|
||||
entergameWindow.characters.mainPanel.charactersScroll:show()
|
||||
end
|
||||
end
|
||||
|
||||
local showOutfits = g_settings.getBoolean("showOutfits", false)
|
||||
entergameWindow.characters.mainPanel.showOutfits:setOn(showOutfits)
|
||||
if showOutfits then
|
||||
entergameWindow.characters.mainPanel.charactersPanel:hide()
|
||||
entergameWindow.characters.mainPanel.charactersScroll:hide()
|
||||
else
|
||||
entergameWindow.characters.mainPanel.outfitsPanel:hide()
|
||||
entergameWindow.characters.mainPanel.outfitsScroll:hide()
|
||||
end
|
||||
|
||||
--- auto reconnect
|
||||
entergameWindow.characters.mainPanel.autoReconnect.onClick = function()
|
||||
local status = (not entergameWindow.characters.mainPanel.autoReconnect:isOn())
|
||||
g_settings.set('autoReconnect', status)
|
||||
entergameWindow.characters.mainPanel.autoReconnect:setOn(status)
|
||||
end
|
||||
local autoReconnect = g_settings.getBoolean("autoReconnect", true)
|
||||
entergameWindow.characters.mainPanel.autoReconnect:setOn(autoReconnect)
|
||||
|
||||
--- buttons
|
||||
entergameWindow.characters.logout.onClick = function()
|
||||
protocol:logout()
|
||||
entergameWindow.characters:hide()
|
||||
entergameWindow.entergame:show()
|
||||
entergameWindow.entergame.mainPanel.account:setText("")
|
||||
entergameWindow.entergame.mainPanel.password:setText("")
|
||||
end
|
||||
entergameWindow.characters.createcharacter.onClick = function()
|
||||
entergameWindow.characters:hide()
|
||||
entergameWindow.createcharacter:show()
|
||||
entergameWindow.createcharacter.mainPanel.name:setText("")
|
||||
end
|
||||
entergameWindow.characters.mainPanel.autoReconnect.onClick = function()
|
||||
entergameWindow.characters.mainPanel.autoReconnect:setOn(not entergameWindow.characters.mainPanel.autoReconnect:isOn())
|
||||
end
|
||||
entergameWindow.characters.settings.onClick = function()
|
||||
entergameWindow.characters:hide()
|
||||
entergameWindow.settings:show()
|
||||
end
|
||||
|
||||
-- create character
|
||||
entergameWindow.createcharacter.back.onClick = function()
|
||||
entergameWindow.createcharacter:hide()
|
||||
entergameWindow.characters:show()
|
||||
end
|
||||
|
||||
-- tests
|
||||
characterGroup = UIRadioGroup.create()
|
||||
for i=1,20 do
|
||||
local character = g_ui.createWidget('EntergameCharacter', entergameWindow.characters.mainPanel.charactersPanel)
|
||||
characterGroup:addWidget(character)
|
||||
character.outfit:setOutfit({feet=10,legs=10,body=176,type=129,auxType=0,addons=3,head=48})
|
||||
entergameWindow.createcharacter.mainPanel.createButton.onClick = createcharacter
|
||||
|
||||
entergameWindow.settings.back.onClick = function()
|
||||
entergameWindow.settings:hide()
|
||||
entergameWindow.characters:show()
|
||||
end
|
||||
characterGroup:selectWidget(entergameWindow.characters.mainPanel.charactersPanel:getFirstChild())
|
||||
characterGroup:getSelectedWidget()
|
||||
entergameWindow.settings.mainPanel.updateButton.onClick = updateSettings
|
||||
|
||||
for i=1,100 do
|
||||
local l = g_ui.createWidget("NewsLabel", entergameWindow.news.content)
|
||||
l:setText("test xxx ssss eeee uu u llel " .. i)
|
||||
-- pick server
|
||||
local server = nil
|
||||
if type(Servers) == "table" then
|
||||
for name, url in pairs(Servers) do
|
||||
server = url
|
||||
end
|
||||
elseif type(Servers) == "string" then
|
||||
server = Servers
|
||||
elseif type(Server) == "string" then
|
||||
server = Server
|
||||
end
|
||||
|
||||
if not server then
|
||||
message("Configuration error", "You must set server url in init.lua!\nExample:\nServer = \"ws://otclient.ovh:8000\"")
|
||||
return
|
||||
end
|
||||
|
||||
-- init protocol
|
||||
-- token is random string
|
||||
local session = g_crypt.sha1Encode("" .. math.random() .. g_clock.realMicros() .. tostring(G.UUID) .. g_platform.getCPUName() .. g_platform.getProcessId())
|
||||
protocol = EnterGameV2Protocol.new(session)
|
||||
if not protocol:setUrl(server) then
|
||||
return message("Configuration error", "Invalid url for entergamev2:\n" .. server)
|
||||
end
|
||||
|
||||
protocol.onLogin = onLogin
|
||||
protocol.onLogout = logout
|
||||
protocol.onMessage = serverMessage
|
||||
protocol.onLoading = showLoading
|
||||
protocol.onQAuth = updateQAuth
|
||||
protocol.onCharacters = updateCharacters
|
||||
protocol.onNews = updateNews
|
||||
protocol.onMotd = updateMotd
|
||||
protocol.onCharacterCreate = onCharacterCreate
|
||||
|
||||
-- game stuff
|
||||
connect(g_game, { onLoginError = onLoginError,
|
||||
onLoginToken = onLoginToken ,
|
||||
onUpdateNeeded = onUpdateNeeded,
|
||||
onConnectionError = onConnectionError,
|
||||
onGameStart = onGameStart,
|
||||
onGameEnd = onGameEnd,
|
||||
onLoginWait = onLoginWait,
|
||||
onLogout = onLogout
|
||||
})
|
||||
|
||||
if g_game.isOnline() then
|
||||
onGameStart()
|
||||
end
|
||||
end
|
||||
|
||||
function terminate()
|
||||
if not USE_NEW_ENERGAME then return end
|
||||
if protocol then
|
||||
protocol:destroy()
|
||||
protocol = nil
|
||||
end
|
||||
if infoBox then
|
||||
infoBox:destroy()
|
||||
infoBox = nil
|
||||
end
|
||||
if loadingBox then
|
||||
loadingBox:destroy()
|
||||
loadingBox = nil
|
||||
end
|
||||
if characterGroup then
|
||||
characterGroup:destroy()
|
||||
characterGroup = nil
|
||||
end
|
||||
if outfitGroup then
|
||||
outfitGroup:destroy()
|
||||
outfitGroup = nil
|
||||
end
|
||||
entergameWindow:destroy()
|
||||
entergameWindow = nil
|
||||
|
||||
disconnect(g_game, { onLoginError = onLoginError,
|
||||
onLoginToken = onLoginToken ,
|
||||
onUpdateNeeded = onUpdateNeeded,
|
||||
onConnectionError = onConnectionError,
|
||||
onGameStart = onGameStart,
|
||||
onGameEnd = onGameEnd,
|
||||
onLoginWait = onLoginWait,
|
||||
onLogout = onLogout
|
||||
})
|
||||
end
|
||||
|
||||
function show()
|
||||
|
||||
end
|
||||
|
||||
function hide()
|
||||
|
||||
end
|
||||
|
||||
function message(title, text)
|
||||
if infoBox then
|
||||
infoBox:destroy()
|
||||
end
|
||||
|
||||
infoBox = displayInfoBox(title, text)
|
||||
infoBox.onDestroy = function(widget)
|
||||
if widget == infoBox then
|
||||
infoBox = nil
|
||||
end
|
||||
end
|
||||
infoBox:show()
|
||||
infoBox:raise()
|
||||
infoBox:focus()
|
||||
end
|
||||
|
||||
function showLoading(titie, text)
|
||||
if loadingBox then
|
||||
loadingBox:destroy()
|
||||
end
|
||||
|
||||
local callback = function() end -- do nothing
|
||||
loadingBox = displayGeneralBox(titie, text, {}, callback, callback)
|
||||
loadingBox.onDestroy = function(widget)
|
||||
if widget == loadingBox then
|
||||
loadingBox = nil
|
||||
end
|
||||
end
|
||||
loadingBox:show()
|
||||
loadingBox:raise()
|
||||
loadingBox:focus()
|
||||
end
|
||||
|
||||
function serverMessage(title, text)
|
||||
return message(title, text)
|
||||
end
|
||||
|
||||
function updateCharacters(characters)
|
||||
if outfitGroup then
|
||||
outfitGroup:destroy()
|
||||
end
|
||||
if characterGroup then
|
||||
characterGroup:destroy()
|
||||
end
|
||||
entergameWindow.characters.mainPanel.charactersPanel:destroyChildren()
|
||||
entergameWindow.characters.mainPanel.outfitsPanel:destroyChildren()
|
||||
|
||||
outfitGroup = UIRadioGroup.create()
|
||||
characterGroup = UIRadioGroup.create()
|
||||
for i, character in ipairs(characters) do
|
||||
local characterWidget = g_ui.createWidget('EntergameCharacter', entergameWindow.characters.mainPanel.charactersPanel)
|
||||
characterGroup:addWidget(characterWidget)
|
||||
local outfitWidget = g_ui.createWidget('EntergameBigCharacter', entergameWindow.characters.mainPanel.outfitsPanel)
|
||||
outfitGroup:addWidget(outfitWidget)
|
||||
for i, widget in ipairs({characterWidget, outfitWidget}) do
|
||||
widget.character = character
|
||||
widget.outfit:setOutfit(character["outfit"])
|
||||
widget.line1:setText(character["line1"])
|
||||
widget.line2:setText(character["line2"])
|
||||
widget.line3:setText(character["line3"])
|
||||
end
|
||||
end
|
||||
if #characters > 1 then
|
||||
characterGroup:selectWidget(entergameWindow.characters.mainPanel.charactersPanel:getFirstChild())
|
||||
outfitGroup:selectWidget(entergameWindow.characters.mainPanel.outfitsPanel:getFirstChild())
|
||||
end
|
||||
end
|
||||
|
||||
function updateQAuth(token)
|
||||
if not token or token:len() == 0 then
|
||||
return entergameWindow.quick:hide()
|
||||
end
|
||||
entergameWindow.quick:show()
|
||||
entergameWindow.quick.qrcode:setQRCode(token, 1)
|
||||
entergameWindow.quick.qrcode.onClick = function()
|
||||
g_platform.openUrl(token)
|
||||
end
|
||||
entergameWindow.quick.quathlogo.onClick = entergameWindow.quick.qrcode.onClick
|
||||
end
|
||||
|
||||
function updateNews(news)
|
||||
if not news or #news == 0 then
|
||||
return entergameWindow.news:hide()
|
||||
end
|
||||
entergameWindow.news:show()
|
||||
entergameWindow.news.content:destroyChildren()
|
||||
for i, entry in ipairs(news) do
|
||||
local title = entry["title"]
|
||||
local text = entry["text"]
|
||||
local image = entry["image"]
|
||||
if title then
|
||||
local newsLabel = g_ui.createWidget('NewsLabel', entergameWindow.news.content)
|
||||
newsLabel:setText(title)
|
||||
end
|
||||
if text ~= nil then
|
||||
local newsText = g_ui.createWidget('NewsText', entergameWindow.news.content)
|
||||
newsText:setText(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function updateMotd(text)
|
||||
if not text or text:len() == 0 then
|
||||
return entergameWindow.characters.mainPanel.motd:hide()
|
||||
end
|
||||
entergameWindow.characters.mainPanel.motd:show()
|
||||
entergameWindow.characters.mainPanel.motd:setText(text)
|
||||
end
|
||||
|
||||
function login()
|
||||
local account = entergameWindow.entergame.mainPanel.account:getText()
|
||||
local password = entergameWindow.entergame.mainPanel.password:getText()
|
||||
entergameWindow.entergame:hide()
|
||||
showLoading("Login", "Connecting to server...")
|
||||
protocol:login(account, password, "")
|
||||
end
|
||||
|
||||
function onLogin(data)
|
||||
if loadingBox then
|
||||
loadingBox:destroy()
|
||||
loadingBox = nil
|
||||
end
|
||||
|
||||
if data["error"] and data["error"]:len() > 0 then
|
||||
entergameWindow.entergame:show()
|
||||
return message("Login error", data["error"])
|
||||
end
|
||||
|
||||
local incorrectThings = validateThings(data["things"])
|
||||
if incorrectThings:len() > 0 then
|
||||
entergameWindow.entergame:show()
|
||||
return message("Login error - missing things", incorrectThings)
|
||||
end
|
||||
|
||||
if infoBox then
|
||||
infoBox:destroy()
|
||||
end
|
||||
|
||||
local version = data["version"]
|
||||
G.clientVersion = version
|
||||
g_game.setClientVersion(version)
|
||||
g_game.setProtocolVersion(g_game.getClientProtocolVersion(version))
|
||||
g_game.setCustomOs(-1) -- disable custom os
|
||||
|
||||
local customProtocol = data["customProtocol"]
|
||||
g_game.setCustomProtocolVersion(0)
|
||||
if type(customProtocol) == 'number' then
|
||||
g_game.setCustomProtocolVersion(customProtocol)
|
||||
end
|
||||
|
||||
local email = data["email"]
|
||||
local security = data["security"]
|
||||
entergameWindow.settings.mainPanel.email:setText(email)
|
||||
entergameWindow.settings.mainPanel.security:setCurrentIndex(math.max(1, security))
|
||||
|
||||
entergameWindow.characters:show()
|
||||
entergameWindow.entergame:hide()
|
||||
end
|
||||
|
||||
function logout()
|
||||
if not entergameWindow.characters:isVisible() and not entergameWindow.createcharacter:isVisible() then
|
||||
return
|
||||
end
|
||||
entergameWindow.characters:hide()
|
||||
entergameWindow.createcharacter:hide()
|
||||
entergameWindow.entergame:show()
|
||||
message("Information", "Session expired, you has been logged out.")
|
||||
end
|
||||
|
||||
function validateThings(things)
|
||||
local incorrectThings = ""
|
||||
local missingFiles = false
|
||||
local versionForMissingFiles = 0
|
||||
if things ~= nil then
|
||||
local thingsNode = {}
|
||||
for thingtype, thingdata in pairs(things) do
|
||||
thingsNode[thingtype] = thingdata[1]
|
||||
if not g_resources.fileExists("/data/things/" .. thingdata[1]) then
|
||||
incorrectThings = incorrectThings .. "Missing file: " .. thingdata[1] .. "\n"
|
||||
missingFiles = true
|
||||
versionForMissingFiles = thingdata[1]:split("/")[1]
|
||||
else
|
||||
local localChecksum = g_resources.fileChecksum("/data/things/" .. thingdata[1]):lower()
|
||||
if localChecksum ~= thingdata[2]:lower() and #thingdata[2] > 1 then
|
||||
if g_resources.isLoadedFromArchive() then -- ignore checksum if it's test/debug version
|
||||
incorrectThings = incorrectThings .. "Invalid checksum of file: " .. thingdata[1] .. " (is " .. localChecksum .. ", should be " .. thingdata[2]:lower() .. ")\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
g_settings.setNode("things", thingsNode)
|
||||
else
|
||||
g_settings.setNode("things", {})
|
||||
end
|
||||
if missingFiles then
|
||||
incorrectThings = incorrectThings .. "\nYou should open data/things and create directory " .. versionForMissingFiles ..
|
||||
".\nIn this directory (data/things/" .. versionForMissingFiles .. ") you should put missing\nfiles (Tibia.dat and Tibia.spr) " ..
|
||||
"from correct Tibia version."
|
||||
end
|
||||
return incorrectThings
|
||||
end
|
||||
|
||||
function doGameLogin()
|
||||
local selected = nil
|
||||
if entergameWindow.characters.mainPanel.charactersPanel:isVisible() then
|
||||
selected = characterGroup:getSelectedWidget()
|
||||
else
|
||||
selected = outfitGroup:getSelectedWidget()
|
||||
end
|
||||
if not selected then
|
||||
return message("Entergame error", "Please select character")
|
||||
end
|
||||
local character = selected.character
|
||||
if not g_game.getFeature(GameSessionKey) then
|
||||
g_game.enableFeature(GameSessionKey)
|
||||
end
|
||||
g_game.loginWorld("", "", character.worldName, character.worldHost, character.worldPort, character.name, "", protocol.session)
|
||||
end
|
||||
|
||||
function onLoginError(err)
|
||||
message("Login error", err)
|
||||
end
|
||||
|
||||
function onLoginToken()
|
||||
|
||||
end
|
||||
|
||||
function onUpdateNeeded(signature)
|
||||
|
||||
end
|
||||
|
||||
function onConnectionError(message, code)
|
||||
|
||||
end
|
||||
|
||||
function onGameStart()
|
||||
entergameWindow:hide()
|
||||
end
|
||||
|
||||
function onGameEnd()
|
||||
entergameWindow:show()
|
||||
end
|
||||
|
||||
function onLoginWait(message, time)
|
||||
|
||||
end
|
||||
|
||||
function onLogout()
|
||||
|
||||
end
|
||||
|
||||
function createcharacter()
|
||||
local name = entergameWindow.createcharacter.mainPanel.name:getText()
|
||||
local gender = entergameWindow.createcharacter.mainPanel.gender:getCurrentOption().text
|
||||
local vocation = entergameWindow.createcharacter.mainPanel.vocation:getCurrentOption().text
|
||||
local town = entergameWindow.createcharacter.mainPanel.town:getCurrentOption().text
|
||||
if name:len() < 3 or name:len() > 20 then
|
||||
return message("Error", "Invalid character name")
|
||||
end
|
||||
protocol:createCharacter(name, gender, vocation, town)
|
||||
showLoading("Creating character", "Creating new character...")
|
||||
end
|
||||
|
||||
function onCharacterCreate(err, msg)
|
||||
if loadingBox then
|
||||
loadingBox:destroy()
|
||||
loadingBox = nil
|
||||
end
|
||||
|
||||
if err then
|
||||
return message("Error", err)
|
||||
end
|
||||
message("Success", msg)
|
||||
entergameWindow.createcharacter:hide()
|
||||
entergameWindow.characters:show()
|
||||
end
|
||||
|
||||
function updateSettings()
|
||||
local email = entergameWindow.settings.mainPanel.email:getText()
|
||||
local security = entergameWindow.settings.mainPanel.security.currentIndex
|
||||
|
||||
protocol:updateSettings({
|
||||
email=email,
|
||||
security=security
|
||||
})
|
||||
|
||||
entergameWindow.settings:hide()
|
||||
entergameWindow.characters:show()
|
||||
end
|
@@ -4,7 +4,7 @@ Module
|
||||
author: otclient.ovh
|
||||
website: http://otclient.ovh
|
||||
sandboxed: true
|
||||
scripts: [ entergamev2 ]
|
||||
scripts: [ protocol, entergamev2 ]
|
||||
@onLoad: init()
|
||||
@onUnload: terminate()
|
||||
|
@@ -60,7 +60,7 @@ EnterGame < Panel
|
||||
id: mainPanel
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: 250 210
|
||||
size: 230 210
|
||||
!text: tr("Enter Game")
|
||||
padding-top: 36
|
||||
padding-left: 16
|
||||
@@ -130,7 +130,7 @@ EnterGame < Panel
|
||||
anchors.horizontalCenter: prev.horizontalCenter
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 20
|
||||
size: 200 50
|
||||
size: 290 50
|
||||
image-source: /images/ui/window_headless
|
||||
image-border: 6
|
||||
padding-top: 8
|
||||
@@ -138,9 +138,18 @@ EnterGame < Panel
|
||||
Button
|
||||
id: register
|
||||
anchors.verticalCenter: buttons.verticalCenter
|
||||
anchors.horizontalCenter: buttons.horizontalCenter
|
||||
anchors.left: buttons.left
|
||||
margin-left: 10
|
||||
!text: tr("Create account")
|
||||
size: 160 30
|
||||
size: 130 30
|
||||
|
||||
Button
|
||||
id: register
|
||||
anchors.verticalCenter: buttons.verticalCenter
|
||||
anchors.right: buttons.right
|
||||
margin-right: 10
|
||||
!text: tr("Casts")
|
||||
size: 130 30
|
||||
|
||||
Registration < Panel
|
||||
anchors.fill: parent
|
||||
@@ -226,7 +235,6 @@ Registration < Panel
|
||||
margin-top: 10
|
||||
margin-left: 50
|
||||
margin-right: 50
|
||||
@onClick: EnterGame.doLogin()
|
||||
|
||||
EnterGamePanel
|
||||
id: buttons
|
||||
@@ -261,7 +269,6 @@ QuickLogin < EnterGamePanel
|
||||
image-fixed-ratio: true
|
||||
image-smooth: false
|
||||
margin-top: 5
|
||||
qr: 123
|
||||
|
||||
UIButton
|
||||
id: quathlogo
|
||||
@@ -319,7 +326,6 @@ Characters < Panel
|
||||
anchors.right: parent.right
|
||||
text-auto-resize: true
|
||||
text-wrap: true
|
||||
text: This is motd ;)
|
||||
text-align: center
|
||||
|
||||
HorizontalSeparator
|
||||
@@ -328,22 +334,45 @@ Characters < Panel
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 5
|
||||
height: 10
|
||||
|
||||
ScrollablePanel
|
||||
id: charactersPanel
|
||||
id: outfitsPanel
|
||||
layout:
|
||||
type: grid
|
||||
cell-size: 100 100
|
||||
cell-size: 125 125
|
||||
cell-spacing: 1
|
||||
flow: true
|
||||
vertical-scrollbar: charactersScroll
|
||||
vertical-scrollbar: outfitsScroll
|
||||
background-color: #444444
|
||||
anchors.top: prev.bottom
|
||||
anchors.bottom: bottomSeparator.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-bottom: 10
|
||||
margin-right: 12
|
||||
margin-bottom: 5
|
||||
margin-top: 5
|
||||
|
||||
VerticalScrollBar
|
||||
id: outfitsScroll
|
||||
anchors.top: outfitsPanel.top
|
||||
anchors.bottom: outfitsPanel.bottom
|
||||
anchors.left: outfitsPanel.right
|
||||
step: 14
|
||||
pixels-scroll: true
|
||||
|
||||
ScrollablePanel
|
||||
id: charactersPanel
|
||||
layout:
|
||||
type: verticalBox
|
||||
vertical-scrollbar: charactersScroll
|
||||
background-color: #444444
|
||||
anchors.top: motdSeparator.bottom
|
||||
anchors.bottom: bottomSeparator.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
margin-right: 12
|
||||
margin-bottom: 5
|
||||
margin-top: 5
|
||||
|
||||
VerticalScrollBar
|
||||
id: charactersScroll
|
||||
@@ -351,14 +380,14 @@ Characters < Panel
|
||||
anchors.bottom: charactersPanel.bottom
|
||||
anchors.left: charactersPanel.right
|
||||
step: 14
|
||||
pixels-scroll: true
|
||||
pixels-scroll: true
|
||||
|
||||
HorizontalSeparator
|
||||
id: bottomSeparator
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
height: 35
|
||||
margin-bottom: 30
|
||||
|
||||
Button
|
||||
id: autoReconnect
|
||||
@@ -370,7 +399,7 @@ Characters < Panel
|
||||
$!on:
|
||||
image-color: red
|
||||
!text: tr('Auto reconnect: Off')
|
||||
|
||||
|
||||
Button
|
||||
id: showOutfits
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
@@ -378,10 +407,10 @@ Characters < Panel
|
||||
width: 140
|
||||
|
||||
$on:
|
||||
!text: tr("Show outfits")
|
||||
!text: tr("Hide big outfits")
|
||||
|
||||
$!on:
|
||||
!text: tr("Hide outfits")
|
||||
!text: tr("Show big outfits")
|
||||
|
||||
Button
|
||||
id: connect
|
||||
@@ -389,6 +418,7 @@ Characters < Panel
|
||||
anchors.bottom: parent.bottom
|
||||
!text: tr("Connect")
|
||||
width: 140
|
||||
@onClick: modules.client_entergamev2.doGameLogin()
|
||||
|
||||
EnterGamePanel
|
||||
id: buttons
|
||||
@@ -409,10 +439,10 @@ Characters < Panel
|
||||
size: 140 30
|
||||
|
||||
Button
|
||||
id: casts
|
||||
id: settings
|
||||
anchors.verticalCenter: buttons.verticalCenter
|
||||
anchors.horizontalCenter: buttons.horizontalCenter
|
||||
!text: tr("Casts")
|
||||
!text: tr("Account settings")
|
||||
size: 140 30
|
||||
|
||||
Button
|
||||
@@ -423,10 +453,11 @@ Characters < Panel
|
||||
!text: tr("Logout")
|
||||
size: 140 30
|
||||
|
||||
EntergameCharacter < UIButton
|
||||
EntergameBigCharacter < UIButton
|
||||
border-width: 1
|
||||
padding: 1 1 1 1
|
||||
@onClick: self:setChecked(true)
|
||||
@onDoubleClick: modules.client_entergamev2.doGameLogin()
|
||||
|
||||
$checked:
|
||||
border-color: #ffffff
|
||||
@@ -438,7 +469,7 @@ EntergameCharacter < UIButton
|
||||
id: outfit
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
size: 48 48
|
||||
size: 70 70
|
||||
margin-bottom: 3
|
||||
phantom: true
|
||||
|
||||
@@ -447,39 +478,99 @@ EntergameCharacter < UIButton
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
text: Dagusia Druid
|
||||
text-align: center
|
||||
text-wrap: false
|
||||
height: 16
|
||||
font: terminus-10px
|
||||
border-width-bottom: 1
|
||||
border-color: #00000077
|
||||
phantom: true
|
||||
|
||||
Label
|
||||
id: line2
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
text: Level: 666
|
||||
text-align: center
|
||||
text-wrap: false
|
||||
height: 16
|
||||
font: terminus-10px
|
||||
border-width-bottom: 1
|
||||
border-color: #00000077
|
||||
phantom: true
|
||||
|
||||
Label
|
||||
id: line3
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
text: World: nemezis
|
||||
text-align: center
|
||||
text-wrap: false
|
||||
height: 16
|
||||
font: terminus-10px
|
||||
border-width-bottom: 1
|
||||
border-color: #00000077
|
||||
phantom: true
|
||||
|
||||
EntergameCharacter < UIButton
|
||||
padding: 3 3 3 3
|
||||
@onClick: self:setChecked(true)
|
||||
@onDoubleClick: modules.client_entergamev2.doGameLogin()
|
||||
height: 34
|
||||
|
||||
$checked:
|
||||
background-color: #333333
|
||||
|
||||
$!checked:
|
||||
background-color: #555555
|
||||
|
||||
UICreature
|
||||
id: outfit
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
size: 32 32
|
||||
margin-top: -2
|
||||
margin-bottom: -2
|
||||
phantom: true
|
||||
|
||||
UILabel
|
||||
id: line1
|
||||
anchors.left: prev.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
margin-left: 6
|
||||
width: 150
|
||||
text-align: left
|
||||
phantom: true
|
||||
|
||||
VerticalSeparator
|
||||
anchors.left: prev.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
UILabel
|
||||
id: line2
|
||||
anchors.left: prev.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: 150
|
||||
text-align: center
|
||||
phantom: true
|
||||
|
||||
VerticalSeparator
|
||||
anchors.left: prev.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
UILabel
|
||||
id: line3
|
||||
anchors.left: prev.right
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
text-align: center
|
||||
phantom: true
|
||||
|
||||
CreateCharacter < Panel
|
||||
anchors.fill: parent
|
||||
@@ -503,12 +594,32 @@ CreateCharacter < Panel
|
||||
text-auto-resize: true
|
||||
|
||||
TextEdit
|
||||
id: accountNameTextEdit
|
||||
id: name
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 2
|
||||
|
||||
MenuLabel
|
||||
!text: tr('Gender')
|
||||
anchors.left: prev.left
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 8
|
||||
text-auto-resize: true
|
||||
|
||||
ComboBox
|
||||
id: gender
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
menu-scroll: true
|
||||
menu-height: 125
|
||||
menu-scroll-step: 25
|
||||
margin-right: 3
|
||||
@onSetup: |
|
||||
self:addOption("Male")
|
||||
self:addOption("Female")
|
||||
|
||||
MenuLabel
|
||||
!text: tr('Vocation')
|
||||
anchors.left: prev.left
|
||||
@@ -516,40 +627,42 @@ CreateCharacter < Panel
|
||||
margin-top: 8
|
||||
text-auto-resize: true
|
||||
|
||||
TextEdit
|
||||
id: emailTextEdit
|
||||
ComboBox
|
||||
id: vocation
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 2
|
||||
|
||||
MenuLabel
|
||||
!text: tr('Password')
|
||||
anchors.left: prev.left
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 8
|
||||
text-auto-resize: true
|
||||
|
||||
PasswordTextEdit
|
||||
id: accountPasswordTextEdit
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 2
|
||||
menu-scroll: true
|
||||
menu-height: 125
|
||||
menu-scroll-step: 25
|
||||
margin-right: 3
|
||||
@onSetup: |
|
||||
self:addOption("Sorcerer")
|
||||
self:addOption("Druid")
|
||||
self:addOption("Paladin")
|
||||
self:addOption("Knight")
|
||||
|
||||
MenuLabel
|
||||
!text: tr('Password confirmation')
|
||||
!text: tr('Town')
|
||||
anchors.left: prev.left
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 8
|
||||
text-auto-resize: true
|
||||
|
||||
PasswordTextEdit
|
||||
id: accountPasswordTextEdit
|
||||
ComboBox
|
||||
id: town
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 2
|
||||
menu-scroll: true
|
||||
menu-height: 125
|
||||
menu-scroll-step: 25
|
||||
margin-right: 3
|
||||
@onSetup: |
|
||||
self:addOption("Carlin")
|
||||
self:addOption("Edron")
|
||||
self:addOption("Thais")
|
||||
self:addOption("Venore")
|
||||
|
||||
HorizontalSeparator
|
||||
anchors.left: parent.left
|
||||
@@ -558,6 +671,7 @@ CreateCharacter < Panel
|
||||
margin-top: 9
|
||||
|
||||
Button
|
||||
id: createButton
|
||||
!text: tr('Create character')
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
@@ -565,7 +679,89 @@ CreateCharacter < Panel
|
||||
margin-top: 10
|
||||
margin-left: 50
|
||||
margin-right: 50
|
||||
@onClick: EnterGame.doLogin()
|
||||
|
||||
EnterGamePanel
|
||||
id: buttons
|
||||
anchors.horizontalCenter: prev.horizontalCenter
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 20
|
||||
size: 200 50
|
||||
image-source: /images/ui/window_headless
|
||||
image-border: 6
|
||||
padding-top: 8
|
||||
|
||||
Button
|
||||
id: back
|
||||
anchors.verticalCenter: buttons.verticalCenter
|
||||
anchors.horizontalCenter: buttons.horizontalCenter
|
||||
!text: tr("Back")
|
||||
size: 160 30
|
||||
|
||||
|
||||
AccountSettings < Panel
|
||||
anchors.fill: parent
|
||||
id: settings
|
||||
|
||||
EnterGamePanel
|
||||
id: mainPanel
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
size: 250 173
|
||||
!text: tr("Account settings")
|
||||
padding-top: 36
|
||||
padding-left: 16
|
||||
padding-right: 16
|
||||
padding-bottom: 16
|
||||
|
||||
MenuLabel
|
||||
!text: tr('Email')
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
text-auto-resize: true
|
||||
|
||||
TextEdit
|
||||
id: email
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 2
|
||||
|
||||
MenuLabel
|
||||
!text: tr('Security level')
|
||||
anchors.left: prev.left
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 8
|
||||
text-auto-resize: true
|
||||
|
||||
ComboBox
|
||||
id: security
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
menu-scroll: true
|
||||
menu-height: 125
|
||||
menu-scroll-step: 25
|
||||
margin-right: 3
|
||||
@onSetup: |
|
||||
self:addOption("Low")
|
||||
self:addOption("Medium")
|
||||
self:addOption("High")
|
||||
|
||||
HorizontalSeparator
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 9
|
||||
|
||||
Button
|
||||
id: updateButton
|
||||
!text: tr('Update settings')
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: prev.bottom
|
||||
margin-top: 10
|
||||
margin-left: 50
|
||||
margin-right: 50
|
||||
|
||||
EnterGamePanel
|
||||
id: buttons
|
||||
@@ -597,3 +793,4 @@ Panel
|
||||
Registration
|
||||
Characters
|
||||
CreateCharacter
|
||||
AccountSettings
|
||||
|
228
modules/client_entergamev2/protocol.lua
Normal file
228
modules/client_entergamev2/protocol.lua
Normal file
@@ -0,0 +1,228 @@
|
||||
EnterGameV2Protocol = {}
|
||||
EnterGameV2Protocol.__index = EnterGameV2Protocol
|
||||
|
||||
EnterGameV2Protocol.new = function(session)
|
||||
if type(session) ~= 'string' then
|
||||
return error("You need to specify string session for EnterGameV2Protocol")
|
||||
end
|
||||
local data = {}
|
||||
data.socket = nil
|
||||
data.terminated = false
|
||||
data.reconnectEvent = nil
|
||||
data.connected = false
|
||||
data.session = session
|
||||
data.sendQueue = {}
|
||||
data.sendQueueMsgId = 1
|
||||
data.loginTimeoutEvent = nil
|
||||
setmetatable(data, EnterGameV2Protocol)
|
||||
return data
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:destroy()
|
||||
self.terminated = true
|
||||
self.sendQueue = {}
|
||||
|
||||
if self.loginTimeoutEvent then
|
||||
removeEvent(self.loginTimeoutEvent)
|
||||
self.loginTimeoutEvent = nil
|
||||
end
|
||||
|
||||
self:reset()
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:reset()
|
||||
self.connected = false
|
||||
if self.reconnectEvent then
|
||||
removeEvent(self.reconnectEvent)
|
||||
self.reconnectEvent = nil
|
||||
end
|
||||
if self.socket then
|
||||
self.socket.close()
|
||||
self.socket = nil
|
||||
end
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:setUrl(url)
|
||||
if self.terminated then
|
||||
return false
|
||||
end
|
||||
self:reset()
|
||||
if self.url ~= url then
|
||||
self.sendQueue = {}
|
||||
self.sendQueueMsgId = 1
|
||||
if self.loginTimeoutEvent then
|
||||
removeEvent(self.loginTimeoutEvent)
|
||||
self.loginTimeoutEvent = nil
|
||||
end
|
||||
end
|
||||
if type(url) ~= 'string' or not url:lower():find("ws") then
|
||||
g_logger.error("Invalid url for EnterGameV2Protocol:\n" .. url)
|
||||
return false
|
||||
end
|
||||
self.url = url
|
||||
self.socket = HTTP.WebSocketJSON(url, {
|
||||
onOpen = function(message, websocketId)
|
||||
if self.terminated or not self.socket or self.socket.id ~= websocketId then return end
|
||||
self.connected = true
|
||||
self:sendFirstMessage()
|
||||
end,
|
||||
onMessage = function(message, websocketId)
|
||||
if self.terminated or not self.socket or self.socket.id ~= websocketId then return end
|
||||
self:onSocketMessage(message)
|
||||
end,
|
||||
onClose = function(message, websocketId)
|
||||
if self.terminated or not self.socket or self.socket.id ~= websocketId then return end
|
||||
self.connected = false
|
||||
self:scheduleReconnect()
|
||||
end,
|
||||
onError = function(message, websocketId)
|
||||
if self.terminated or not self.socket or self.socket.id ~= websocketId then return end
|
||||
self.connected = false
|
||||
self:scheduleReconnect()
|
||||
end
|
||||
})
|
||||
return true
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:isConnected()
|
||||
return self.socket and self.connected
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:scheduleReconnect()
|
||||
if self.socket then
|
||||
self.connected = false
|
||||
self.socket.close()
|
||||
self.socket = nil
|
||||
end
|
||||
if self.terminated then return end
|
||||
if self.reconnectEvent then return end
|
||||
self.reconnectEvent = scheduleEvent(function() self:reconnect() end, 500)
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:login(account, password, token, callback)
|
||||
self:send({
|
||||
type="login",
|
||||
account=account,
|
||||
password=password,
|
||||
token=token,
|
||||
})
|
||||
if self.loginTimeoutEvent then
|
||||
removeEvent(self.loginTimeoutEvent)
|
||||
end
|
||||
self.loginTimeoutEvent = scheduleEvent(function()
|
||||
self.loginTimeoutEvent = nil
|
||||
self.onLogin({error="Connection timeout"})
|
||||
end, 10000)
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:logout()
|
||||
self:send({
|
||||
type="logout"
|
||||
})
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:register(name, email, password, callback)
|
||||
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:createCharacter(name, gender, vocation, town)
|
||||
self:send({
|
||||
type="createcharacter",
|
||||
name=name,
|
||||
gender=gender,
|
||||
vocation=vocation,
|
||||
town=town
|
||||
})
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:updateSettings(settings)
|
||||
self:send({
|
||||
type="settings",
|
||||
settings=settings
|
||||
})
|
||||
end
|
||||
|
||||
-- private functions
|
||||
function EnterGameV2Protocol:reconnect()
|
||||
if #self.sendQueue > 1 then
|
||||
self.sendQueue = {} -- TEMPORARY
|
||||
end
|
||||
self.reconnectEvent = nil
|
||||
if self.terminated then return end
|
||||
self:setUrl(self.url)
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:send(data)
|
||||
if type(data) ~= "table" then
|
||||
return error("data should be table")
|
||||
end
|
||||
data["id"] = self.sendQueueMsgId
|
||||
table.insert(self.sendQueue, {id=self.sendQueueMsgId, msg=json.encode(data)})
|
||||
self.sendQueueMsgId = self.sendQueueMsgId + 1
|
||||
if self.socket then
|
||||
self.socket.send(self.sendQueue[#self.sendQueue].msg)
|
||||
end
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:sendFirstMessage()
|
||||
self.socket.send({type="init", session=self.session})
|
||||
for i, msg in ipairs(self.sendQueue) do
|
||||
self.socket.send(msg.msg)
|
||||
end
|
||||
end
|
||||
|
||||
function EnterGameV2Protocol:onSocketMessage(message)
|
||||
local lastId = message["lastId"]
|
||||
if type(lastId) == 'number' then -- clear send queue
|
||||
while #self.sendQueue > 0 do
|
||||
local id = self.sendQueue[1].id
|
||||
if id < lastId then
|
||||
break
|
||||
end
|
||||
table.remove(self.sendQueue, 1)
|
||||
end
|
||||
end
|
||||
if message["type"] == "ping" then
|
||||
self.socket.send({type="ping"})
|
||||
elseif message["type"] == "login" then
|
||||
if self.loginTimeoutEvent then
|
||||
removeEvent(self.loginTimeoutEvent)
|
||||
self.loginTimeoutEvent = nil
|
||||
end
|
||||
if self.onLogin then
|
||||
self.onLogin(message)
|
||||
end
|
||||
elseif message["type"] == "logout" then
|
||||
if self.onLogout then
|
||||
self.onLogout()
|
||||
end
|
||||
elseif message["type"] == "qauth" then
|
||||
if self.onQAuth then
|
||||
self.onQAuth(message["token"])
|
||||
end
|
||||
elseif message["type"] == "characters" then
|
||||
if self.onCharacters then
|
||||
self.onCharacters(message["characters"])
|
||||
end
|
||||
elseif message["type"] == "message" then
|
||||
if self.onMessage then
|
||||
self.onMessage(message["title"], message["text"])
|
||||
end
|
||||
elseif message["type"] == "loading" then
|
||||
if self.onMessage then
|
||||
self.onLoading(message["title"], message["text"])
|
||||
end
|
||||
elseif message["type"] == "news" then
|
||||
if self.onNews then
|
||||
self.onNews(message["news"])
|
||||
end
|
||||
elseif message["type"] == "motd" then
|
||||
if self.onMotd then
|
||||
self.onMotd(message["text"])
|
||||
end
|
||||
elseif message["type"] == "createcharacter" then
|
||||
if self.onMessage then
|
||||
self.onCharacterCreate(message["error"], message["message"])
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user