mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-12-26 23:43:34 +00:00
4a6f3e44ee
* First iteration of pack reader and interface * Fix memory leak and remove logs * Complete packed asset interface and begin on file loading replacement * Implement proper BinaryIO error * Improve AssetMemoryBuffer for reading and implement more reading * Repair more file loading code and improve how navmeshes are loaded * Missing checks implementation * Revert addition of Manifest class and migration changes * Resolved all feedback.
352 lines
10 KiB
C++
352 lines
10 KiB
C++
#include "Game.h"
|
|
#include "Level.h"
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include "BinaryIO.h"
|
|
#include "dLogger.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) {
|
|
Game::logger->Log("Level", "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;
|
|
}
|
|
}
|
|
|
|
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>("FeatureGating");
|
|
|
|
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) {
|
|
dZoneManager::Instance()->GetZone()->SetSpawnPos(obj.position);
|
|
dZoneManager::Instance()->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
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
Spawner* spawner = new Spawner(spawnInfo);
|
|
dZoneManager::Instance()->AddSpawner(obj.id, spawner);
|
|
} 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 = dZoneManager::Instance()->GetZoneControlObject();
|
|
if (zoneControlObject != nullptr && info.lot == zoneControlObject->GetLOT())
|
|
goto deleteSettings;
|
|
|
|
EntityManager::Instance()->CreateEntity(info, nullptr);
|
|
} else {
|
|
deleteSettings:
|
|
|
|
for (auto* setting : info.settings) {
|
|
delete setting;
|
|
setting = nullptr;
|
|
}
|
|
|
|
info.settings.clear();
|
|
obj.settings.clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
header.sceneObjects = chunk;
|
|
}
|