feat: add config

This commit is contained in:
2026-04-11 10:41:45 -03:00
parent 83ac8615ba
commit 0300ced79c
2 changed files with 156 additions and 82 deletions
+146 -82
View File
@@ -39,6 +39,7 @@ void Spoofer::init(Proxy* proxy)
{ {
registerListeners(proxy); registerListeners(proxy);
loadData(); loadData();
loadConfig();
} }
void Spoofer::registerListeners(Proxy* proxy) void Spoofer::registerListeners(Proxy* proxy)
@@ -90,6 +91,37 @@ void Spoofer::loadData()
LOADDATA("perks.json", Perks, _camperPerkIds, _slasherPerkIds, "Perks won't be added"); LOADDATA("perks.json", Perks, _camperPerkIds, _slasherPerkIds, "Perks won't be added");
} }
void Spoofer::loadConfig()
{
std::string configPath = utils::getExePath() + "config.json";
std::ifstream configFile(configPath);
if (configFile.is_open())
{
try
{
json configJson = json::parse(configFile);
_config.spoofCharacterOwnership = configJson.value("spoofCharacterOwnership", false);
_config.spoofInventory = configJson.value("spoofInventory", true);
_config.spoofCustomization = configJson.value("spoofCustomization", true);
Log::info("Loaded config: Ownership={}, Inventory={}, Customization={}", _config.spoofCharacterOwnership,
_config.spoofInventory, _config.spoofCustomization);
}
catch (...)
{
Log::error("Failed to parse config.json, using defaults");
}
}
else
{
Log::info("config.json not found, using default settings");
json defaultConfig = {
{"spoofCharacterOwnership", true}, {"spoofInventory", true}, {"spoofCustomization", true}};
std::ofstream out(configPath);
out << defaultConfig.dump(4);
}
}
/* /*
data parsing data parsing
*/ */
@@ -241,81 +273,94 @@ void Spoofer::modifyCharacterData(json& js)
} }
std::string name = js["characterName"]; std::string name = js["characterName"];
bool slasher = isSlasher(js["characterName"]);
bool needsSpoofing = false; if (_config.spoofCharacterOwnership)
if (js.value("isEntitled", true) == false)
{ {
_unownedCharacters.insert(name); bool needsSpoofing = false;
js["isEntitled"] = true;
js["purchaseInfo"] = {{"quantity", 1}, if (js.value("isEntitled", false) == false)
{"origin", "PlayerInventory"}, {
{"reason", "Item(s) added via Purchase"}, _unownedCharacters.insert(name);
{"lastUpdateAt", std::time(nullptr)}, js["isEntitled"] = true;
{"objectId", name}}; js["purchaseInfo"] = {{"quantity", 1},
needsSpoofing = true; {"origin", "PlayerInventory"},
{"reason", "Item(s) added via Purchase"},
{"lastUpdateAt", std::time(nullptr)},
{"objectId", name}};
needsSpoofing = true;
}
else if (_unownedCharacters.contains(name))
needsSpoofing = true;
/*
modifications for unowned characters (spoof level and fake bloodweb)
*/
if (needsSpoofing)
{
if (js.contains("bloodWebLevel") && js["bloodWebLevel"].is_number() && js["bloodWebLevel"] <= 15)
if (!js.contains("prestigeLevel") || (js["prestigeLevel"].is_number() && js["prestigeLevel"] <= 0))
js["bloodWebLevel"] = 16;
if (js.contains("bloodWebData")) generateBloodweb(js["bloodWebData"]);
}
} }
else if (_unownedCharacters.contains(name)) else
needsSpoofing = true;
/*
modifications for unowned characters (spoof level and fake bloodweb)
*/
if (needsSpoofing)
{ {
if (js.contains("bloodWebLevel") && js["bloodWebLevel"].is_number() && js["bloodWebLevel"] <= 15) if (js.value("isEntitled", false) == false) return;
if (!js.contains("prestigeLevel") || (js["prestigeLevel"].is_number() && js["prestigeLevel"] <= 0))
js["bloodWebLevel"] = 16;
if (js.contains("bloodWebData")) generateBloodweb(js["bloodWebData"]);
} }
/* /*
item spoofing item spoofing
*/ */
if (js.contains("characterItems") && js["characterItems"].is_array()) if (_config.spoofInventory)
{ {
std::unordered_set<std::string> existingItemIds; bool slasher = isSlasher(js["characterName"]);
std::unordered_set<std::string> stackableIds; if (js.contains("characterItems") && js["characterItems"].is_array())
stackableIds.insert(_camperItemIds.begin(), _camperItemIds.end());
stackableIds.insert(_camperOfferingIds.begin(), _camperOfferingIds.end());
stackableIds.insert(_camperAddonIds.begin(), _camperAddonIds.end());
stackableIds.insert(_slasherAddonIds.begin(), _slasherAddonIds.end());
stackableIds.insert(_slasherOfferingIds.begin(), _slasherOfferingIds.end());
for (auto& item : js["characterItems"])
{ {
/* std::unordered_set<std::string> existingItemIds;
std::unordered_set<std::string> stackableIds;
stackableIds.insert(_camperItemIds.begin(), _camperItemIds.end());
stackableIds.insert(_camperOfferingIds.begin(), _camperOfferingIds.end());
stackableIds.insert(_camperAddonIds.begin(), _camperAddonIds.end());
stackableIds.insert(_slasherAddonIds.begin(), _slasherAddonIds.end());
stackableIds.insert(_slasherOfferingIds.begin(), _slasherOfferingIds.end());
for (auto& item : js["characterItems"])
{
/*
set existing items to rnd number set existing items to rnd number
*/ */
if (item.contains("itemId") && item["itemId"].is_string()) if (item.contains("itemId") && item["itemId"].is_string())
{ {
std::string itemId = item["itemId"]; std::string itemId = item["itemId"];
existingItemIds.insert(itemId); existingItemIds.insert(itemId);
if (stackableIds.contains(itemId)) item["quantity"] = getRandomQuantity(); if (stackableIds.contains(itemId)) item["quantity"] = getRandomQuantity();
}
} }
}
auto appendItems = [&](const std::unordered_set<std::string>& idList, bool isPerk) { auto appendItems = [&](const std::unordered_set<std::string>& idList, bool isPerk) {
for (const auto& itemId : idList) for (const auto& itemId : idList)
if (existingItemIds.find(itemId) == existingItemIds.end()) if (existingItemIds.find(itemId) == existingItemIds.end())
js["characterItems"].push_back( js["characterItems"].push_back(
{{"itemId", itemId}, {"quantity", isPerk ? 3 : getRandomQuantity()}}); {{"itemId", itemId}, {"quantity", isPerk ? 3 : getRandomQuantity()}});
}; };
if (!slasher) if (!slasher)
{ {
appendItems(_camperItemIds, false); appendItems(_camperItemIds, false);
appendItems(_camperAddonIds, false); appendItems(_camperAddonIds, false);
appendItems(_camperOfferingIds, false); appendItems(_camperOfferingIds, false);
appendItems(_camperPerkIds, true); appendItems(_camperPerkIds, true);
} }
else else
{ {
appendItems(_slasherAddonIds, false); appendItems(_slasherAddonIds, false);
appendItems(_slasherOfferingIds, false); appendItems(_slasherOfferingIds, false);
appendItems(_slasherPerkIds, true); appendItems(_slasherPerkIds, true);
}
} }
} }
@@ -380,8 +425,24 @@ void Spoofer::onInventoryAll(std::string& body)
int quantity; int quantity;
}; };
std::vector<Category> categories = {{_camperPerkIds, 3}, {_slasherPerkIds, 3}, {_camperOfferingIds, -1}, std::vector<Category> categories;
{_slasherOfferingIds, -1}, {_catalogOutfitIds, 1}, {_catalogItemIds, 1}}; if (_config.spoofInventory)
{
categories.push_back({_camperPerkIds, 3});
categories.push_back({_slasherPerkIds, 3});
categories.push_back({_camperOfferingIds, -1});
categories.push_back({_slasherOfferingIds, -1});
categories.push_back({_camperItemIds, -1});
categories.push_back({_camperAddonIds, -1});
categories.push_back({_slasherAddonIds, -1});
}
if (_config.spoofCustomization)
{
categories.push_back({_catalogOutfitIds, 1});
categories.push_back({_catalogItemIds, 1});
}
for (auto& item : itemsArr) for (auto& item : itemsArr)
{ {
@@ -481,36 +542,39 @@ void Spoofer::onBloodweb(std::string& body, std::string& respHeaders)
json doc = json::parse(body, nullptr, false); json doc = json::parse(body, nullptr, false);
if (doc.is_discarded()) return Log::error("JSON parse error for bloodweb response"); if (doc.is_discarded()) return Log::error("JSON parse error for bloodweb response");
if (body.find("NotAllowedException") != std::string::npos && body.find("not owned") != std::string::npos) if (_config.spoofCharacterOwnership)
{ {
Log::info("Spoofing bloodweb error for unowned character"); if (body.find("NotAllowedException") != std::string::npos && body.find("not owned") != std::string::npos)
json mock; {
mock["bloodWebLevelChanged"] = false; Log::info("Spoofing bloodweb error for unowned character");
mock["updatedWallets"] = json::array(); json mock;
mock["bloodWebLevel"] = 16; mock["bloodWebLevelChanged"] = false;
mock["prestigeLevel"] = 0; mock["updatedWallets"] = json::array();
mock["bloodWebData"] = json::object(); mock["bloodWebLevel"] = 16;
mock["characterItems"] = json::array(); mock["prestigeLevel"] = 0;
mock["characterName"] = this->_lastBloodWebChar; mock["bloodWebData"] = json::object();
mock["isEntitled"] = true; mock["characterItems"] = json::array();
mock["purchaseInfo"] = {{"quantity", 1}, mock["characterName"] = this->_lastBloodWebChar;
{"origin", "PlayerInventory"}, mock["isEntitled"] = true;
{"reason", "Item(s) added via Purchase"}, mock["purchaseInfo"] = {{"quantity", 1},
{"lastUpdateAt", std::time(nullptr)}, {"origin", "PlayerInventory"},
{"objectId", this->_lastBloodWebChar}}; {"reason", "Item(s) added via Purchase"},
{"lastUpdateAt", std::time(nullptr)},
{"objectId", this->_lastBloodWebChar}};
_unownedCharacters.insert(this->_lastBloodWebChar); // probably not needed but just in case _unownedCharacters.insert(this->_lastBloodWebChar); // probably not needed but just in case
modifyCharacterData(mock); modifyCharacterData(mock);
std::regex statusRegex(R"(HTTP\/\d\.\d\s+403)"); std::regex statusRegex(R"(HTTP\/\d\.\d\s+403)");
respHeaders = std::regex_replace(respHeaders, statusRegex, "HTTP/1.1 200"); respHeaders = std::regex_replace(respHeaders, statusRegex, "HTTP/1.1 200");
body = mock.dump(); body = mock.dump();
#ifdef _DEBUG #ifdef _DEBUG
Log::verbose("Spoofed bloodweb request for unowned character."); Log::verbose("Spoofed bloodweb request for unowned character.");
#endif #endif
return; return;
}
} }
modifyCharacterData(doc); modifyCharacterData(doc);
+10
View File
@@ -9,6 +9,13 @@
#include <nlohmann/json_fwd.hpp> #include <nlohmann/json_fwd.hpp>
struct SpooferConfig
{
bool spoofCharacterOwnership = false;
bool spoofInventory = false;
bool spoofCustomization = false;
};
class Spoofer class Spoofer
{ {
public: public:
@@ -17,6 +24,7 @@ class Spoofer
private: private:
void registerListeners(Proxy* proxy); void registerListeners(Proxy* proxy);
void loadData(); void loadData();
void loadConfig();
bool parseCatalog(std::string data); bool parseCatalog(std::string data);
bool parseStackable(std::string data, std::unordered_set<std::string>& camperSet, bool parseStackable(std::string data, std::unordered_set<std::string>& camperSet,
@@ -37,6 +45,8 @@ class Spoofer
void serverResponseHandler(const std::string& url, std::string& body, std::string& respHeaders); void serverResponseHandler(const std::string& url, std::string& body, std::string& respHeaders);
void clientRequestHandler(std::string& url, const std::string& body, std::string& reqHeaders); void clientRequestHandler(std::string& url, const std::string& body, std::string& reqHeaders);
SpooferConfig _config;
std::unordered_set<std::string> _camperItemIds; std::unordered_set<std::string> _camperItemIds;
std::unordered_set<std::string> _slasherPowerIds; std::unordered_set<std::string> _slasherPowerIds;