fix: Websocket Vbot 4.8 (#1191)

This commit is contained in:
InnerCircleTFS 2025-06-11 18:57:23 -04:00 committed by GitHub
parent 754fa407f2
commit d2a40c8451
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 211 additions and 82 deletions

View file

@ -24,7 +24,7 @@ local luaFiles = {
"extras",
"cavebot",
"playerlist",
--"BotServer", --currently 02/03/24 No websocket
"BotServer",
"alarms",
"Conditions",
"Equipper",

View file

@ -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 };

View file

@ -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);

View file

@ -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) };
}

View file

@ -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 (...) {
//
}
}
}

View file

@ -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 };