DarkflameServer/dZoneManager/Level.cpp
2023-11-09 21:28:52 -06:00

355 lines
10 KiB
C++

#include "Game.h"
#include "Level.h"
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include "BinaryIO.h"
#include "Logger.h"
#include "Spawner.h"
#include "dZoneManager.h"
#include "GeneralUtils.h"
#include "Entity.h"
#include "EntityManager.h"
#include "CDFeatureGatingTable.h"
#include "CDClientManager.h"
#include "AssetManager.h"
Level::Level(Zone* parentZone, const std::string& filepath) {
m_ParentZone = parentZone;
auto buffer = Game::assetManager->GetFileAsBuffer(filepath.c_str());
if (!buffer.m_Success) {
LOG("Failed to load %s", filepath.c_str());
return;
}
std::istream file(&buffer);
ReadChunks(file);
buffer.close();
}
Level::~Level() {
for (std::map<uint32_t, Header>::iterator it = m_ChunkHeaders.begin(); it != m_ChunkHeaders.end(); ++it) {
if (it->second.id == Level::ChunkTypeID::FileInfo) delete it->second.fileInfo;
if (it->second.id == Level::ChunkTypeID::SceneObjectData) delete it->second.sceneObjects;
}
}
void Level::MakeSpawner(SceneObject obj){
SpawnerInfo spawnInfo = SpawnerInfo();
SpawnerNode* node = new SpawnerNode();
spawnInfo.templateID = obj.lot;
spawnInfo.spawnerID = obj.id;
spawnInfo.templateScale = obj.scale;
node->position = obj.position;
node->rotation = obj.rotation;
node->config = obj.settings;
spawnInfo.nodes.push_back(node);
for (LDFBaseData* data : obj.settings) {
if (data) {
if (data->GetKey() == u"spawntemplate") {
spawnInfo.templateID = std::stoi(data->GetValueAsString());
}
if (data->GetKey() == u"spawner_node_id") {
node->nodeID = std::stoi(data->GetValueAsString());
}
if (data->GetKey() == u"spawner_name") {
spawnInfo.name = data->GetValueAsString();
}
if (data->GetKey() == u"max_to_spawn") {
spawnInfo.maxToSpawn = std::stoi(data->GetValueAsString());
}
if (data->GetKey() == u"spawner_active_on_load") {
spawnInfo.activeOnLoad = std::stoi(data->GetValueAsString());
}
if (data->GetKey() == u"active_on_load") {
spawnInfo.activeOnLoad = std::stoi(data->GetValueAsString());
}
if (data->GetKey() == u"respawn") {
if (data->GetValueType() == eLDFType::LDF_TYPE_FLOAT) // Floats are in seconds
{
spawnInfo.respawnTime = std::stof(data->GetValueAsString());
} else if (data->GetValueType() == eLDFType::LDF_TYPE_U32) // Ints are in ms?
{
spawnInfo.respawnTime = std::stoul(data->GetValueAsString()) / 1000;
}
}
if (data->GetKey() == u"spawnsGroupOnSmash") {
spawnInfo.spawnsOnSmash = std::stoi(data->GetValueAsString());
}
if (data->GetKey() == u"spawnNetNameForSpawnGroupOnSmash") {
spawnInfo.spawnOnSmashGroupName = data->GetValueAsString();
}
if (data->GetKey() == u"groupID") { // Load object groups
std::string groupStr = data->GetValueAsString();
spawnInfo.groups = GeneralUtils::SplitString(groupStr, ';');
spawnInfo.groups.erase(spawnInfo.groups.end() - 1);
}
if (data->GetKey() == u"no_auto_spawn") {
spawnInfo.noAutoSpawn = static_cast<LDFData<bool>*>(data)->GetValue();
}
if (data->GetKey() == u"no_timed_spawn") {
spawnInfo.noTimedSpawn = static_cast<LDFData<bool>*>(data)->GetValue();
}
if (data->GetKey() == u"spawnActivator") {
spawnInfo.spawnActivator = static_cast<LDFData<bool>*>(data)->GetValue();
}
}
}
Game::zoneManager->MakeSpawner(spawnInfo);
}
const void Level::PrintAllObjects() {
for (std::map<uint32_t, Header>::iterator it = m_ChunkHeaders.begin(); it != m_ChunkHeaders.end(); ++it) {
if (it->second.id == Level::ChunkTypeID::SceneObjectData) {
it->second.sceneObjects->PrintAllObjects();
}
}
}
void Level::ReadChunks(std::istream& file) {
const uint32_t CHNK_HEADER = ('C' + ('H' << 8) + ('N' << 16) + ('K' << 24));
while (!file.eof()) {
uint32_t initPos = uint32_t(file.tellg());
uint32_t header = 0;
BinaryIO::BinaryRead(file, header);
if (header == CHNK_HEADER) { //Make sure we're reading a valid CHNK
Header header;
BinaryIO::BinaryRead(file, header.id);
BinaryIO::BinaryRead(file, header.chunkVersion);
BinaryIO::BinaryRead(file, header.chunkType);
BinaryIO::BinaryRead(file, header.size);
BinaryIO::BinaryRead(file, header.startPosition);
uint32_t target = initPos + header.size;
file.seekg(header.startPosition);
//We're currently not loading env or particle data
if (header.id == ChunkTypeID::FileInfo) {
ReadFileInfoChunk(file, header);
} else if (header.id == ChunkTypeID::SceneObjectData) {
ReadSceneObjectDataChunk(file, header);
}
m_ChunkHeaders.insert(std::make_pair(header.id, header));
file.seekg(target);
} else {
if (initPos == std::streamoff(0)) { //Really old chunk version
file.seekg(0);
Header header;
header.id = ChunkTypeID::FileInfo; //I guess?
FileInfoChunk* fileInfo = new FileInfoChunk();
BinaryIO::BinaryRead(file, header.chunkVersion);
BinaryIO::BinaryRead(file, header.chunkType);
file.ignore(1);
BinaryIO::BinaryRead(file, fileInfo->revision);
if (header.chunkVersion >= 45) file.ignore(4);
file.ignore(4 * (4 * 3));
if (header.chunkVersion >= 31) {
if (header.chunkVersion >= 39) {
file.ignore(12 * 4);
if (header.chunkVersion >= 40) {
uint32_t s = 0;
BinaryIO::BinaryRead(file, s);
for (uint32_t i = 0; i < s; ++i) {
file.ignore(4); //a uint
file.ignore(4); //two floats
file.ignore(4);
}
}
} else {
file.ignore(8);
}
file.ignore(3 * 4);
}
if (header.chunkVersion >= 36) {
file.ignore(3 * 4);
}
if (header.chunkVersion < 42) {
file.ignore(3 * 4);
if (header.chunkVersion >= 33) {
file.ignore(4 * 4);
}
}
for (uint32_t i = 0; i < 6; ++i) {
uint32_t count = 0;
BinaryIO::BinaryRead(file, count);
file.ignore(count);
}
file.ignore(4);
uint32_t count = 0;
BinaryIO::BinaryRead(file, count);
file.ignore(count * 12);
header.fileInfo = fileInfo;
m_ChunkHeaders.insert(std::make_pair(header.id, header));
//Now pretend to be a normal file and read Objects chunk:
Header hdr;
hdr.id = ChunkTypeID::SceneObjectData;
ReadSceneObjectDataChunk(file, hdr);
m_ChunkHeaders.insert(std::make_pair(hdr.id, hdr));
} break;
}
}
}
void Level::ReadFileInfoChunk(std::istream& file, Header& header) {
FileInfoChunk* fi = new FileInfoChunk;
BinaryIO::BinaryRead(file, fi->version);
BinaryIO::BinaryRead(file, fi->revision);
BinaryIO::BinaryRead(file, fi->enviromentChunkStart);
BinaryIO::BinaryRead(file, fi->objectChunkStart);
BinaryIO::BinaryRead(file, fi->particleChunkStart);
header.fileInfo = fi;
//PATCH FOR AG: (messed up file?)
if (header.fileInfo->revision == 3452816845 && m_ParentZone->GetZoneID().GetMapID() == 1100) header.fileInfo->revision = 26;
}
void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) {
SceneObjectDataChunk* chunk = new SceneObjectDataChunk;
uint32_t objectsCount = 0;
BinaryIO::BinaryRead(file, objectsCount);
CDFeatureGatingTable* featureGatingTable = CDClientManager::Instance().GetTable<CDFeatureGatingTable>();
for (uint32_t i = 0; i < objectsCount; ++i) {
SceneObject obj;
BinaryIO::BinaryRead(file, obj.id);
BinaryIO::BinaryRead(file, obj.lot);
/*if (header.fileInfo->version >= 0x26)*/ BinaryIO::BinaryRead(file, obj.value1);
/*if (header.fileInfo->version >= 0x20)*/ BinaryIO::BinaryRead(file, obj.value2);
BinaryIO::BinaryRead(file, obj.position);
BinaryIO::BinaryRead(file, obj.rotation);
BinaryIO::BinaryRead(file, obj.scale);
//This is a little bit of a bodge, but because the alpha client (HF) doesn't store the
//spawn position / rotation like the later versions do, we need to check the LOT for the spawn pos & set it.
if (obj.lot == LOT_MARKER_PLAYER_START) {
Game::zoneManager->GetZone()->SetSpawnPos(obj.position);
Game::zoneManager->GetZone()->SetSpawnRot(obj.rotation);
}
std::u16string ldfString = u"";
uint32_t length = 0;
BinaryIO::BinaryRead(file, length);
for (uint32_t i = 0; i < length; ++i) {
uint16_t data;
BinaryIO::BinaryRead(file, data);
ldfString.push_back(data);
}
std::string sData = GeneralUtils::UTF16ToWTF8(ldfString);
std::stringstream ssData(sData);
std::string token;
char deliminator = '\n';
while (std::getline(ssData, token, deliminator)) {
LDFBaseData* ldfData = LDFBaseData::DataFromString(token);
obj.settings.push_back(ldfData);
}
BinaryIO::BinaryRead(file, obj.value3);
// Feature gating
bool gated = false;
for (LDFBaseData* data : obj.settings) {
if (data->GetKey() == u"gatingOnFeature") {
std::string featureGate = data->GetValueAsString();
if (!featureGatingTable->FeatureUnlocked(featureGate)) {
gated = true;
break;
}
}
}
if (gated) {
for (auto* setting : obj.settings) {
delete setting;
}
obj.settings.clear();
continue;
}
if (obj.lot == 176) { //Spawner
MakeSpawner(obj);
} else { //Regular object
EntityInfo info;
info.spawnerID = 0;
info.id = obj.id;
info.lot = obj.lot;
info.pos = obj.position;
info.rot = obj.rotation;
info.settings = obj.settings;
info.scale = obj.scale;
//Check to see if we shouldn't be loading this:
bool clientOnly = false;
bool serverOnly = false;
std::string featureGate = "";
for (LDFBaseData* data : obj.settings) {
if (data) {
if (data->GetKey() == u"loadOnClientOnly") {
clientOnly = (bool)std::stoi(data->GetValueAsString());
break;
}
if (data->GetKey() == u"loadSrvrOnly") {
serverOnly = (bool)std::stoi(data->GetValueAsString());
break;
}
}
}
if (!clientOnly) {
// We should never have more than 1 zone control object
const auto zoneControlObject = Game::zoneManager->GetZoneControlObject();
if (zoneControlObject != nullptr && info.lot == zoneControlObject->GetLOT())
goto deleteSettings;
Game::entityManager->CreateEntity(info, nullptr);
} else {
deleteSettings:
for (auto* setting : info.settings) {
delete setting;
setting = nullptr;
}
info.settings.clear();
obj.settings.clear();
}
}
}
header.sceneObjects = chunk;
}