mirror of
https://github.com/OTCv8/otclientv8.git
synced 2025-05-05 13:29:22 +02:00
Fixes for bot and websocket based entergame
This commit is contained in:
parent
c750ea65f8
commit
ddb155333d
@ -14,6 +14,7 @@ It's based on https://github.com/edubart/otclient and it's not backward compatib
|
|||||||
- Rewritten path finding and auto walking
|
- Rewritten path finding and auto walking
|
||||||
- Rewritten walking system with animations
|
- Rewritten walking system with animations
|
||||||
- HTTP/HTTPS lua API with JSON support
|
- HTTP/HTTPS lua API with JSON support
|
||||||
|
- WebSocket lua API
|
||||||
- Auto updater with failsafe (recovery) mode
|
- Auto updater with failsafe (recovery) mode
|
||||||
- New filesystem
|
- New filesystem
|
||||||
- File encryption and compression
|
- File encryption and compression
|
||||||
@ -28,6 +29,7 @@ It's based on https://github.com/edubart/otclient and it's not backward compatib
|
|||||||
- Much more client options
|
- Much more client options
|
||||||
- Removed a lot of useless and outdated things
|
- Removed a lot of useless and outdated things
|
||||||
- Advanced bot (https://github.com/OTCv8/otclientv8_bot)
|
- Advanced bot (https://github.com/OTCv8/otclientv8_bot)
|
||||||
|
- Linux version
|
||||||
- Support for proxies to lower latency and protect against DDoS (extra paid option)
|
- Support for proxies to lower latency and protect against DDoS (extra paid option)
|
||||||
- Bot protection (extra paid option)
|
- Bot protection (extra paid option)
|
||||||
- [Soon] Mobile application for quick authorization
|
- [Soon] Mobile application for quick authorization
|
||||||
|
96
api/websockets/app.ts
Normal file
96
api/websockets/app.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
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}`)
|
||||||
|
}
|
||||||
|
});
|
36
api/websockets/config.json
Normal file
36
api/websockets/config.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
84
api/websockets/login.ts
Normal file
84
api/websockets/login.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
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) {
|
||||||
|
|
||||||
|
}
|
22
api/websockets/package.json
Normal file
22
api/websockets/package.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
11
api/websockets/tsconfig.json
Normal file
11
api/websockets/tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es6",
|
||||||
|
"lib": ["es6"],
|
||||||
|
"sourceMap": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
5
api/websockets/typings.json
Normal file
5
api/websockets/typings.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"mysql2": "registry:npm/mysql2#1.1.1+20170314181009"
|
||||||
|
}
|
||||||
|
}
|
@ -310,6 +310,7 @@ function EnterGame.checkWebsocket()
|
|||||||
if webSocket then
|
if webSocket then
|
||||||
webSocket:close()
|
webSocket:close()
|
||||||
webSocket = nil
|
webSocket = nil
|
||||||
|
newLogin.code:setText("")
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@ -328,13 +329,14 @@ function EnterGame.checkWebsocket()
|
|||||||
webSocket = HTTP.WebSocketJSON(url, {
|
webSocket = HTTP.WebSocketJSON(url, {
|
||||||
onOpen = function(message, webSocketId)
|
onOpen = function(message, webSocketId)
|
||||||
if webSocket and webSocket.id == webSocketId then
|
if webSocket and webSocket.id == webSocketId then
|
||||||
webSocket.send({type="init", uid=G.uuid, version=APP_VERSION})
|
webSocket.send({type="init", uid=G.UUID, version=APP_VERSION})
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
onMessage = function(message, webSocketId)
|
onMessage = function(message, webSocketId)
|
||||||
if webSocket and webSocket.id == webSocketId then
|
if webSocket and webSocket.id == webSocketId then
|
||||||
if message.type == "login" then
|
if message.type == "login" then
|
||||||
webSocketLoginPacket = nil
|
webSocketLoginPacket = nil
|
||||||
|
EnterGame.hide()
|
||||||
onHTTPResult(message, nil)
|
onHTTPResult(message, nil)
|
||||||
elseif message.type == "quick_login" and message.code and message.qrcode then
|
elseif message.type == "quick_login" and message.code and message.qrcode then
|
||||||
EnterGame.showNewLogin(message.code, message.qrcode)
|
EnterGame.showNewLogin(message.code, message.qrcode)
|
||||||
|
@ -130,7 +130,12 @@ context.useRune = function(itemid, target, lastSpellTimeout)
|
|||||||
end
|
end
|
||||||
context.userune = context.useRune
|
context.userune = context.useRune
|
||||||
|
|
||||||
context.findItem = g_game.findItemInContainers
|
context.findItem = function(itemId, subType)
|
||||||
|
if subType == nil then
|
||||||
|
subType = -1
|
||||||
|
end
|
||||||
|
return g_game.findItemInContainers(itemId, subType)
|
||||||
|
end
|
||||||
|
|
||||||
context.attack = g_game.attack
|
context.attack = g_game.attack
|
||||||
context.cancelAttack = g_game.cancelAttack
|
context.cancelAttack = g_game.cancelAttack
|
||||||
|
@ -28,6 +28,15 @@ context.getFinger = function() return context.getInventoryItem(context.SlotFinge
|
|||||||
context.getAmmo = function() return context.getInventoryItem(context.SlotAmmo) end
|
context.getAmmo = function() return context.getInventoryItem(context.SlotAmmo) end
|
||||||
context.getPurse = function() return context.getInventoryItem(context.SlotPurse) end
|
context.getPurse = function() return context.getInventoryItem(context.SlotPurse) end
|
||||||
|
|
||||||
|
|
||||||
context.getContainers = function() return g_game.getContainers() end
|
context.getContainers = function() return g_game.getContainers() end
|
||||||
context.getContainer = function(index) return g_game.getContainer(index) end
|
context.getContainer = function(index) return g_game.getContainer(index) end
|
||||||
|
|
||||||
|
context.moveToSlot = function(item, slot, count)
|
||||||
|
if not item then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if count == nil then
|
||||||
|
count = item:getCount()
|
||||||
|
end
|
||||||
|
return g_game.move(item, {x=65535, y=slot, z=0}, count)
|
||||||
|
end
|
@ -276,9 +276,9 @@ Panels.Equip = function(parent)
|
|||||||
if slotItem and (slotItem:getId() == item1 or slotItem:getId() == item2) then
|
if slotItem and (slotItem:getId() == item1 or slotItem:getId() == item2) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local newItem = context.findItem(context.storage[panelName].item1, 0)
|
local newItem = context.findItem(context.storage[panelName].item1)
|
||||||
if not newItem then
|
if not newItem then
|
||||||
newItem = context.findItem(context.storage[panelName].item2, 0)
|
newItem = context.findItem(context.storage[panelName].item2)
|
||||||
if not newItem then
|
if not newItem then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user