feat: add websocket server

This commit is contained in:
2026-06-19 08:33:04 -03:00
parent 612b9429a2
commit 47e0ea18e8
2 changed files with 183 additions and 0 deletions
+126
View File
@@ -23,11 +23,16 @@ Spoofer::Spoofer()
_log->info("Spoofer init");
loadConfig();
_log->info("Starting WebSocket server");
initServer();
}
Spoofer::~Spoofer()
{
_log->info("Stopping WebSocket server");
stopServer();
delete _log;
}
@@ -113,6 +118,127 @@ void Spoofer::saveConfig()
auto errCtx = glz::write_file_json(conf, configPath, buffer);
if (errCtx.ec != glz::error_code::none) _log->error("Failed to save config to {}", configPath);
}
/*
websocket
*/
void Spoofer::initServer()
{
if (_wsServer) PANIC("Attempted to init websocket server from invalid state");
_wsServer = new ix::WebSocketServer(WS_PORT, WS_ADDR);
_wsServer->setOnClientMessageCallback([this](std::shared_ptr<ix::ConnectionState> connectionState,
ix::WebSocket& webSocket,
const std::unique_ptr<ix::WebSocketMessage>& msg) {
this->wsMessageCallback(connectionState, webSocket, msg);
});
std::pair<bool, std::string> res = _wsServer->listen();
if (!res.first) PANIC("Websocket server failed to start");
_log->verbose("WebSocket server running @ {}:{}", WS_ADDR, WS_PORT);
_wsServer->start();
}
void Spoofer::stopServer()
{
if (_wsServer)
{
_wsServer->stop();
delete _wsServer;
_wsServer = nullptr;
}
}
void Spoofer::wsMessageCallback(std::shared_ptr<ix::ConnectionState> /*connectionState*/, ix::WebSocket& webSocket,
const std::unique_ptr<ix::WebSocketMessage>& msg)
{
std::lock_guard<std::mutex> lock(_mutex);
switch (msg->type)
{
case ix::WebSocketMessageType::Open:
{
_log->verbose("Websocket connection open, URI: {}", msg->openInfo.uri);
WSMessages::Init initMsg;
initMsg.profile.camperItems = _camperItems;
initMsg.profile.camperAddons = _camperAddons;
initMsg.profile.slasherAddons = _slasherAddons;
initMsg.profile.camperOfferings = _camperOfferings;
initMsg.profile.slasherOfferings = _slasherOfferings;
initMsg.profile.globalOfferings = _globalOfferings;
initMsg.profile.camperPerks = _camperPerks;
initMsg.profile.slasherPerks = _slasherPerks;
initMsg.profile.catalogItemIds = _catalogItemIds;
initMsg.profile.dlcListGRDK = _dlcListGRDK;
initMsg.profile.dlcListEGS = _dlcListEGS;
initMsg.profile.dlcListSteam = _dlcListSteam;
initMsg.profile.unlockedCharacters = _unlockedCharacters;
initMsg.toggles.spoofItems = _spoofItems;
initMsg.toggles.spoofPerks = _spoofPerks;
initMsg.toggles.spoofCatalog = _spoofCatalog;
initMsg.toggles.spoofDLCs = _spoofDLCs;
std::string out;
auto errCtx = glz::write_json(initMsg, out);
if (errCtx && errCtx.ec != glz::error_code::none)
_log->error("Error occurred while writing init msg");
else
webSocket.send(out);
break;
}
case ix::WebSocketMessageType::Close:
_log->verbose("Websocket connection close");
break;
case ix::WebSocketMessageType::Message:
{
WSMessages::Request req;
auto err = glz::read_json(req, msg->str);
if (err.ec != glz::error_code::none)
{
_log->error("Failed to parse websocket message");
break;
}
switch (req.action)
{
case WSMessages::SYNC_STATE:
{
_camperItems = std::move(req.profile.camperItems);
_camperAddons = std::move(req.profile.camperAddons);
_slasherAddons = std::move(req.profile.slasherAddons);
_camperOfferings = std::move(req.profile.camperOfferings);
_slasherOfferings = std::move(req.profile.slasherOfferings);
_globalOfferings = std::move(req.profile.globalOfferings);
_camperPerks = std::move(req.profile.camperPerks);
_slasherPerks = std::move(req.profile.slasherPerks);
_catalogItemIds = std::move(req.profile.catalogItemIds);
_dlcListGRDK = std::move(req.profile.dlcListGRDK);
_dlcListEGS = std::move(req.profile.dlcListEGS);
_dlcListSteam = std::move(req.profile.dlcListSteam);
_unlockedCharacters = std::move(req.profile.unlockedCharacters);
saveConfig();
_log->verbose("Profile state synchronized.");
break;
}
case WSMessages::SYNC_TOGGLES:
{
_spoofItems = req.toggles.spoofItems;
_spoofPerks = req.toggles.spoofPerks;
_spoofCatalog = req.toggles.spoofCatalog;
_spoofDLCs = req.toggles.spoofDLCs;
saveConfig();
_log->verbose("Toggles synchronized.");
break;
}
default:
_log->warning("Unknown action: {}", static_cast<int>(req.action));
break;
}
break;
}
default:
break;
}
}
/*
proxy handlers
*/
+57
View File
@@ -9,6 +9,20 @@
#include <mutex>
#define WS_ADDR "0.0.0.0"
#define WS_PORT 4444
class TinyMITMProxy;
namespace ix
{
class WebSocket;
class WebSocketServer;
struct WebSocketMessage;
class ConnectionState;
} // namespace ix
namespace seallib
{
class Logger;
@@ -31,6 +45,35 @@ struct SpooferConfig
std::unordered_set<std::string> unlockedCharacters;
};
namespace WSMessages
{
enum Action : int
{
INIT_CONFIG = 0,
SYNC_STATE = 1,
SYNC_TOGGLES = 2
};
struct Toggles
{
bool spoofItems = false;
bool spoofPerks = false;
bool spoofCatalog = false;
bool spoofDLCs = false;
};
struct Init
{
Action action = INIT_CONFIG;
SpooferConfig profile;
Toggles toggles;
};
struct Request
{
Action action;
SpooferConfig profile{};
Toggles toggles{};
};
} // namespace WSMessages
class Spoofer
{
public:
@@ -45,6 +88,15 @@ class Spoofer
*/
void loadConfig();
void saveConfig();
/*
websocket server stuff
*/
void initServer();
void stopServer();
void wsMessageCallback(std::shared_ptr<ix::ConnectionState> connectionState, ix::WebSocket& webSocket,
const std::unique_ptr<ix::WebSocketMessage>& msg);
/*
proxy handlers
*/
@@ -77,6 +129,11 @@ class Spoofer
std::unordered_set<std::string> _dlcListSteam;
std::unordered_set<std::string> _unlockedCharacters;
/*
internal variables
*/
ix::WebSocketServer* _wsServer = nullptr;
seallib::Logger* _log = nullptr;
std::mutex _mutex;
};