mirror of
https://github.com/mehah/otclient
synced 2026-08-15 16:29:06 -04:00
fix: Websocket Vbot 4.8 (#1191)
This commit is contained in:
parent
754fa407f2
commit
d2a40c8451
6 changed files with 211 additions and 82 deletions
|
|
@ -24,7 +24,7 @@ local luaFiles = {
|
|||
"extras",
|
||||
"cavebot",
|
||||
"playerlist",
|
||||
--"BotServer", --currently 02/03/24 No websocket
|
||||
"BotServer",
|
||||
"alarms",
|
||||
"Conditions",
|
||||
"Equipper",
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ public:
|
|||
void setMasterId(const uint32_t id) { m_masterId = id; }
|
||||
void setName(std::string_view name);
|
||||
void setHealthPercent(uint8_t healthPercent);
|
||||
void setManaPercent(uint8_t value) { m_manaPercent = value; }
|
||||
void setDirection(Otc::Direction direction);
|
||||
void setOutfit(const Outfit& outfit);
|
||||
void setLight(const Light& light) { m_light = light; }
|
||||
|
|
@ -115,6 +116,7 @@ public:
|
|||
uint8_t getType() { return m_type; }
|
||||
uint8_t getIcon() { return m_icon; }
|
||||
uint8_t getHealthPercent() { return m_healthPercent; }
|
||||
uint8_t getManaPercent() { return m_manaPercent; }
|
||||
|
||||
uint16_t getSpeed() { return m_speed; }
|
||||
uint16_t getBaseSpeed() { return m_baseSpeed; }
|
||||
|
|
@ -292,6 +294,7 @@ private:
|
|||
|
||||
uint8_t m_type;
|
||||
uint8_t m_healthPercent{ 101 };
|
||||
uint8_t m_manaPercent{ 101 };
|
||||
uint8_t m_skull{ Otc::SkullNone };
|
||||
uint8_t m_icon{ Otc::NpcIconNone };
|
||||
uint8_t m_shield{ Otc::ShieldNone };
|
||||
|
|
|
|||
|
|
@ -570,6 +570,7 @@ void Client::registerLuaFunctions()
|
|||
g_lua.bindClassMemberFunction<Creature>("getMasterId", &Creature::getMasterId);
|
||||
g_lua.bindClassMemberFunction<Creature>("getName", &Creature::getName);
|
||||
g_lua.bindClassMemberFunction<Creature>("getHealthPercent", &Creature::getHealthPercent);
|
||||
g_lua.bindClassMemberFunction<Creature>("getManaPercent", &Creature::getManaPercent);
|
||||
g_lua.bindClassMemberFunction<Creature>("getSpeed", &Creature::getSpeed);
|
||||
g_lua.bindClassMemberFunction<Creature>("getBaseSpeed", &Creature::getBaseSpeed);
|
||||
g_lua.bindClassMemberFunction<Creature>("getSkull", &Creature::getSkull);
|
||||
|
|
@ -591,6 +592,7 @@ void Client::registerLuaFunctions()
|
|||
g_lua.bindClassMemberFunction<Creature>("setTypeTexture", &Creature::setTypeTexture);
|
||||
g_lua.bindClassMemberFunction<Creature>("setIconTexture", &Creature::setIconTexture);
|
||||
g_lua.bindClassMemberFunction<Creature>("setStaticWalking", &Creature::setStaticWalking);
|
||||
g_lua.bindClassMemberFunction<Creature>("setManaPercent", &Creature::setManaPercent);
|
||||
g_lua.bindClassMemberFunction<Creature>("showStaticSquare", &Creature::showStaticSquare);
|
||||
g_lua.bindClassMemberFunction<Creature>("hideStaticSquare", &Creature::hideStaticSquare);
|
||||
g_lua.bindClassMemberFunction<Creature>("isWalking", &Creature::isWalking);
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ std::string ResourceManager::readFileContents(const std::string& fileName)
|
|||
|
||||
PHYSFS_File* file = PHYSFS_openRead(fullPath.c_str());
|
||||
if (!file)
|
||||
throw Exception("unable to open file '%s': %s", fullPath, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
||||
throw Exception("unable to open file '{}': {}", fullPath, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
||||
|
||||
const int fileSize = PHYSFS_fileLength(file);
|
||||
std::string buffer(fileSize, 0);
|
||||
|
|
@ -325,7 +325,7 @@ FileStreamPtr ResourceManager::openFile(const std::string& fileName)
|
|||
|
||||
PHYSFS_File* file = PHYSFS_openRead(fullPath.c_str());
|
||||
if (!file)
|
||||
throw Exception("unable to open file '%s': %s", fullPath, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
||||
throw Exception("unable to open file '{}': {}", fullPath, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
||||
return { std::make_shared<FileStream>(fullPath, file, false) };
|
||||
}
|
||||
|
||||
|
|
@ -333,7 +333,7 @@ FileStreamPtr ResourceManager::appendFile(const std::string& fileName) const
|
|||
{
|
||||
PHYSFS_File* file = PHYSFS_openAppend(fileName.c_str());
|
||||
if (!file)
|
||||
throw Exception("failed to append file '%s': %s", fileName, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
||||
throw Exception("failed to append file '{}': {}", fileName, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
||||
return { std::make_shared<FileStream>(fileName, file, true) };
|
||||
}
|
||||
|
||||
|
|
@ -341,7 +341,7 @@ FileStreamPtr ResourceManager::createFile(const std::string& fileName) const
|
|||
{
|
||||
PHYSFS_File* file = PHYSFS_openWrite(fileName.c_str());
|
||||
if (!file)
|
||||
throw Exception("failed to create file '%s': %s", fileName, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
||||
throw Exception("failed to create file '{}': {}", fileName, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
||||
return { std::make_shared<FileStream>(fileName, file, true) };
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -658,12 +658,24 @@ void WebsocketSession::on_request_sent(const std::error_code& ec, size_t /*bytes
|
|||
websocket_accept = header.c_str() + pos + sizeof("Sec-WebSocket-Accept: ") - 1;
|
||||
}*/
|
||||
|
||||
async_read(m_ssl, m_response,
|
||||
asio::transfer_at_least(1),
|
||||
[sft = shared_from_this()](
|
||||
const std::error_code& ec, const size_t bytes) {
|
||||
sft->on_read(ec, bytes);
|
||||
});
|
||||
if (!m_closed && m_ssl.lowest_layer().is_open()) {
|
||||
m_handshake_complete = true;
|
||||
m_callback(WebsocketCallbackType::OPEN, "code::websocket_open");
|
||||
m_timer.cancel();
|
||||
|
||||
while (!m_pending_messages.empty() && !m_closed) {
|
||||
auto msg = m_pending_messages.front();
|
||||
m_pending_messages.pop();
|
||||
send(msg.first, msg.second);
|
||||
}
|
||||
|
||||
async_read(m_ssl, m_response,
|
||||
asio::transfer_at_least(1),
|
||||
[sft = shared_from_this()](
|
||||
const std::error_code& ec, const size_t bytes) {
|
||||
sft->on_read(ec, bytes);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
async_read_until(
|
||||
|
|
@ -685,16 +697,26 @@ void WebsocketSession::on_request_sent(const std::error_code& ec, size_t /*bytes
|
|||
websocket_accept = header.c_str() + pos + sizeof("Sec-WebSocket-Accept: ") - 1;
|
||||
}*/
|
||||
|
||||
async_read(m_socket, m_response,
|
||||
asio::transfer_at_least(1),
|
||||
[sft = shared_from_this()](
|
||||
const std::error_code& ec, const size_t bytes) {
|
||||
sft->on_read(ec, bytes);
|
||||
});
|
||||
if (!m_closed && m_socket.is_open()) {
|
||||
m_handshake_complete = true;
|
||||
m_callback(WebsocketCallbackType::OPEN, "code::websocket_open");
|
||||
m_timer.cancel();
|
||||
|
||||
while (!m_pending_messages.empty() && !m_closed) {
|
||||
auto msg = m_pending_messages.front();
|
||||
m_pending_messages.pop();
|
||||
send(msg.first, msg.second);
|
||||
}
|
||||
|
||||
async_read(m_socket, m_response,
|
||||
asio::transfer_at_least(1),
|
||||
[sft = shared_from_this()](
|
||||
const std::error_code& ec, const size_t bytes) {
|
||||
sft->on_read(ec, bytes);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
m_callback(WebsocketCallbackType::OPEN, "code::websocket_open");
|
||||
m_timer.cancel();
|
||||
}
|
||||
|
||||
void WebsocketSession::on_write(const std::error_code& ec, size_t /*bytes_transferred*/)
|
||||
|
|
@ -734,55 +756,110 @@ void WebsocketSession::on_write(const std::error_code& ec, size_t /*bytes_transf
|
|||
void WebsocketSession::on_read(const std::error_code& ec, const size_t bytes_transferred)
|
||||
{
|
||||
if (ec && ec != asio::error::eof) {
|
||||
onError("WebsocketSession unable to on_read " + m_url + ": " + ec.message());
|
||||
if (ec != asio::error::operation_aborted && ec != asio::error::not_connected) {
|
||||
onError("WebsocketSession unable to on_read " + m_url + ": " + ec.message());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_closed) {
|
||||
if (m_closed || !m_handshake_complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
stdext::millisleep(100);
|
||||
if (bytes_transferred > 0) {
|
||||
m_response.prepare(bytes_transferred);
|
||||
const auto& data = m_response.data();
|
||||
std::string response = { buffers_begin(data), buffers_end(data) };
|
||||
const uint8_t fin_code = response.at(0);
|
||||
// size_t length = (response.at(1) & 127);
|
||||
response.erase(0, 1);
|
||||
|
||||
// close connection
|
||||
if (fin_code == 0x88) {
|
||||
close();
|
||||
// to ping
|
||||
} else if (fin_code == 0x89) {
|
||||
send("", fin_code + 1);
|
||||
// to pong
|
||||
} else if (fin_code == 0x8A) {
|
||||
// fragmented message
|
||||
} else if (fin_code == 0x80) {
|
||||
m_callback(WebsocketCallbackType::MESSAGE, response);
|
||||
} else {
|
||||
m_callback(WebsocketCallbackType::MESSAGE, response);
|
||||
}
|
||||
|
||||
std::string received_data(buffers_begin(data), buffers_begin(data) + bytes_transferred);
|
||||
m_read_buffer.append(received_data);
|
||||
m_response.consume(bytes_transferred);
|
||||
}
|
||||
|
||||
if (instance_uri.port == "443") {
|
||||
async_read(m_ssl, m_response,
|
||||
asio::transfer_at_least(1),
|
||||
[sft = shared_from_this()](
|
||||
const std::error_code& ec, const size_t bytes) {
|
||||
sft->on_read(ec, bytes);
|
||||
});
|
||||
} else {
|
||||
async_read(m_socket, m_response,
|
||||
asio::transfer_at_least(1),
|
||||
[sft = shared_from_this()](
|
||||
const std::error_code& ec, const size_t bytes) {
|
||||
sft->on_read(ec, bytes);
|
||||
});
|
||||
while (m_read_buffer.size() >= 2) {
|
||||
size_t frame_start = 0;
|
||||
|
||||
const uint8_t fin_opcode = static_cast<uint8_t>(m_read_buffer[frame_start]);
|
||||
const uint8_t mask_len = static_cast<uint8_t>(m_read_buffer[frame_start + 1]);
|
||||
|
||||
const uint8_t opcode = fin_opcode & 0x0F;
|
||||
const bool masked = (mask_len & 0x80) != 0;
|
||||
uint64_t payload_length = mask_len & 0x7F;
|
||||
|
||||
size_t header_size = 2;
|
||||
|
||||
if (payload_length == 126) {
|
||||
if (m_read_buffer.size() < frame_start + 4) break;
|
||||
payload_length = (static_cast<uint8_t>(m_read_buffer[frame_start + 2]) << 8) |
|
||||
static_cast<uint8_t>(m_read_buffer[frame_start + 3]);
|
||||
header_size += 2;
|
||||
} else if (payload_length == 127) {
|
||||
if (m_read_buffer.size() < frame_start + 10) break;
|
||||
payload_length = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
payload_length = (payload_length << 8) | static_cast<uint8_t>(m_read_buffer[frame_start + 2 + i]);
|
||||
}
|
||||
header_size += 8;
|
||||
}
|
||||
|
||||
if (masked) {
|
||||
header_size += 4;
|
||||
}
|
||||
if (m_read_buffer.size() < frame_start + header_size + payload_length) {
|
||||
break;
|
||||
}
|
||||
std::string payload;
|
||||
if (payload_length > 0) {
|
||||
size_t payload_start = frame_start + header_size;
|
||||
|
||||
if (masked) {
|
||||
std::array<uint8_t, 4> mask;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
mask[i] = static_cast<uint8_t>(m_read_buffer[frame_start + header_size - 4 + i]);
|
||||
}
|
||||
|
||||
payload.reserve(payload_length);
|
||||
for (uint64_t i = 0; i < payload_length; i++) {
|
||||
uint8_t masked_byte = static_cast<uint8_t>(m_read_buffer[payload_start + i]);
|
||||
payload.push_back(static_cast<char>(masked_byte ^ mask[i % 4]));
|
||||
}
|
||||
} else {
|
||||
payload = m_read_buffer.substr(payload_start, payload_length);
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode == 0x8) {
|
||||
close();
|
||||
return;
|
||||
} else if (opcode == 0x9) {
|
||||
send(payload, 0x8A);
|
||||
} else if (opcode == 0xA) {
|
||||
//
|
||||
} else if (opcode == 0x1 || opcode == 0x2 || opcode == 0x0) {
|
||||
if (!payload.empty()) {
|
||||
m_callback(WebsocketCallbackType::MESSAGE, payload);
|
||||
}
|
||||
}
|
||||
m_read_buffer.erase(0, frame_start + header_size + payload_length);
|
||||
}
|
||||
}
|
||||
if (!m_closed) {
|
||||
if (instance_uri.port == "443") {
|
||||
if (m_ssl.lowest_layer().is_open()) {
|
||||
async_read(m_ssl, m_response,
|
||||
asio::transfer_at_least(1),
|
||||
[sft = shared_from_this()](
|
||||
const std::error_code& ec, const size_t bytes) {
|
||||
sft->on_read(ec, bytes);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (m_socket.is_open()) {
|
||||
async_read(m_socket, m_response,
|
||||
asio::transfer_at_least(1),
|
||||
[sft = shared_from_this()](
|
||||
const std::error_code& ec, const size_t bytes) {
|
||||
sft->on_read(ec, bytes);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -815,6 +892,24 @@ void WebsocketSession::onTimeout(const std::error_code& ec)
|
|||
|
||||
void WebsocketSession::send(const std::string& data, const uint8_t ws_opcode)
|
||||
{
|
||||
if (!m_handshake_complete || m_closed) {
|
||||
if (!m_closed) {
|
||||
m_pending_messages.emplace(data, ws_opcode);
|
||||
}
|
||||
return;
|
||||
}
|
||||
bool socket_open = false;
|
||||
if (instance_uri.port == "443") {
|
||||
socket_open = m_ssl.lowest_layer().is_open();
|
||||
} else {
|
||||
socket_open = m_socket.is_open();
|
||||
}
|
||||
|
||||
if (!socket_open) {
|
||||
onError("WebsocketSession attempted to send on closed socket " + m_url);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ws_frame;
|
||||
std::array<unsigned char, 4> mask;
|
||||
std::uniform_int_distribution<unsigned short> dist(0, 255);
|
||||
|
|
@ -942,33 +1037,59 @@ void WebsocketSession::send(const std::string& data, const uint8_t ws_opcode)
|
|||
void WebsocketSession::close()
|
||||
{
|
||||
if (!m_closed) {
|
||||
/*
|
||||
0x88 in binary format is 1000 1000
|
||||
1... .... = Fin: True
|
||||
.000 .... = Reserved: 0x0
|
||||
.... 1000 = Opcode: Connection Close (8)
|
||||
*/
|
||||
send("", 0x88);
|
||||
m_closed = true;
|
||||
bool socket_open = false;
|
||||
if (instance_uri.port == "443") {
|
||||
m_ssl.lowest_layer().close();
|
||||
m_ssl.async_shutdown(
|
||||
[sft = shared_from_this()](
|
||||
std::error_code ec) {
|
||||
if (ec == asio::error::eof) {
|
||||
ec = {};
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
sft->onError("shutdown " + sft->m_url + ": " + ec.message());
|
||||
}
|
||||
});
|
||||
socket_open = m_ssl.lowest_layer().is_open();
|
||||
} else {
|
||||
m_socket.close();
|
||||
std::error_code ec;
|
||||
m_socket.shutdown(asio::ip::tcp::socket::shutdown_both, ec);
|
||||
// not_connected happens sometimes so don't bother reporting it.
|
||||
if (ec && ec != asio::error::not_connected) {
|
||||
onError("shutdown " + m_url + ": " + ec.message());
|
||||
socket_open = m_socket.is_open();
|
||||
}
|
||||
|
||||
if (socket_open && m_handshake_complete) {
|
||||
/*
|
||||
0x88 in binary format is 1000 1000
|
||||
1... .... = Fin: True
|
||||
.000 .... = Reserved: 0x0
|
||||
.... 1000 = Opcode: Connection Close (8)
|
||||
*/
|
||||
try {
|
||||
send("", 0x88);
|
||||
} catch (...) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
if (instance_uri.port == "443") {
|
||||
try {
|
||||
if (m_ssl.lowest_layer().is_open()) {
|
||||
std::error_code ec;
|
||||
m_ssl.lowest_layer().close(ec);
|
||||
m_ssl.async_shutdown(
|
||||
[sft = shared_from_this()](
|
||||
std::error_code ec) {
|
||||
if (ec == asio::error::eof) {
|
||||
ec = {};
|
||||
}
|
||||
if (ec && ec != asio::error::not_connected && ec != asio::error::operation_aborted) {
|
||||
sft->onError("shutdown " + sft->m_url + ": " + ec.message());
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (...) {
|
||||
//
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (m_socket.is_open()) {
|
||||
std::error_code ec;
|
||||
m_socket.shutdown(asio::ip::tcp::socket::shutdown_both, ec);
|
||||
m_socket.close(ec);
|
||||
if (ec && ec != asio::error::not_connected && ec != asio::error::operation_aborted) {
|
||||
onError("shutdown " + m_url + ": " + ec.message());
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,6 +157,8 @@ private:
|
|||
asio::io_service& m_service;
|
||||
std::string m_url;
|
||||
std::string m_agent;
|
||||
std::string m_read_buffer;
|
||||
std::queue<std::pair<std::string, uint8_t>> m_pending_messages;
|
||||
bool m_enable_time_out_on_read_write;
|
||||
int m_timeout;
|
||||
HttpResult_ptr m_result;
|
||||
|
|
@ -165,6 +167,7 @@ private:
|
|||
asio::ip::tcp::socket m_socket;
|
||||
asio::ip::tcp::resolver m_resolver;
|
||||
bool m_closed{ false };
|
||||
bool m_handshake_complete{ false };
|
||||
ParsedURI instance_uri;
|
||||
|
||||
asio::ssl::context m_context{ asio::ssl::context::tlsv12_client };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue