feat: add config
This commit is contained in:
+146
-82
@@ -39,6 +39,7 @@ void Spoofer::init(Proxy* proxy)
|
||||
{
|
||||
registerListeners(proxy);
|
||||
loadData();
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
void Spoofer::registerListeners(Proxy* proxy)
|
||||
@@ -90,6 +91,37 @@ void Spoofer::loadData()
|
||||
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
|
||||
*/
|
||||
@@ -241,81 +273,94 @@ void Spoofer::modifyCharacterData(json& js)
|
||||
}
|
||||
|
||||
std::string name = js["characterName"];
|
||||
bool slasher = isSlasher(js["characterName"]);
|
||||
|
||||
bool needsSpoofing = false;
|
||||
if (js.value("isEntitled", true) == false)
|
||||
if (_config.spoofCharacterOwnership)
|
||||
{
|
||||
_unownedCharacters.insert(name);
|
||||
js["isEntitled"] = true;
|
||||
js["purchaseInfo"] = {{"quantity", 1},
|
||||
{"origin", "PlayerInventory"},
|
||||
{"reason", "Item(s) added via Purchase"},
|
||||
{"lastUpdateAt", std::time(nullptr)},
|
||||
{"objectId", name}};
|
||||
needsSpoofing = true;
|
||||
bool needsSpoofing = false;
|
||||
|
||||
if (js.value("isEntitled", false) == false)
|
||||
{
|
||||
_unownedCharacters.insert(name);
|
||||
js["isEntitled"] = true;
|
||||
js["purchaseInfo"] = {{"quantity", 1},
|
||||
{"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))
|
||||
needsSpoofing = true;
|
||||
else
|
||||
|
||||
/*
|
||||
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"]);
|
||||
if (js.value("isEntitled", false) == false) return;
|
||||
}
|
||||
|
||||
/*
|
||||
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;
|
||||
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"])
|
||||
if (js.contains("characterItems") && js["characterItems"].is_array())
|
||||
{
|
||||
/*
|
||||
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
|
||||
*/
|
||||
if (item.contains("itemId") && item["itemId"].is_string())
|
||||
{
|
||||
std::string itemId = item["itemId"];
|
||||
existingItemIds.insert(itemId);
|
||||
if (stackableIds.contains(itemId)) item["quantity"] = getRandomQuantity();
|
||||
if (item.contains("itemId") && item["itemId"].is_string())
|
||||
{
|
||||
std::string itemId = item["itemId"];
|
||||
existingItemIds.insert(itemId);
|
||||
if (stackableIds.contains(itemId)) item["quantity"] = getRandomQuantity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto appendItems = [&](const std::unordered_set<std::string>& idList, bool isPerk) {
|
||||
for (const auto& itemId : idList)
|
||||
if (existingItemIds.find(itemId) == existingItemIds.end())
|
||||
js["characterItems"].push_back(
|
||||
{{"itemId", itemId}, {"quantity", isPerk ? 3 : getRandomQuantity()}});
|
||||
};
|
||||
auto appendItems = [&](const std::unordered_set<std::string>& idList, bool isPerk) {
|
||||
for (const auto& itemId : idList)
|
||||
if (existingItemIds.find(itemId) == existingItemIds.end())
|
||||
js["characterItems"].push_back(
|
||||
{{"itemId", itemId}, {"quantity", isPerk ? 3 : getRandomQuantity()}});
|
||||
};
|
||||
|
||||
if (!slasher)
|
||||
{
|
||||
appendItems(_camperItemIds, false);
|
||||
appendItems(_camperAddonIds, false);
|
||||
appendItems(_camperOfferingIds, false);
|
||||
appendItems(_camperPerkIds, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
appendItems(_slasherAddonIds, false);
|
||||
appendItems(_slasherOfferingIds, false);
|
||||
appendItems(_slasherPerkIds, true);
|
||||
if (!slasher)
|
||||
{
|
||||
appendItems(_camperItemIds, false);
|
||||
appendItems(_camperAddonIds, false);
|
||||
appendItems(_camperOfferingIds, false);
|
||||
appendItems(_camperPerkIds, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
appendItems(_slasherAddonIds, false);
|
||||
appendItems(_slasherOfferingIds, false);
|
||||
appendItems(_slasherPerkIds, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,8 +425,24 @@ void Spoofer::onInventoryAll(std::string& body)
|
||||
int quantity;
|
||||
};
|
||||
|
||||
std::vector<Category> categories = {{_camperPerkIds, 3}, {_slasherPerkIds, 3}, {_camperOfferingIds, -1},
|
||||
{_slasherOfferingIds, -1}, {_catalogOutfitIds, 1}, {_catalogItemIds, 1}};
|
||||
std::vector<Category> categories;
|
||||
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)
|
||||
{
|
||||
@@ -481,36 +542,39 @@ void Spoofer::onBloodweb(std::string& body, std::string& respHeaders)
|
||||
json doc = json::parse(body, nullptr, false);
|
||||
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");
|
||||
json mock;
|
||||
mock["bloodWebLevelChanged"] = false;
|
||||
mock["updatedWallets"] = json::array();
|
||||
mock["bloodWebLevel"] = 16;
|
||||
mock["prestigeLevel"] = 0;
|
||||
mock["bloodWebData"] = json::object();
|
||||
mock["characterItems"] = json::array();
|
||||
mock["characterName"] = this->_lastBloodWebChar;
|
||||
mock["isEntitled"] = true;
|
||||
mock["purchaseInfo"] = {{"quantity", 1},
|
||||
{"origin", "PlayerInventory"},
|
||||
{"reason", "Item(s) added via Purchase"},
|
||||
{"lastUpdateAt", std::time(nullptr)},
|
||||
{"objectId", this->_lastBloodWebChar}};
|
||||
if (body.find("NotAllowedException") != std::string::npos && body.find("not owned") != std::string::npos)
|
||||
{
|
||||
Log::info("Spoofing bloodweb error for unowned character");
|
||||
json mock;
|
||||
mock["bloodWebLevelChanged"] = false;
|
||||
mock["updatedWallets"] = json::array();
|
||||
mock["bloodWebLevel"] = 16;
|
||||
mock["prestigeLevel"] = 0;
|
||||
mock["bloodWebData"] = json::object();
|
||||
mock["characterItems"] = json::array();
|
||||
mock["characterName"] = this->_lastBloodWebChar;
|
||||
mock["isEntitled"] = true;
|
||||
mock["purchaseInfo"] = {{"quantity", 1},
|
||||
{"origin", "PlayerInventory"},
|
||||
{"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)");
|
||||
respHeaders = std::regex_replace(respHeaders, statusRegex, "HTTP/1.1 200");
|
||||
std::regex statusRegex(R"(HTTP\/\d\.\d\s+403)");
|
||||
respHeaders = std::regex_replace(respHeaders, statusRegex, "HTTP/1.1 200");
|
||||
|
||||
body = mock.dump();
|
||||
body = mock.dump();
|
||||
#ifdef _DEBUG
|
||||
Log::verbose("Spoofed bloodweb request for unowned character.");
|
||||
Log::verbose("Spoofed bloodweb request for unowned character.");
|
||||
#endif
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
modifyCharacterData(doc);
|
||||
|
||||
Reference in New Issue
Block a user