Version 1.4 - Websockets and bug fixes

This commit is contained in:
OTCv8
2019-11-26 02:32:51 +01:00
parent 1072671986
commit c750ea65f8
29 changed files with 426 additions and 124 deletions

View File

@@ -108,12 +108,6 @@ if ($conn->connect_error) {
die("SQL connection failed: " . $conn->connect_error); die("SQL connection failed: " . $conn->connect_error);
} }
if($data->quick == 1) {
require_once("quick.php");
die();
}
$account = $data->account; $account = $data->account;
if($encryption == "sha1") if($encryption == "sha1")
$password = sha1($data->password); $password = sha1($data->password);

BIN
data/images/ui/qauth.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -12,13 +12,13 @@ Services = {
feedback = "http://otclient.ovh/api/feedback.php" feedback = "http://otclient.ovh/api/feedback.php"
} }
-- Servers accept http login url or ip:port:version -- Servers accept http login url, websocket login url or ip:port:version
Servers = { Servers = {
OTClientV8 = "http://otclient.ovh/api/login.php", OTClientV8 = "http://otclient.ovh/api/login.php",
OTClientV8Websocket = "wss://otclient.ovh:3000/",
OTClientV8proxy = "http://otclient.ovh/api/login.php?proxy=1", OTClientV8proxy = "http://otclient.ovh/api/login.php?proxy=1",
OTClientV8classic = "otclient.ovh:7171:1099", OTClientV8c = "otclient.ovh:7171:1099:25:30:80:90",
OTClientV8cwithfeatures = "otclient.ovh:7171:1099:25:30:80:90", OTClientV8Test = "http://otclient.ovh/api/login2.php",
OTClientV8Test = "http://otclient.ovh/api/login2.php"
} }
ALLOW_CUSTOM_SERVERS = true -- if true it shows option ANOTHER on server list ALLOW_CUSTOM_SERVERS = true -- if true it shows option ANOTHER on server list
-- CONFIG END -- CONFIG END

View File

@@ -50,11 +50,7 @@ StaticMainWindow
@onSetup: | @onSetup: |
g_keyboard.bindKeyPress('Up', function() self:getChildById('characters'):focusPreviousChild(KeyboardFocusReason) end, self) g_keyboard.bindKeyPress('Up', function() self:getChildById('characters'):focusPreviousChild(KeyboardFocusReason) end, self)
g_keyboard.bindKeyPress('Down', function() self:getChildById('characters'):focusNextChild(KeyboardFocusReason) end, self) g_keyboard.bindKeyPress('Down', function() self:getChildById('characters'):focusNextChild(KeyboardFocusReason) end, self)
if g_game.getFeature(GamePreviewState) then self:setSize({width = 350, height = 400})
self:setSize({width = 350, height = 400})
else
self:setSize({width = 250, height = 248})
end
TextList TextList
id: characters id: characters

View File

@@ -9,10 +9,6 @@ local protocolLogin
local server = nil local server = nil
local versionsFound = false local versionsFound = false
local newLogin = nil
local newLoginUrl = nil
local newLoginEvent
local customServerSelectorPanel local customServerSelectorPanel
local serverSelectorPanel local serverSelectorPanel
local serverSelector local serverSelector
@@ -21,6 +17,8 @@ local serverHostTextEdit
local rememberPasswordBox local rememberPasswordBox
local protos = {"740", "760", "772", "800", "810", "854", "860", "1077", "1090", "1096", "1098", "1099", "1100"} local protos = {"740", "760", "772", "800", "810", "854", "860", "1077", "1090", "1096", "1098", "1099", "1100"}
local webSocket
local webSocketLoginPacket
-- private functions -- private functions
local function onProtocolError(protocol, message, errorCode) local function onProtocolError(protocol, message, errorCode)
@@ -125,6 +123,10 @@ local function onHTTPResult(data, err)
if #incorrectThings > 0 then if #incorrectThings > 0 then
g_logger.info(incorrectThings) g_logger.info(incorrectThings)
if Updater then if Updater then
if webSocket then
webSocket:close()
webSocket = nil
end
return Updater.updateThings(things, incorrectThings) return Updater.updateThings(things, incorrectThings)
else else
return EnterGame.onError(incorrectThings) return EnterGame.onError(incorrectThings)
@@ -175,6 +177,10 @@ local function onHTTPResult(data, err)
end end
end end
if webSocket then
webSocket:close()
webSocket = nil
end
onCharacterList(nil, characters, account, nil) onCharacterList(nil, characters, account, nil)
end end
@@ -244,7 +250,10 @@ end
function EnterGame.terminate() function EnterGame.terminate()
g_keyboard.unbindKeyDown('Ctrl+G') g_keyboard.unbindKeyDown('Ctrl+G')
removeEvent(newLoginEvent) if webSocket then
webSocket.close()
webSocket = nil
end
enterGame:destroy() enterGame:destroy()
if newLogin then if newLogin then
@@ -269,7 +278,7 @@ function EnterGame.show()
enterGame:raise() enterGame:raise()
enterGame:focus() enterGame:focus()
enterGame:getChildById('accountNameTextEdit'):focus() enterGame:getChildById('accountNameTextEdit'):focus()
EnterGame.checkNewLogin() EnterGame.checkWebsocket()
end end
function EnterGame.hide() function EnterGame.hide()
@@ -294,33 +303,74 @@ function EnterGame.clearAccountFields()
g_settings.remove('password') g_settings.remove('password')
end end
function EnterGame.hideNewLogin() function EnterGame.checkWebsocket()
newLogin:hide() if enterGame:isHidden() then return end
newLoginUrl = nil local url = serverHostTextEdit:getText()
end if url:find("ws://") == nil and url:find("wss://") == nil then
if webSocket then
function EnterGame.checkNewLoginEvent() webSocket:close()
newLoginEvent = scheduleEvent(function() EnterGame.checkNewLoginEvent() end, 1000) webSocket = nil
EnterGame.checkNewLogin() end
end
function EnterGame.checkNewLogin()
if not newLoginUrl then
return return
end end
local url = newLoginUrl if webSocket then
HTTP.postJSON(newLoginUrl, { quick = 1 }, function(data, err) if webSocket.url == url then
if url ~= newLoginUrl then return end if newLogin:isHidden() and newLogin.code:getText():len() > 1 then
if err then return end newLogin:show()
if not data["qrcode"] then return end newLogin:raise()
newLogin.qrcode:setImageSourceBase64(data["qrcode"]) end
newLogin.code:setText(data["code"]) return
if enterGame:isHidden() then return end
if newLogin:isHidden() then
newLogin:show()
newLogin:raise()
end end
end) webSocket:close()
webSocket = nil
end
newLogin.code:setText("")
webSocket = HTTP.WebSocketJSON(url, {
onOpen = function(message, webSocketId)
if webSocket and webSocket.id == webSocketId then
webSocket.send({type="init", uid=G.uuid, version=APP_VERSION})
end
end,
onMessage = function(message, webSocketId)
if webSocket and webSocket.id == webSocketId then
if message.type == "login" then
webSocketLoginPacket = nil
onHTTPResult(message, nil)
elseif message.type == "quick_login" and message.code and message.qrcode then
EnterGame.showNewLogin(message.code, message.qrcode)
end
end
end,
onClose = function(message, webSocketId)
if webSocket and webSocket.id == webSocketId then
webSocket = nil
if webSocketLoginPacket then
webSocketLoginPacket = nil
onHTTPResult(nil, "WebSocket disconnected")
end
EnterGame.checkWebsocket() -- reconnect
end
end,
onError = function(message, webSocketId)
if webSocket and webSocket.id == webSocketId then
-- handle error
end
end
})
end
function EnterGame.hideNewLogin()
newLogin:hide()
end
function EnterGame.showNewLogin(code, qrcode)
if enterGame:isHidden() then return end
newLogin.code:setText(code)
newLogin.qrcode:setQRCode(qrcode, 1)
if newLogin:isHidden() then
newLogin:show()
newLogin:raise()
end
end end
function EnterGame.onServerChange() function EnterGame.onServerChange()
@@ -338,8 +388,7 @@ function EnterGame.onServerChange()
end end
if Servers and Servers[server] ~= nil then if Servers and Servers[server] ~= nil then
serverHostTextEdit:setText(Servers[server]) serverHostTextEdit:setText(Servers[server])
newLoginUrl = Servers[server] EnterGame.checkWebsocket()
EnterGame.checkNewLogin()
end end
end end
@@ -371,6 +420,9 @@ function EnterGame.doLogin()
g_settings.set('client-version', G.clientVersion) g_settings.set('client-version', G.clientVersion)
g_settings.save() g_settings.save()
if G.host:find("ws://") ~= nil or G.host:find("wss://") ~= nil then
return EnterGame.doLoginWs()
end
if G.host:find("http") ~= nil then if G.host:find("http") ~= nil then
return EnterGame.doLoginHttp() return EnterGame.doLoginHttp()
end end
@@ -446,6 +498,36 @@ function EnterGame.doLogin()
end end
end end
function EnterGame.doLoginWs()
-- PREVIEW, need to implement websocket reconnect and error handling
if G.host == nil or G.host:len() < 10 then
return EnterGame.onError("Invalid server url: " .. G.host)
end
if not webSocket then
return EnterGame.onError("There's no websocket connection to: " .. G.host)
end
loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to login server...'))
connect(loadBox, { onCancel = function(msgbox)
loadBox = nil
webSocketLoginPacket = nil
EnterGame.show()
end })
local data = {
type = "login",
account = G.account,
password = G.password,
token = G.authenticatorToken,
version = APP_VERSION,
uid = G.UUID
}
webSocketLoginPacket = data
webSocket.send(data)
EnterGame.hide()
end
function EnterGame.doLoginHttp() function EnterGame.doLoginHttp()
if G.host == nil or G.host:len() < 10 then if G.host == nil or G.host:len() < 10 then
return EnterGame.onError("Invalid server url: " .. G.host) return EnterGame.onError("Invalid server url: " .. G.host)

View File

@@ -4,21 +4,33 @@ StaticWindow
margin-right: 20 margin-right: 20
id: newLoginPanel id: newLoginPanel
width: 230 width: 230
height: 330 height: 350
!text: tr('Quick Login & Registration') !text: tr('Quick Login & Registration')
Label Label
id: qrcode id: qrcode
width: 200 width: 200
height: 180 height: 200
anchors.top: parent.top anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
image-fixed-ratio: true
image-smooth: false
margin-top: 5 margin-top: 5
Label Label
anchors.top: prev.bottom id: quathlogo
anchors.left: prev.left width: 66
anchors.right: prev.right height: 40
anchors.verticalCenter: prev.verticalCenter
anchors.horizontalCenter: prev.horizontalCenter
image-fixed-ratio: true
image-smooth: false
image-source: /images/ui/qauth
Label
anchors.top: qrcode.bottom
anchors.left: qrcode.left
anchors.right: qrcode.right
text-align: center text-align: center
text-auto-resize: true text-auto-resize: true
!text: tr("Scan QR code or process\nbellow code to register or login") !text: tr("Scan QR code or process\nbellow code to register or login")

View File

@@ -25,7 +25,7 @@ function terminate()
end end
function show() function show()
if Services.feedback == nil or Services.feedback:len() < 4 then if not Services or not Services.feedback or Services.feedback:len() < 4 then
return return
end end

View File

@@ -303,8 +303,8 @@ function setOption(key, value, force)
end end
--elseif key == 'ignoreServerDirection' then --elseif key == 'ignoreServerDirection' then
-- g_game.ignoreServerDirection(value) -- g_game.ignoreServerDirection(value)
elseif key == 'realDirection' then --elseif key == 'realDirection' then
g_game.showRealDirection(value) -- g_game.showRealDirection(value)
elseif key == 'hotkeyDelay' then elseif key == 'hotkeyDelay' then
generalPanel:getChildById('hotkeyDelayLabel'):setText(tr('Hotkey delay: %s ms', value)) generalPanel:getChildById('hotkeyDelayLabel'):setText(tr('Hotkey delay: %s ms', value))
elseif key == 'walkFirstStepDelay' then elseif key == 'walkFirstStepDelay' then

View File

@@ -49,6 +49,11 @@ function init()
updateFps() updateFps()
if not Services or not Services.feedback or Services.feedback:len() < 4 then
topMenu.reportBug:setVisible(false)
topMenu.reportBug:setWidth(0)
end
if HIDE_TOPMENU then if HIDE_TOPMENU then
topMenu:setHeight(0) topMenu:setHeight(0)
topMenu:hide() topMenu:hide()

View File

@@ -1,5 +1,6 @@
HTTP = { HTTP = {
timeout=5, timeout=5,
websocketTimeout=15,
imageId=1000, imageId=1000,
images={}, images={},
operations={} operations={}
@@ -55,6 +56,30 @@ function HTTP.downloadImage(url, callback)
return operation return operation
end end
function HTTP.webSocket(url, callbacks, jsonWebsocket)
local operation = g_http.ws(url, HTTP.websocketTimeout)
HTTP.operations[operation] = {type="ws", json=jsonWebsocket, url=url, callbacks=callbacks}
return {
id = operation,
url = url,
close = function()
g_http.wsClose(operation)
end,
send = function(message)
if type(message) == "table" then
message = json.encode(message)
end
g_http.wsSend(operation, message)
end
}
end
HTTP.WebSocket = HTTP.webSocket
function HTTP.webSocketJSON(url, callbacks)
return HTTP.webSocket(url, callbacks, true)
end
HTTP.WebSocketJSON = HTTP.webSocketJSON
function HTTP.cancel(operationId) function HTTP.cancel(operationId)
return g_http.cancel(operationId) return g_http.cancel(operationId)
end end
@@ -71,6 +96,9 @@ function HTTP.onGet(operationId, url, err, data)
local status, result = pcall(function() return json.decode(data) end) local status, result = pcall(function() return json.decode(data) end)
if not status then if not status then
err = "JSON ERROR: " .. result err = "JSON ERROR: " .. result
if data and data:len() > 0 then
err = err .. " (" .. data:sub(1, 100) .. ")"
end
end end
data = result data = result
end end
@@ -99,6 +127,9 @@ function HTTP.onPost(operationId, url, err, data)
local status, result = pcall(function() return json.decode(data) end) local status, result = pcall(function() return json.decode(data) end)
if not status then if not status then
err = "JSON ERROR: " .. result err = "JSON ERROR: " .. result
if data and data:len() > 0 then
err = err .. " (" .. data:sub(1, 100) .. ")"
end
end end
data = result data = result
end end
@@ -142,6 +173,64 @@ function HTTP.onDownloadProgress(operationId, url, progress, speed)
end end
end end
function HTTP.onWsOpen(operationId, message)
local operation = HTTP.operations[operationId]
if operation == nil then
return
end
if operation.callbacks.onOpen then
operation.callbacks.onOpen(message, operationId)
end
end
function HTTP.onWsMessage(operationId, message)
local operation = HTTP.operations[operationId]
if operation == nil then
return
end
if operation.callbacks.onMessage then
if operation.json then
local status, result = pcall(function() return json.decode(message) end)
local err = nil
if not status then
err = "JSON ERROR: " .. result
if message and message:len() > 0 then
err = err .. " (" .. message:sub(1, 100) .. ")"
end
end
if err then
if operation.callbacks.onError then
operation.callbacks.onError(err, operationId)
end
else
operation.callbacks.onMessage(result, operationId)
end
else
operation.callbacks.onMessage(message, operationId)
end
end
end
function HTTP.onWsClose(operationId, message)
local operation = HTTP.operations[operationId]
if operation == nil then
return
end
if operation.callbacks.onClose then
operation.callbacks.onClose(message, operationId)
end
end
function HTTP.onWsError(operationId, message)
local operation = HTTP.operations[operationId]
if operation == nil then
return
end
if operation.callbacks.onError then
operation.callbacks.onError(message, operationId)
end
end
connect(g_http, connect(g_http,
{ {
onGet = HTTP.onGet, onGet = HTTP.onGet,
@@ -149,6 +238,10 @@ connect(g_http,
onPost = HTTP.onPost, onPost = HTTP.onPost,
onPostProgress = HTTP.onPostProgress, onPostProgress = HTTP.onPostProgress,
onDownload = HTTP.onDownload, onDownload = HTTP.onDownload,
onDownloadProgress = HTTP.onDownloadProgress onDownloadProgress = HTTP.onDownloadProgress,
onWsOpen = HTTP.onWsOpen,
onWsMessage = HTTP.onWsMessage,
onWsClose = HTTP.onWsClose,
onWsError = HTTP.onWsError,
}) })

View File

@@ -227,6 +227,9 @@ function checkCreatures()
end end
local player = g_game.getLocalPlayer() local player = g_game.getLocalPlayer()
if not player then
return
end
local dimension = modules.game_interface.getMapPanel():getVisibleDimension() local dimension = modules.game_interface.getMapPanel():getVisibleDimension()
local spectators = g_map.getSpectatorsInRangeEx(player:getPosition(), false, math.floor(dimension.width / 2), math.floor(dimension.width / 2), math.floor(dimension.height / 2), math.floor(dimension.height / 2)) local spectators = g_map.getSpectatorsInRangeEx(player:getPosition(), false, math.floor(dimension.width / 2), math.floor(dimension.width / 2), math.floor(dimension.height / 2), math.floor(dimension.height / 2))

View File

@@ -1,7 +1,7 @@
MiniWindow MiniWindow
id: botWindow id: botWindow
!text: tr('Bot') !text: tr('Bot')
height: 200 height: 600
icon: /images/topbuttons/bot icon: /images/topbuttons/bot
@onClose: modules.game_bot.onMiniWindowClose() @onClose: modules.game_bot.onMiniWindowClose()
&save: true &save: true
@@ -16,7 +16,7 @@ MiniWindow
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
margin-top: 5 margin-top: 2
margin-right: 90 margin-right: 90
text-offset: 3 0 text-offset: 3 0
@@ -43,7 +43,7 @@ MiniWindow
anchors.top: prev.bottom anchors.top: prev.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
margin-top: 5 margin-top: 3
text-auto-resize: true text-auto-resize: true
!text: tr('Status: waiting') !text: tr('Status: waiting')
text-align: center text-align: center
@@ -53,7 +53,7 @@ MiniWindow
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.top: prev.bottom anchors.top: prev.bottom
margin-top: 5 margin-top: 3
Panel Panel
anchors.top: prev.bottom anchors.top: prev.bottom
@@ -75,7 +75,7 @@ MiniWindow
anchors.top: prev.bottom anchors.top: prev.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
tab-spacing: 2 tab-spacing: 1
height: 20 height: 20
movable: false movable: false

View File

@@ -10,9 +10,9 @@ botDefaultConfig = {
--#panels --#panels
local healTab = addTab("HP") local healTab = addTab("HP")
local attackTab = addTab("Atck") local batTab = addTab("Batt")
local warTab = addTab("War")
local caveTab = addTab("Cave") local caveTab = addTab("Cave")
local toolsTab = addTab("Tools")
Panels.TradeMessage() Panels.TradeMessage()
Panels.AutoStackItems() Panels.AutoStackItems()
@@ -45,12 +45,12 @@ Panels.Equip(healTab)
Panels.Equip(healTab) Panels.Equip(healTab)
Panels.Eating(healTab) Panels.Eating(healTab)
Panels.AttackSpell(attackTab) Panels.AttackSpell(batTab)
Panels.AttackItem(attackTab) Panels.AttackItem(batTab)
Panels.AttackLeaderTarget(warTab) Panels.AttackLeaderTarget(batTab)
Panels.LimitFloor(warTab) Panels.LimitFloor(batTab)
Panels.AntiPush(warTab) Panels.AntiPush(batTab)
local waypoints = Panels.Waypoints(caveTab) local waypoints = Panels.Waypoints(caveTab)
local attacking = Panels.Attacking(caveTab) local attacking = Panels.Attacking(caveTab)
@@ -61,6 +61,18 @@ end, caveTab)
--#macros --#macros
macro(1000, "exchange money", function()
local containers = getContainers()
for i, container in pairs(containers) do
for j, item in ipairs(container:getItems()) do
if item:isStackable() and (item:getId() == 3035 or item:getId() == 3031) and item:getCount() == 100 then
g_game.use(item)
return
end
end
end
end)
macro(1000, "this macro does nothing", "f7", function() macro(1000, "this macro does nothing", "f7", function()
end) end)

View File

@@ -14,7 +14,7 @@ context.playSound = function(file)
end end
botSoundChannel:setEnabled(true) botSoundChannel:setEnabled(true)
botSoundChannel:stop(0) botSoundChannel:stop(0)
botSoundChannel:play(file, 0, 0) botSoundChannel:play(file, 0, 1.0)
return botSoundChannel return botSoundChannel
end end

View File

@@ -456,7 +456,7 @@ Panels.Attacking = function(parent)
local ui = context.setupUI([[ local ui = context.setupUI([[
Panel Panel
id: attacking id: attacking
height: 150 height: 140
BotLabel BotLabel
anchors.top: parent.top anchors.top: parent.top
@@ -486,7 +486,7 @@ Panel
anchors.left: parent.left anchors.left: parent.left
text: Add text: Add
width: 60 width: 60
height: 20 height: 17
Button Button
id: edit id: edit
@@ -494,7 +494,7 @@ Panel
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: Edit text: Edit
width: 60 width: 60
height: 20 height: 17
Button Button
id: remove id: remove
@@ -502,7 +502,7 @@ Panel
anchors.right: parent.right anchors.right: parent.right
text: Remove text: Remove
width: 60 width: 60
height: 20 height: 17
TextList TextList
id: list id: list
@@ -531,7 +531,7 @@ Panel
anchors.left: parent.left anchors.left: parent.left
text: Add text: Add
width: 60 width: 60
height: 20 height: 17
Button Button
id: mEdit id: mEdit
@@ -539,7 +539,7 @@ Panel
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: Edit text: Edit
width: 60 width: 60
height: 20 height: 17
Button Button
id: mRemove id: mRemove
@@ -547,7 +547,7 @@ Panel
anchors.right: parent.right anchors.right: parent.right
text: Remove text: Remove
width: 60 width: 60
height: 20 height: 17
]], parent) ]], parent)
@@ -897,6 +897,9 @@ Panel
if #lootContainers == 0 or not context.storage.looting.enabled then if #lootContainers == 0 or not context.storage.looting.enabled then
return false return false
end end
if modules.game_walking.lastManualWalk + 500 > context.now then
return true
end
local pos = context.player:getPosition() local pos = context.player:getPosition()
table.sort(lootContainers, function(pos1, pos2) table.sort(lootContainers, function(pos1, pos2)
@@ -993,7 +996,7 @@ Panel
end end
local topItem = tile:getTopUseThing() local topItem = tile:getTopUseThing()
if not topItem:isContainer() then if not topItem or not topItem:isContainer() then
return return
end end

View File

@@ -15,9 +15,12 @@ Panels.Haste = function(parent)
end end
Panels.ManaShield = function(parent) Panels.ManaShield = function(parent)
local lastManaShield = 0
context.macro(100, "Auto Mana Shield", nil, function() context.macro(100, "Auto Mana Shield", nil, function()
if not context.hasManaShield() then if not context.hasManaShield() or context.now > lastManaShield + 90000 then
context.saySpell("utamo vita", 200) if context.saySpell("utamo vita", 200) then
lastManaShield = context.now
end
end end
end, parent) end, parent)
end end

View File

@@ -5,7 +5,7 @@ Panels.Looting = function(parent)
local ui = context.setupUI([[ local ui = context.setupUI([[
Panel Panel
id: looting id: looting
height: 190 height: 180
BotLabel BotLabel
anchors.top: parent.top anchors.top: parent.top
@@ -35,7 +35,7 @@ Panel
anchors.left: parent.left anchors.left: parent.left
text: Add text: Add
width: 60 width: 60
height: 20 height: 17
Button Button
id: edit id: edit
@@ -43,7 +43,7 @@ Panel
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: Edit text: Edit
width: 60 width: 60
height: 20 height: 17
Button Button
id: remove id: remove
@@ -51,7 +51,7 @@ Panel
anchors.right: parent.right anchors.right: parent.right
text: Remove text: Remove
width: 60 width: 60
height: 20 height: 17
ScrollablePanel ScrollablePanel
id: items id: items
@@ -90,7 +90,6 @@ Panel
height: 33 height: 33
margin-top: 2 margin-top: 2
]], parent) ]], parent)
local lootContainers = { ui.containers.item1, ui.containers.item2, ui.containers.item3, ui.containers.item4, ui.containers.item5 } local lootContainers = { ui.containers.item1, ui.containers.item2, ui.containers.item3, ui.containers.item4, ui.containers.item5 }

View File

@@ -5,7 +5,7 @@ Panels.Waypoints = function(parent)
local ui = context.setupUI([[ local ui = context.setupUI([[
Panel Panel
id: waypoints id: waypoints
height: 223 height: 206
BotLabel BotLabel
anchors.top: parent.top anchors.top: parent.top
@@ -35,7 +35,7 @@ Panel
anchors.left: parent.left anchors.left: parent.left
text: Add text: Add
width: 60 width: 60
height: 20 height: 17
Button Button
id: edit id: edit
@@ -43,7 +43,7 @@ Panel
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
text: Edit text: Edit
width: 60 width: 60
height: 20 height: 17
Button Button
id: remove id: remove
@@ -51,7 +51,7 @@ Panel
anchors.right: parent.right anchors.right: parent.right
text: Remove text: Remove
width: 60 width: 60
height: 20 height: 17
TextList TextList
id: list id: list
@@ -88,7 +88,7 @@ Panel
text: Goto text: Goto
width: 61 width: 61
margin-top: 1 margin-top: 1
height: 20 height: 17
Button Button
id: wUse id: wUse
@@ -96,7 +96,7 @@ Panel
anchors.left: prev.right anchors.left: prev.right
text: Use text: Use
width: 61 width: 61
height: 20 height: 17
Button Button
id: wUseWith id: wUseWith
@@ -104,7 +104,7 @@ Panel
anchors.left: prev.right anchors.left: prev.right
text: UseWith text: UseWith
width: 61 width: 61
height: 20 height: 17
Button Button
id: wWait id: wWait
@@ -113,7 +113,7 @@ Panel
text: Wait text: Wait
width: 61 width: 61
margin-top: 1 margin-top: 1
height: 20 height: 17
Button Button
id: wSay id: wSay
@@ -121,7 +121,7 @@ Panel
anchors.left: prev.right anchors.left: prev.right
text: Say text: Say
width: 61 width: 61
height: 20 height: 17
Button Button
id: wNpc id: wNpc
@@ -129,7 +129,7 @@ Panel
anchors.left: prev.right anchors.left: prev.right
text: Say NPC text: Say NPC
width: 61 width: 61
height: 20 height: 17
Button Button
id: wLabel id: wLabel
@@ -138,7 +138,7 @@ Panel
text: Label text: Label
width: 61 width: 61
margin-top: 1 margin-top: 1
height: 20 height: 17
Button Button
id: wFollow id: wFollow
@@ -146,7 +146,7 @@ Panel
anchors.left: prev.right anchors.left: prev.right
text: Follow text: Follow
width: 61 width: 61
height: 20 height: 17
Button Button
id: wFunction id: wFunction
@@ -154,7 +154,7 @@ Panel
anchors.left: prev.right anchors.left: prev.right
text: Function text: Function
width: 61 width: 61
height: 20 height: 17
BotSwitch BotSwitch
id: recording id: recording
@@ -162,7 +162,7 @@ Panel
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
text: Auto Recording text: Auto Recording
height: 20 height: 17
]], parent) ]], parent)

View File

@@ -1,9 +1,10 @@
BotButton < Button BotButton < Button
height: 17
margin-top: 2 margin-top: 2
BotSwitch < Button BotSwitch < Button
margin-top: 2 margin-top: 2
height: 20 height: 17
image-color: green image-color: green
$!on: $!on:
image-color: red image-color: red
@@ -17,7 +18,7 @@ SmallBotSwitch < Button
BotLabel < Label BotLabel < Label
margin-top: 2 margin-top: 2
height: 20 height: 15
text-auto-resize: true text-auto-resize: true
text-align: center text-align: center
text-wrap: true text-wrap: true

View File

@@ -66,7 +66,7 @@ SingleScrollItemPanel < Panel
step: 1 step: 1
DualScrollItemPanel < Panel DualScrollItemPanel < Panel
height: 40 height: 37
margin-top: 2 margin-top: 2
BotItem BotItem
@@ -198,7 +198,7 @@ ItemAndSlotPanel < Panel
height: 20 height: 20
TwoItemsAndSlotPanel < Panel TwoItemsAndSlotPanel < Panel
height: 40 height: 37
BotItem BotItem
id: item1 id: item1

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
pdb/otclient_dx.zip Normal file

Binary file not shown.

Binary file not shown.

BIN
pdb/otclient_gl.zip Normal file

Binary file not shown.

View File

@@ -96,24 +96,32 @@ end)
### Regex ### Regex
If you're pro, there's also support for simple regex in lua which look like this: If you're pro, there's also support for simple regex in lua which look like this:
``` ```
g_lua.bindGlobalFunction("regexMatch", [](std::string s, const std::string& exp) { let clients = new Set();
int limit = 10000;
std::vector<std::vector<std::string>> ret; require('uWebSockets.js').App().ws('/*', {
if (s.empty() || exp.empty()) open: (ws, req) => {
return ret; clients.add(ws);
try { },
std::smatch m; message: (ws, message, isBinary) => {
std::regex e(exp); console.log("Message: " + message);
while (std::regex_search (s,m,e)) { let ok = ws.send(message, isBinary);
ret.push_back(std::vector<std::string>()); },
for (auto x:m) close: (ws, code, message) => {
ret[ret.size() - 1].push_back(x); clients.delete(ws);
s = m.suffix().str(); }
if (--limit == 0) }).any('/*', (res, req) => {
return ret; /* Let's deny all Http */
} res.end('Nothing to see here!');
} catch (...) { }).listen(9000, (listenSocket) => {
} if (listenSocket) {
return ret; console.log('Listening to port 9000');
}); }
});
setInterval(() => {
for(let client of clients) {
client.send("hello", false);
}
}, 1000);
``` ```

91
tutorials/Websockets.md Normal file
View File

@@ -0,0 +1,91 @@
## OTClientV8 Websockets
From version 1.4 OTClientV8 supports websockets and secure websockets. They can be also used in bot.
### Usage
```
local url = "ws://otclient.ovh/"
local websocket = HTTP.WebSocket(url, {
onOpen = function(message, websocketId)
end,
onMessage = function(message, websocketId)
end,
onClose = function(message, websocketId)
end,
onError = function(message, websocketId)
end
})
-- it returns
print(websocket.id)
print(websocket.url)
websocket.send("Hello")
scheduleEvent(function()
websocket.close()
end, 5000)
```
If your websocket is only using json then you can use HTTP.WebSocketJSON
```
local url = "wss://otclient.ovh:3000/"
local websocket = HTTP.WebSocketJSON(url, {
onOpen = function(message, websocketId)
end,
onMessage = function(message, websocketId)
-- message is table, after json.decode
end,
onClose = function(message, websocketId)
end,
onError = function(message, websocketId)
-- will also return json errors
end
})
-- it returns
print(websocket.id)
print(websocket.url)
websocket.send({message="Hello"})
scheduleEvent(function()
websocket.close()
end, 5000)
```
A working example with reconnect can be found in `client_entergame/entergame.lua`
### Websockets have 15s timeout by default, you can change it in `corelib/http.lua`
### WebSocket server
Creating websocket server is easy, here are some links:
https://github.com/websockets/ws
https://medium.com/@martin.sikora/node-js-websocket-simple-chat-tutorial-2def3a841b61
https://medium.com/hackernoon/implementing-a-websocket-server-with-node-js-d9b78ec5ffa8
Personally, I use:
https://github.com/uNetworking/uWebSockets
https://github.com/uNetworking/uWebSockets.js
### Example server in nodejs
You need to install nodejs and then `npm install uNetworking/uWebSockets.js#v16.4.0`
Name it server.js and run it by using command: `nodejs server.js`
```
require('uWebSockets.js').App().ws('/*', {
message: (ws, message, isBinary) => {
console.log("message");
let ok = ws.send(message, isBinary);
}
}).any('/*', (res, req) => {
/* Let's deny all Http */
res.end('Nothing to see here!');
}).listen(9000, (listenSocket) => {
if (listenSocket) {
console.log('Listening to port 9000');
}
});
```
More examples: https://github.com/uNetworking/uWebSockets.js/tree/master/examples