mirror of
https://github.com/DarkflameUniverse/DarkflameServer
synced 2024-08-30 18:43:58 +00:00
455f9470a5
* Move EntityManager to Game namespace * move initialization to later Need to wait for dZoneManager to be initialized. * Fix bugs - Cannot delete from a RandomAccessIterator while in a range based for loop. Touchup zone manager initialize replace magic numbers with better named constants replace magic zonecontrol id with a more readable hex alternative condense stack variables move initializers closer to their use initialize entity manager with zone control change initialize timings If zone is not zero we expect to initialize the entity manager during zone manager initialization Add constexpr for zone control LOT * Add proper error handling * revert vanity changes * Update WorldServer.cpp * Update dZoneManager.cpp
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>();
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|