mirror of
https://github.com/OTCv8/otclientv8.git
synced 2025-04-29 18:59:20 +02:00
Version 1.6 - important fix for high memory usage
This commit is contained in:
parent
d15cc347dc
commit
1d2bdf855d
@ -1,6 +1,6 @@
|
||||
# OTClientV8
|
||||
|
||||
Tibia client design for versions 7.40 - 10.99
|
||||
Tibia client design for versions 7.40 - 11.00
|
||||
It's based on https://github.com/edubart/otclient and it's not backward compatible.
|
||||
|
||||
## DISCORD: https://discord.gg/feySup6
|
||||
@ -42,7 +42,11 @@ It's based on https://github.com/edubart/otclient and it's not backward compatib
|
||||
# Paid version
|
||||
The difference between paid version and this one is that the 1st one comes with c++ sources and has better support. You may need c++ source if you want to add some more advanced modifications, better encryption, bot protection or some other things. The free version doesn't offer technical support, you need to follow tutorials and in case of any bug or problem you should submit an issue on github. Visit http://otclient.ovh if you want to know more about paid version and other extra services.
|
||||
|
||||
# Quick Start
|
||||
# Quick Start for players
|
||||
|
||||
Download whole repository and run binary.
|
||||
|
||||
# Quick Start for server owners
|
||||
|
||||
Open `init.lua` and edit:
|
||||
|
||||
|
@ -1,96 +0,0 @@
|
||||
import { App } from 'uWebSockets.js';
|
||||
import * as Login from './login';
|
||||
const config = require("./config.json");
|
||||
|
||||
let Sessions = new Set();
|
||||
let Clients = {};
|
||||
let QuickLogin = {};
|
||||
|
||||
App({
|
||||
// options for ssl
|
||||
key_file_name: 'key.pem',
|
||||
cert_file_name: 'cert.pem'
|
||||
}).ws('/*', {
|
||||
compression: 0,
|
||||
maxPayloadLength: 64 * 1024,
|
||||
idleTimeout: 10,
|
||||
open: (ws, req) => {
|
||||
ws.uid = null;
|
||||
Sessions.add(ws);
|
||||
},
|
||||
close: (ws, code, message) => {
|
||||
if (ws.uid && Clients[ws.uid] == ws) {
|
||||
delete Clients[ws.uid];
|
||||
delete QuickLogin[ws.short_code];
|
||||
delete QuickLogin[ws.full_code];
|
||||
}
|
||||
Sessions.delete(ws);
|
||||
},
|
||||
message: (ws, message, isBinary) => {
|
||||
try {
|
||||
let data = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(message)));
|
||||
if (data["type"] == "init") {
|
||||
if (ws.uid || typeof (data["uid"]) != "string" || data["uid"].length < 10) {
|
||||
return ws.end(1, "Invalid init message"); // already has an uid or uid is invalid
|
||||
}
|
||||
ws.uid = data["uid"];
|
||||
ws.version = data["version"]
|
||||
if (Clients[ws.uid]) {
|
||||
Clients[ws.uid].close();
|
||||
}
|
||||
ws.short_code = "XXXX";
|
||||
ws.full_code = "Login on server otclient.ovh. XXXX";
|
||||
Clients[ws.uid] = ws;
|
||||
QuickLogin[ws.short_code] = ws;
|
||||
QuickLogin[ws.full_code] = ws;
|
||||
return ws.send(JSON.stringify({
|
||||
"type": "quick_login",
|
||||
"code": ws.short_code,
|
||||
"qrcode": ws.full_code,
|
||||
"message": ""
|
||||
}));
|
||||
}
|
||||
if (!ws.uid) {
|
||||
return ws.end(2, "Missing uid");
|
||||
}
|
||||
if (data["type"] == "login") {
|
||||
return Login.login(ws, data["account"], data["password"]);
|
||||
}
|
||||
} catch (e) {
|
||||
try {
|
||||
return ws.end(3, "Exception");
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
}).any('/login', (res, req) => {
|
||||
let buffer: string = "";
|
||||
res.onData((chunk, last) => {
|
||||
try {
|
||||
buffer += String.fromCharCode.apply(null, new Uint8Array(chunk));
|
||||
if (!last) {
|
||||
return;
|
||||
}
|
||||
const data = JSON.parse(buffer);
|
||||
const code = data["code"];
|
||||
const client = QuickLogin[code];
|
||||
if (!client) {
|
||||
return res.end("Invalid code");
|
||||
}
|
||||
Login.quickLogin(res, client, data);
|
||||
} catch (e) {
|
||||
res.end("Exception");
|
||||
}
|
||||
});
|
||||
|
||||
res.onAborted(() => {
|
||||
return res.end("Aborted");
|
||||
});
|
||||
}).any('/*', (res, req) => {
|
||||
res.end('404');
|
||||
}).listen(config.port, (listenSocket) => {
|
||||
if (listenSocket) {
|
||||
console.log(`Listening to port ${config.port}`);
|
||||
} else {
|
||||
console.log(`Error, can't listen on port ${config.port}`)
|
||||
}
|
||||
});
|
@ -1,36 +0,0 @@
|
||||
{
|
||||
"port": 88,
|
||||
"sql": {
|
||||
"host": "otclient.ovh",
|
||||
"user": "otclient",
|
||||
"password": "otclient",
|
||||
"database": "otclient"
|
||||
},
|
||||
"maxLogins": 10,
|
||||
"blockTime": 60,
|
||||
"hash": "sha1",
|
||||
"serverName": "OTClientV8",
|
||||
"serverIP": "otclient.ovh",
|
||||
"serverPort": 7172,
|
||||
"version": 1099,
|
||||
"things": {
|
||||
"sprites": [ "1099/Tibia.spr", "63d38646597649a55a8be463d6c0fb49" ],
|
||||
"data": [ "1099/Tibia.dat", "ae7157cfff42f14583d6363e77044df7" ]
|
||||
},
|
||||
"customProtocol": null,
|
||||
"options": {
|
||||
"allowFullView": true
|
||||
},
|
||||
"features": {
|
||||
"22": true,
|
||||
"25": true,
|
||||
"30": true,
|
||||
"80": true,
|
||||
"90": true,
|
||||
"95": true
|
||||
},
|
||||
"proxies": {
|
||||
|
||||
},
|
||||
"rsa": "109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413"
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
import { HttpResponse, WebSocket } from 'uWebSockets.js';
|
||||
import * as mysql from 'mysql2/promise';
|
||||
import * as crypto from 'crypto';
|
||||
const config = require("./config.json");
|
||||
|
||||
function hash(algorithm: string, data: string): string {
|
||||
return crypto.createHash(algorithm).update(data).digest("hex");
|
||||
}
|
||||
|
||||
function time(): number {
|
||||
return new Date().getTime();
|
||||
}
|
||||
|
||||
export async function login(ws: WebSocket, login: string, password: string) {
|
||||
let sql : mysql.Connection = null;
|
||||
try {
|
||||
sql = await mysql.createConnection({
|
||||
host: config.sql.host,
|
||||
user: config.sql.user,
|
||||
password: config.sql.password,
|
||||
database: config.sql.database
|
||||
});
|
||||
|
||||
let hash_password = password
|
||||
if (config.hash == "md5") {
|
||||
hash_password = hash("md5", password);
|
||||
} else if (config.hash == "sha1") {
|
||||
hash_password = hash("sha1", password);
|
||||
}
|
||||
|
||||
const [accounts, accountFields] = await sql.execute('SELECT * FROM `accounts` where `name` = ? and `password` = ?', [login, hash_password]);
|
||||
if (accounts.length != 1) {
|
||||
await sql.end();
|
||||
return ws.send(JSON.stringify({"type": "login", "error": "Invalid account/password"}), false);
|
||||
}
|
||||
const account = accounts[0];
|
||||
const [players, playersFields] = await sql.execute('SELECT * FROM `players` where `account_id` = ?', [account.id]);
|
||||
await sql.end();
|
||||
|
||||
let response = {
|
||||
"type": "login",
|
||||
"error": "",
|
||||
"rsa": config.rsa,
|
||||
"version": config.version,
|
||||
"things": config.things,
|
||||
"customProtocol": config.customProtocol,
|
||||
"session": "",
|
||||
"characters": [],
|
||||
"account": {},
|
||||
"options": config.options,
|
||||
"features": config.features,
|
||||
"proxies": config.proxies
|
||||
}
|
||||
|
||||
response["session"] = `${login}\n${password}\n\n${time()}`;
|
||||
|
||||
response["account"]["status"] = 0; // 0=ok, 1=frozen, 2=supsended
|
||||
response["account"]["subStatus"] = 1; // 0=free, 1=premium
|
||||
response["account"]["premDays"] = 65535;
|
||||
|
||||
for (let i = 0; i < players.length; ++i) {
|
||||
response.characters.push({
|
||||
"name": players[i].name,
|
||||
"worldName": config.serverName,
|
||||
"worldIp": config.serverIP,
|
||||
"worldPort": config.serverPort
|
||||
})
|
||||
}
|
||||
|
||||
console.log(response);
|
||||
ws.send(JSON.stringify(response), false);
|
||||
} catch (e) {
|
||||
try {
|
||||
await sql.end()
|
||||
} catch (e) { };
|
||||
try {
|
||||
ws.end(5, "Login exception");
|
||||
} catch (e) { };
|
||||
}
|
||||
}
|
||||
|
||||
export async function quickLogin(res : HttpResponse, ws: WebSocket, data: any) {
|
||||
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
{
|
||||
"name": "otcv8socks",
|
||||
"version": "1.0.0",
|
||||
"description": "Websockets server for otclientv8",
|
||||
"main": "app.js",
|
||||
"author": {
|
||||
"name": ""
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"clean": "tsc --build --clean"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mysql2": "github:types/mysql2",
|
||||
"@types/node": "^8.0.14",
|
||||
"typescript": "^3.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"mysql2": "^2.0.1",
|
||||
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v16.4.0"
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"lib": ["es6"],
|
||||
"sourceMap": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"mysql2": "registry:npm/mysql2#1.1.1+20170314181009"
|
||||
}
|
||||
}
|
10
init.lua
10
init.lua
@ -14,11 +14,11 @@ Services = {
|
||||
|
||||
-- Servers accept http login url, websocket login url or ip:port:version
|
||||
Servers = {
|
||||
OTClientV8 = "http://otclient.ovh/api/login.php",
|
||||
OTClientV8Websocket = "wss://otclient.ovh:3000/",
|
||||
OTClientV8proxy = "http://otclient.ovh/api/login.php?proxy=1",
|
||||
OTClientV8c = "otclient.ovh:7171:1099:25:30:80:90",
|
||||
OTClientV8Test = "http://otclient.ovh/api/login2.php",
|
||||
-- OTClientV8 = "http://otclient.ovh/api/login.php",
|
||||
-- OTClientV8Websocket = "wss://otclient.ovh:3000/",
|
||||
-- OTClientV8proxy = "http://otclient.ovh/api/login.php?proxy=1",
|
||||
-- OTClientV8ClassicWithFeatures = "otclient.ovh:7171:1099:25:30:80:90",
|
||||
-- OTClientV8Classic = "otclient.ovh:7171:1099"
|
||||
}
|
||||
ALLOW_CUSTOM_SERVERS = true -- if true it shows option ANOTHER on server list
|
||||
-- CONFIG END
|
||||
|
@ -31,14 +31,6 @@ local function tryLogin(charInfo, tries)
|
||||
|
||||
CharacterList.hide()
|
||||
|
||||
-- proxies for not http login users
|
||||
if charInfo.worldHost == "0.0.0.0" and g_proxy then
|
||||
g_proxy.clear()
|
||||
-- g_proxy.addProxy(proxyHost, proxyPort, proxyPriority)
|
||||
g_proxy.addProxy("163.172.147.135", 7162, 0)
|
||||
g_proxy.addProxy("158.69.68.42", 7162, 0)
|
||||
end
|
||||
|
||||
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...'))
|
||||
@ -243,9 +235,11 @@ function CharacterList.terminate()
|
||||
CharacterList = nil
|
||||
end
|
||||
|
||||
function CharacterList.create(characters, account, otui)
|
||||
function CharacterList.create(characters, account, otui, websocket)
|
||||
if not otui then otui = 'characterlist' end
|
||||
|
||||
if websocket then
|
||||
websocket:close()
|
||||
end
|
||||
if charactersWindow then
|
||||
charactersWindow:destroy()
|
||||
end
|
||||
|
@ -15,7 +15,7 @@ local serverSelector
|
||||
local clientVersionSelector
|
||||
local serverHostTextEdit
|
||||
local rememberPasswordBox
|
||||
local protos = {"740", "760", "772", "800", "810", "854", "860", "1077", "1090", "1096", "1098", "1099", "1100"}
|
||||
local protos = {"740", "760", "772", "792", "800", "810", "854", "860", "1077", "1090", "1096", "1098", "1099", "1100"}
|
||||
|
||||
local webSocket
|
||||
local webSocketLoginPacket
|
||||
@ -53,10 +53,14 @@ local function onCharacterList(protocol, characters, account, otui)
|
||||
loadBox:destroy()
|
||||
loadBox = nil
|
||||
end
|
||||
|
||||
CharacterList.create(characters, account, otui)
|
||||
|
||||
CharacterList.create(characters, account, otui, webSocket)
|
||||
CharacterList.show()
|
||||
|
||||
if webSocket then
|
||||
webSocket = nil
|
||||
end
|
||||
|
||||
g_settings.save()
|
||||
end
|
||||
|
||||
@ -76,18 +80,22 @@ end
|
||||
|
||||
local 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
|
||||
correctThings = false
|
||||
incorrectThings = incorrectThings .. "Missing file: " .. thingdata[1] .. "\n"
|
||||
end
|
||||
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"
|
||||
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
|
||||
@ -95,6 +103,12 @@ local function validateThings(things)
|
||||
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
|
||||
|
||||
@ -177,10 +191,6 @@ local function onHTTPResult(data, err)
|
||||
end
|
||||
end
|
||||
|
||||
if webSocket then
|
||||
webSocket:close()
|
||||
webSocket = nil
|
||||
end
|
||||
onCharacterList(nil, characters, account, nil)
|
||||
end
|
||||
|
||||
@ -310,22 +320,16 @@ function EnterGame.checkWebsocket()
|
||||
if webSocket then
|
||||
webSocket:close()
|
||||
webSocket = nil
|
||||
newLogin.code:setText("")
|
||||
end
|
||||
return
|
||||
end
|
||||
if webSocket then
|
||||
if webSocket.url == url then
|
||||
if newLogin:isHidden() and newLogin.code:getText():len() > 1 then
|
||||
newLogin:show()
|
||||
newLogin:raise()
|
||||
end
|
||||
return
|
||||
end
|
||||
webSocket:close()
|
||||
webSocket = nil
|
||||
end
|
||||
newLogin.code:setText("")
|
||||
webSocket = HTTP.WebSocketJSON(url, {
|
||||
onOpen = function(message, webSocketId)
|
||||
if webSocket and webSocket.id == webSocketId then
|
||||
@ -338,8 +342,8 @@ function EnterGame.checkWebsocket()
|
||||
webSocketLoginPacket = nil
|
||||
EnterGame.hide()
|
||||
onHTTPResult(message, nil)
|
||||
elseif message.type == "quick_login" and message.code and message.qrcode then
|
||||
EnterGame.showNewLogin(message.code, message.qrcode)
|
||||
elseif message.type == "quick_login" and message.qrcode then
|
||||
EnterGame.showNewLogin(message.qrcode)
|
||||
end
|
||||
end
|
||||
end,
|
||||
@ -365,10 +369,15 @@ function EnterGame.hideNewLogin()
|
||||
newLogin:hide()
|
||||
end
|
||||
|
||||
function EnterGame.showNewLogin(code, qrcode)
|
||||
function EnterGame.showNewLogin(qrcode)
|
||||
if enterGame:isHidden() then return end
|
||||
newLogin.code:setText(code)
|
||||
newLogin.qrcode:setQRCode(qrcode, 1)
|
||||
newLogin.qrcode:setQRCode("https://quath.co/0/" .. qrcode, 1)
|
||||
newLogin.qrcode:setEnabled(true)
|
||||
local clickFunction = function()
|
||||
g_platform.openUrl("qauth://" .. qrcode)
|
||||
end
|
||||
newLogin.qrcode.onClick = clickFunction
|
||||
newLogin.quathlogo.onClick = clickFunction
|
||||
if newLogin:isHidden() then
|
||||
newLogin:show()
|
||||
newLogin:raise()
|
||||
@ -449,7 +458,7 @@ function EnterGame.doLogin()
|
||||
|
||||
local incorrectThings = validateThings(things)
|
||||
if #incorrectThings > 0 then
|
||||
g_logger.info(incorrectThings)
|
||||
g_logger.error(incorrectThings)
|
||||
if Updater then
|
||||
return Updater.updateThings(things, incorrectThings)
|
||||
else
|
||||
|
@ -3,11 +3,11 @@ StaticWindow
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
margin-right: 20
|
||||
id: newLoginPanel
|
||||
width: 230
|
||||
height: 350
|
||||
width: 240
|
||||
height: 320
|
||||
!text: tr('Quick Login & Registration')
|
||||
|
||||
Label
|
||||
UIButton
|
||||
id: qrcode
|
||||
width: 200
|
||||
height: 200
|
||||
@ -17,7 +17,7 @@ StaticWindow
|
||||
image-smooth: false
|
||||
margin-top: 5
|
||||
|
||||
Label
|
||||
UIButton
|
||||
id: quathlogo
|
||||
width: 66
|
||||
height: 40
|
||||
@ -33,28 +33,18 @@ StaticWindow
|
||||
anchors.right: qrcode.right
|
||||
text-align: center
|
||||
text-auto-resize: true
|
||||
!text: tr("Scan QR code or process\nbellow code to register or login")
|
||||
!text: tr("Scan or click QR code\nto register or login")
|
||||
height: 40
|
||||
margin-top: 10
|
||||
margin-bottom: 5
|
||||
|
||||
Label
|
||||
id: code
|
||||
height: 20
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: prev.left
|
||||
anchors.right: prev.right
|
||||
text-align: center
|
||||
font: sans-bold-16px
|
||||
margin-top: 10
|
||||
text: XXXXXX
|
||||
|
||||
Label
|
||||
Button
|
||||
anchors.top: prev.bottom
|
||||
anchors.left: prev.left
|
||||
anchors.right: prev.right
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
text-align: center
|
||||
!text: tr("Click to get Android/iOS app")
|
||||
!text: tr("Click to get PC/Android/iOS app")
|
||||
height: 20
|
||||
margin-top: 10
|
||||
color: #FFFFFF
|
||||
color: #FFFFFF
|
||||
@onClick: g_platform.openUrl("http://qauth.co")
|
@ -116,6 +116,7 @@ function sendStats()
|
||||
classic = tostring(g_settings.getBoolean("classicView")),
|
||||
fullscreen = tostring(g_window.isFullscreen()),
|
||||
vsync = tostring(g_settings.getBoolean("vsync")),
|
||||
autoReconnect = tostring(g_settings.getBoolean("autoReconnect")),
|
||||
window_width = g_window.getWidth(),
|
||||
window_height = g_window.getHeight(),
|
||||
player_name = g_game.getCharacterName(),
|
||||
@ -132,7 +133,8 @@ function sendStats()
|
||||
cpu = g_platform.getCPUName(),
|
||||
mem = g_platform.getTotalSystemMemory(),
|
||||
mem_usage = g_platform.getMemoryUsage(),
|
||||
os_name = g_platform.getOSName()
|
||||
os_name = g_platform.getOSName(),
|
||||
uptime = g_clock.seconds()
|
||||
}
|
||||
}
|
||||
if g_proxy then
|
||||
|
@ -9,9 +9,9 @@ end
|
||||
function updateFeatures(version)
|
||||
g_game.resetFeatures()
|
||||
|
||||
-- you can add custom features here, list of them in modules\gamelib\const.lua
|
||||
-- you can add custom features here, list of them is in the modules\gamelib\const.lua
|
||||
g_game.enableFeature(GameBot)
|
||||
g_game.enableFeature(GameMinimapLimitedToSingleFloor)
|
||||
--g_game.enableFeature(GameMinimapLimitedToSingleFloor) -- it will generate minimap only for current floor
|
||||
--g_game.enableFeature(GameSpritesAlphaChannel)
|
||||
|
||||
if(version >= 770) then
|
||||
@ -91,6 +91,10 @@ function updateFeatures(version)
|
||||
g_game.enableFeature(GameAdditionalVipInfo)
|
||||
end
|
||||
|
||||
if(version >= 972) then
|
||||
g_game.enableFeature(GameDoublePlayerGoodsMoney)
|
||||
end
|
||||
|
||||
if(version >= 980) then
|
||||
g_game.enableFeature(GamePreviewState)
|
||||
g_game.enableFeature(GameClientVersion)
|
||||
|
@ -67,7 +67,12 @@ end
|
||||
function bindKeys()
|
||||
gameRootPanel:setAutoRepeatDelay(10)
|
||||
|
||||
g_keyboard.bindKeyPress('Escape', function() g_game.cancelAttackAndFollow() end, gameRootPanel)
|
||||
local lastAction = 0
|
||||
g_keyboard.bindKeyPress('Escape', function()
|
||||
if lastAction + 50 > g_clock.millis() then return end
|
||||
lastAction = g_clock.millis()
|
||||
g_game.cancelAttackAndFollow()
|
||||
end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Ctrl+=', function() if g_game.getFeature(GameNoDebug) then return end gameMapPanel:zoomIn() end, gameRootPanel)
|
||||
g_keyboard.bindKeyPress('Ctrl+-', function() if g_game.getFeature(GameNoDebug) then return end gameMapPanel:zoomOut() end, gameRootPanel)
|
||||
g_keyboard.bindKeyDown('Ctrl+Q', function() tryLogout(false) end, gameRootPanel)
|
||||
|
@ -7,6 +7,7 @@ lastFinishedStep = 0
|
||||
autoWalkEvent = nil
|
||||
firstStep = true
|
||||
walkLock = 0
|
||||
walkEvent = nil
|
||||
lastWalk = 0
|
||||
lastTurn = 0
|
||||
lastTurnDirection = 0
|
||||
@ -202,7 +203,7 @@ function changeWalkDir(dir, pop)
|
||||
end
|
||||
|
||||
function smartWalk(dir)
|
||||
scheduleEvent(function()
|
||||
walkEvent = scheduleEvent(function()
|
||||
if g_keyboard.getModifiers() == KeyboardNoModifier then
|
||||
local direction = smartWalkDir or dir
|
||||
walk(direction)
|
||||
@ -371,6 +372,8 @@ function turn(dir, repeated)
|
||||
return
|
||||
end
|
||||
|
||||
removeEvent(walkEvent)
|
||||
|
||||
if not repeated or (lastTurn + 100 < g_clock.millis()) then
|
||||
g_game.turn(dir)
|
||||
changeWalkDir(dir)
|
||||
|
@ -162,6 +162,10 @@ GamePrey = 78
|
||||
GameExtendedOpcode = 80
|
||||
GameMinimapLimitedToSingleFloor = 81
|
||||
|
||||
GameDoubleLevel = 83
|
||||
GameDoubleSoul = 84
|
||||
GameDoublePlayerGoodsMoney = 85
|
||||
|
||||
GameNewWalking = 90
|
||||
GameSlowerManualWalking = 91
|
||||
GameExtendedNewWalking = 92
|
||||
|
BIN
otclient_dx.exe
BIN
otclient_dx.exe
Binary file not shown.
BIN
otclient_gl.exe
BIN
otclient_gl.exe
Binary file not shown.
BIN
otclient_linux
BIN
otclient_linux
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user