End of optimizations for now

going faster
This commit is contained in:
David Markowitz 2023-11-21 00:04:33 -08:00
parent 2a40e37b07
commit d8476b7325
9 changed files with 191 additions and 391 deletions

View File

@ -1,14 +1,6 @@
#include "BinaryIO.h" #include "BinaryIO.h"
#include <string> #include <string>
void BinaryIO::WriteString(const std::string& stringToWrite, std::ofstream& outstream) {
//BinaryWrite(outstream, uint32_t(stringToWrite.length()));
for (size_t i = 0; i < size_t(stringToWrite.length()); ++i) {
BinaryIO::BinaryWrite(outstream, stringToWrite[i]);
}
}
//For reading null-terminated strings //For reading null-terminated strings
std::string BinaryIO::ReadString(std::istream& instream) { std::string BinaryIO::ReadString(std::istream& instream) {
std::string toReturn; std::string toReturn;
@ -23,36 +15,3 @@ std::string BinaryIO::ReadString(std::istream& instream) {
return toReturn; return toReturn;
} }
//For reading strings of a specific size
std::string BinaryIO::ReadString(std::istream& instream, size_t size) {
std::string toReturn;
char buffer;
for (size_t i = 0; i < size; ++i) {
BinaryIO::BinaryRead(instream, buffer);
toReturn += buffer;
}
return toReturn;
}
std::string BinaryIO::ReadWString(std::istream& instream) {
size_t size;
BinaryRead(instream, size);
//toReturn.resize(size);
std::string test;
unsigned char buf;
for (size_t i = 0; i < size; ++i) {
//instream.ignore(1);
BinaryRead(instream, buf);
test += buf;
}
//printf("%s\n", test.c_str());
//instream.read((char*)&toReturn[0], size * 2);
//std::string str(toReturn.begin(), toReturn.end());
return test;
}

View File

@ -1,8 +1,17 @@
#pragma once #pragma once
#ifndef __BINARYIO__H__
#define __BINARYIO__H__
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <string>
#include "Game.h"
#include "Logger.h"
namespace BinaryIO { namespace BinaryIO {
template<typename T> template<typename T>
std::ostream& BinaryWrite(std::ostream& stream, const T& value) { std::ostream& BinaryWrite(std::ostream& stream, const T& value) {
return stream.write(reinterpret_cast<const char*>(&value), sizeof(T)); return stream.write(reinterpret_cast<const char*>(&value), sizeof(T));
@ -15,13 +24,51 @@ namespace BinaryIO {
return stream.read(reinterpret_cast<char*>(&value), sizeof(T)); return stream.read(reinterpret_cast<char*>(&value), sizeof(T));
} }
void WriteString(const std::string& stringToWrite, std::ofstream& outstream); enum class ReadType : int8_t {
WideString = 0,
String = 1,
};
template<typename SizeType>
inline void ReadString(std::istream& stream, std::u16string& value) {
static_assert(std::is_integral<SizeType>::value, "SizeType must be an integral type.");
SizeType size;
BinaryRead(stream, size);
if (!stream.good()) throw std::runtime_error("Failed to read from istream.");
value.resize(size);
stream.read(reinterpret_cast<char*>(value.data()), size * sizeof(uint16_t));
}
template<typename SizeType>
inline void ReadString(std::istream& stream, std::string& value, ReadType readType) {
static_assert(std::is_integral<SizeType>::value, "SizeType must be an integral type.");
SizeType size;
BinaryRead(stream, size);
if (!stream.good()) throw std::runtime_error("Failed to read from istream.");
value.resize(size);
if (readType == ReadType::WideString) {
uint16_t wideChar;
// Faster to do this than to read a u16string and convert it to a string since we only go through allocator once
for (SizeType i = 0; i < size; ++i) {
BinaryRead(stream, wideChar);
value[i] = static_cast<char>(wideChar);
}
} else {
stream.read(value.data(), size);
}
}
std::string ReadString(std::istream& instream); std::string ReadString(std::istream& instream);
std::string ReadString(std::istream& instream, size_t size);
std::string ReadWString(std::istream& instream);
inline bool DoesFileExist(const std::string& name) { inline bool DoesFileExist(const std::string& name) {
std::ifstream f(name.c_str()); std::ifstream f(name.c_str());
return f.good(); return f.good();
} }
} }
#endif //!__BINARYIO__H__

View File

@ -9,20 +9,9 @@ PackIndex::PackIndex(const std::filesystem::path& filePath) {
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version); BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version);
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount); BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount);
for (int i = 0; i < m_PackPathCount; i++) { m_PackPaths.resize(m_PackPathCount);
uint32_t stringLen = 0; for (auto& item : m_PackPaths) {
BinaryIO::BinaryRead<uint32_t>(m_FileStream, stringLen); BinaryIO::ReadString<uint32_t>(m_FileStream, item, BinaryIO::ReadType::String);
std::string path;
for (int j = 0; j < stringLen; j++) {
char inChar;
BinaryIO::BinaryRead<char>(m_FileStream, inChar);
path += inChar;
}
m_PackPaths.push_back(path);
} }
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackFileIndexCount); BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackFileIndexCount);

View File

@ -16,12 +16,6 @@
#include "AssetManager.h" #include "AssetManager.h"
#include "dConfig.h" #include "dConfig.h"
void Level::SceneObjectDataChunk::PrintAllObjects() const {
for (const auto& [id, sceneObj] : objects) {
LOG("ID: %d LOT: %d", id, sceneObj.lot);
}
}
Level::Level(Zone* parentZone, const std::string& filepath) { Level::Level(Zone* parentZone, const std::string& filepath) {
m_ParentZone = parentZone; m_ParentZone = parentZone;
@ -38,13 +32,6 @@ Level::Level(Zone* parentZone, const std::string& filepath) {
buffer.close(); buffer.close();
} }
Level::~Level() {
for (auto& [id, header] : m_ChunkHeaders) {
if (header.id == Level::ChunkTypeID::FileInfo) delete header.fileInfo;
if (header.id == Level::ChunkTypeID::SceneObjectData) delete header.sceneObjects;
}
}
void Level::MakeSpawner(SceneObject obj) { void Level::MakeSpawner(SceneObject obj) {
SpawnerInfo spawnInfo = SpawnerInfo(); SpawnerInfo spawnInfo = SpawnerInfo();
SpawnerNode* node = new SpawnerNode(); SpawnerNode* node = new SpawnerNode();
@ -56,7 +43,7 @@ void Level::MakeSpawner(SceneObject obj){
node->config = obj.settings; node->config = obj.settings;
spawnInfo.nodes.push_back(node); spawnInfo.nodes.push_back(node);
for (LDFBaseData* data : obj.settings) { for (LDFBaseData* data : obj.settings) {
if (data) { if (!data) continue;
if (data->GetKey() == u"spawntemplate") { if (data->GetKey() == u"spawntemplate") {
spawnInfo.templateID = std::stoi(data->GetValueAsString()); spawnInfo.templateID = std::stoi(data->GetValueAsString());
} }
@ -111,16 +98,8 @@ void Level::MakeSpawner(SceneObject obj){
spawnInfo.spawnActivator = static_cast<LDFData<bool>*>(data)->GetValue(); spawnInfo.spawnActivator = static_cast<LDFData<bool>*>(data)->GetValue();
} }
} }
}
Game::zoneManager->MakeSpawner(spawnInfo);
}
const void Level::PrintAllObjects() { Game::zoneManager->MakeSpawner(spawnInfo);
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) { void Level::ReadChunks(std::istream& file) {
@ -155,11 +134,10 @@ void Level::ReadChunks(std::istream& file) {
file.seekg(0); file.seekg(0);
Header header; Header header;
header.id = ChunkTypeID::FileInfo; //I guess? header.id = ChunkTypeID::FileInfo; //I guess?
FileInfoChunk* fileInfo = new FileInfoChunk();
BinaryIO::BinaryRead(file, header.chunkVersion); BinaryIO::BinaryRead(file, header.chunkVersion);
BinaryIO::BinaryRead(file, header.chunkType); BinaryIO::BinaryRead(file, header.chunkType);
file.ignore(1); file.ignore(1);
BinaryIO::BinaryRead(file, fileInfo->revision); BinaryIO::BinaryRead(file, header.fileInfo.revision);
if (header.chunkVersion >= 45) file.ignore(4); if (header.chunkVersion >= 45) file.ignore(4);
file.ignore(4 * (4 * 3)); file.ignore(4 * (4 * 3));
@ -172,9 +150,7 @@ void Level::ReadChunks(std::istream& file) {
uint32_t s = 0; uint32_t s = 0;
BinaryIO::BinaryRead(file, s); BinaryIO::BinaryRead(file, s);
for (uint32_t i = 0; i < s; ++i) { for (uint32_t i = 0; i < s; ++i) {
file.ignore(4); //a uint file.ignore(4 * 3); //a uint and two floats
file.ignore(4); //two floats
file.ignore(4);
} }
} }
} else { } else {
@ -208,7 +184,6 @@ void Level::ReadChunks(std::istream& file) {
BinaryIO::BinaryRead(file, count); BinaryIO::BinaryRead(file, count);
file.ignore(count * 12); file.ignore(count * 12);
header.fileInfo = fileInfo;
m_ChunkHeaders.insert(std::make_pair(header.id, header)); m_ChunkHeaders.insert(std::make_pair(header.id, header));
//Now pretend to be a normal file and read Objects chunk: //Now pretend to be a normal file and read Objects chunk:
@ -222,20 +197,17 @@ void Level::ReadChunks(std::istream& file) {
} }
void Level::ReadFileInfoChunk(std::istream& file, Header& header) { void Level::ReadFileInfoChunk(std::istream& file, Header& header) {
FileInfoChunk* fi = new FileInfoChunk; BinaryIO::BinaryRead(file, header.fileInfo.version);
BinaryIO::BinaryRead(file, fi->version); BinaryIO::BinaryRead(file, header.fileInfo.revision);
BinaryIO::BinaryRead(file, fi->revision); BinaryIO::BinaryRead(file, header.fileInfo.enviromentChunkStart);
BinaryIO::BinaryRead(file, fi->enviromentChunkStart); BinaryIO::BinaryRead(file, header.fileInfo.objectChunkStart);
BinaryIO::BinaryRead(file, fi->objectChunkStart); BinaryIO::BinaryRead(file, header.fileInfo.particleChunkStart);
BinaryIO::BinaryRead(file, fi->particleChunkStart);
header.fileInfo = fi;
//PATCH FOR AG: (messed up file?) //PATCH FOR AG: (messed up file?)
if (header.fileInfo->revision == 3452816845 && m_ParentZone->GetZoneID().GetMapID() == 1100) header.fileInfo->revision = 26; if (header.fileInfo.revision == 0xCDCDCDCD && m_ParentZone->GetZoneID().GetMapID() == 1100) header.fileInfo.revision = 26;
} }
void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) { void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) {
SceneObjectDataChunk* chunk = new SceneObjectDataChunk;
uint32_t objectsCount = 0; uint32_t objectsCount = 0;
BinaryIO::BinaryRead(file, objectsCount); BinaryIO::BinaryRead(file, objectsCount);
@ -249,17 +221,22 @@ void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) {
GeneralUtils::TryParse<int32_t>(Game::config->GetValue("version_current"), gating.current); GeneralUtils::TryParse<int32_t>(Game::config->GetValue("version_current"), gating.current);
GeneralUtils::TryParse<int32_t>(Game::config->GetValue("version_minor"), gating.minor); GeneralUtils::TryParse<int32_t>(Game::config->GetValue("version_minor"), gating.minor);
const auto zoneControlObject = Game::zoneManager->GetZoneControlObject();
DluAssert(zoneControlObject != nullptr);
for (uint32_t i = 0; i < objectsCount; ++i) { for (uint32_t i = 0; i < objectsCount; ++i) {
std::u16string ldfString;
SceneObject obj; SceneObject obj;
BinaryIO::BinaryRead(file, obj.id); BinaryIO::BinaryRead(file, obj.id);
BinaryIO::BinaryRead(file, obj.lot); BinaryIO::BinaryRead(file, obj.lot);
/*if (header.fileInfo->version >= 0x26)*/ BinaryIO::BinaryRead(file, obj.nodeType); /*if (header.fileInfo.version >= 0x26)*/ BinaryIO::BinaryRead(file, obj.nodeType);
/*if (header.fileInfo->version >= 0x20)*/ BinaryIO::BinaryRead(file, obj.glomId); /*if (header.fileInfo.version >= 0x20)*/ BinaryIO::BinaryRead(file, obj.glomId);
BinaryIO::BinaryRead(file, obj.position); BinaryIO::BinaryRead(file, obj.position);
BinaryIO::BinaryRead(file, obj.rotation); BinaryIO::BinaryRead(file, obj.rotation);
BinaryIO::BinaryRead(file, obj.scale); BinaryIO::BinaryRead(file, obj.scale);
BinaryIO::ReadString<uint32_t>(file, ldfString);
BinaryIO::BinaryRead(file, obj.value3);
//This is a little bit of a bodge, but because the alpha client (HF) doesn't store the //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. //spawn position / rotation like the later versions do, we need to check the LOT for the spawn pos & set it.
@ -268,16 +245,6 @@ void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) {
Game::zoneManager->GetZone()->SetSpawnRot(obj.rotation); 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::string sData = GeneralUtils::UTF16ToWTF8(ldfString);
std::stringstream ssData(sData); std::stringstream ssData(sData);
std::string token; std::string token;
@ -288,35 +255,40 @@ void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) {
obj.settings.push_back(ldfData); obj.settings.push_back(ldfData);
} }
BinaryIO::BinaryRead(file, obj.value3);
// Feature gating // We should never have more than 1 zone control object
bool gated = false; bool skipLoadingObject = obj.lot == zoneControlObject->GetLOT();
for (LDFBaseData* data : obj.settings) { for (LDFBaseData* data : obj.settings) {
if (!data) continue;
if (data->GetKey() == u"gatingOnFeature") { if (data->GetKey() == u"gatingOnFeature") {
gating.featureName = data->GetValueAsString(); gating.featureName = data->GetValueAsString();
if (gating.featureName == Game::config->GetValue("event_1")) break; if (gating.featureName == Game::config->GetValue("event_1")) continue;
else if (gating.featureName == Game::config->GetValue("event_2")) break; else if (gating.featureName == Game::config->GetValue("event_2")) continue;
else if (gating.featureName == Game::config->GetValue("event_3")) break; else if (gating.featureName == Game::config->GetValue("event_3")) continue;
else if (gating.featureName == Game::config->GetValue("event_4")) break; else if (gating.featureName == Game::config->GetValue("event_4")) continue;
else if (gating.featureName == Game::config->GetValue("event_5")) break; else if (gating.featureName == Game::config->GetValue("event_5")) continue;
else if (gating.featureName == Game::config->GetValue("event_6")) break; else if (gating.featureName == Game::config->GetValue("event_6")) continue;
else if (gating.featureName == Game::config->GetValue("event_7")) break; else if (gating.featureName == Game::config->GetValue("event_7")) continue;
else if (gating.featureName == Game::config->GetValue("event_8")) break; else if (gating.featureName == Game::config->GetValue("event_8")) continue;
else if (!featureGatingTable->FeatureUnlocked(gating)) { else if (!featureGatingTable->FeatureUnlocked(gating)) {
gated = true; // The feature is not unlocked, so we can skip loading this object
skipLoadingObject = true;
break; break;
} }
} }
// If this is a client only object, we can skip loading it
if (data->GetKey() == u"loadOnClientOnly") {
skipLoadingObject = static_cast<bool>(std::stoi(data->GetValueAsString()));
break;
}
} }
if (gated) { if (skipLoadingObject) {
for (auto* setting : obj.settings) { for (auto* setting : obj.settings) {
delete setting; delete setting;
setting = nullptr;
} }
obj.settings.clear();
continue; continue;
} }
@ -331,45 +303,7 @@ void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) {
info.rot = obj.rotation; info.rot = obj.rotation;
info.settings = obj.settings; info.settings = obj.settings;
info.scale = obj.scale; info.scale = obj.scale;
Game::entityManager->CreateEntity(info);
//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;
}

View File

@ -27,33 +27,21 @@ public:
uint32_t particleChunkStart; uint32_t particleChunkStart;
}; };
struct SceneObjectDataChunk {
std::map<LWOOBJID, SceneObject> objects;
void PrintAllObjects() const;
uint32_t GetObjectCount() { return objects.size(); }
};
struct Header { struct Header {
uint32_t id; uint32_t id;
uint16_t chunkVersion; uint16_t chunkVersion;
ChunkTypeID chunkType; ChunkTypeID chunkType;
uint32_t size; uint32_t size;
uint32_t startPosition; uint32_t startPosition;
FileInfoChunk* fileInfo; FileInfoChunk fileInfo;
SceneObjectDataChunk* sceneObjects;
LWOSCENEID lwoSceneID; LWOSCENEID lwoSceneID;
}; };
public: public:
Level(Zone* parentZone, const std::string& filepath); Level(Zone* parentZone, const std::string& filepath);
~Level();
static void MakeSpawner(SceneObject obj); static void MakeSpawner(SceneObject obj);
const void PrintAllObjects();
std::map<uint32_t, Header> m_ChunkHeaders; std::map<uint32_t, Header> m_ChunkHeaders;
private: private:
Zone* m_ParentZone; Zone* m_ParentZone;

View File

@ -20,7 +20,6 @@
Zone::Zone(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) : Zone::Zone(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) :
m_ZoneID(mapID, instanceID, cloneID) { m_ZoneID(mapID, instanceID, cloneID) {
m_NumberOfScenesLoaded = 0;
m_NumberOfObjectsLoaded = 0; m_NumberOfObjectsLoaded = 0;
m_NumberOfSceneTransitionsLoaded = 0; m_NumberOfSceneTransitionsLoaded = 0;
m_CheckSum = 0; m_CheckSum = 0;
@ -81,18 +80,10 @@ void Zone::LoadZoneIntoMemory() {
} }
//Read generic zone info: //Read generic zone info:
uint8_t stringLength; BinaryIO::ReadString<uint8_t>(file, m_ZonePath, BinaryIO::ReadType::String);
BinaryIO::BinaryRead(file, stringLength); BinaryIO::ReadString<uint8_t>(file, m_ZoneRawPath, BinaryIO::ReadType::String);
m_ZonePath = BinaryIO::ReadString(file, stringLength); BinaryIO::ReadString<uint8_t>(file, m_ZoneName, BinaryIO::ReadType::String);
BinaryIO::ReadString<uint8_t>(file, m_ZoneDesc, BinaryIO::ReadType::String);
BinaryIO::BinaryRead(file, stringLength);
m_ZoneRawPath = BinaryIO::ReadString(file, stringLength);
BinaryIO::BinaryRead(file, stringLength);
m_ZoneName = BinaryIO::ReadString(file, stringLength);
BinaryIO::BinaryRead(file, stringLength);
m_ZoneDesc = BinaryIO::ReadString(file, stringLength);
if (m_FileFormatVersion >= Zone::FileFormatVersion::PreAlpha) { if (m_FileFormatVersion >= Zone::FileFormatVersion::PreAlpha) {
BinaryIO::BinaryRead(file, m_NumberOfSceneTransitionsLoaded); BinaryIO::BinaryRead(file, m_NumberOfSceneTransitionsLoaded);
@ -108,10 +99,11 @@ void Zone::LoadZoneIntoMemory() {
uint32_t pathCount; uint32_t pathCount;
BinaryIO::BinaryRead(file, pathCount); BinaryIO::BinaryRead(file, pathCount);
m_Paths.reserve(pathCount);
for (uint32_t i = 0; i < pathCount; ++i) LoadPath(file); for (uint32_t i = 0; i < pathCount; ++i) LoadPath(file);
for (Path path : m_Paths) { for (Path path : m_Paths) {
if (path.pathType == PathType::Spawner) { if (path.pathType != PathType::Spawner) continue;
SpawnerInfo info = SpawnerInfo(); SpawnerInfo info = SpawnerInfo();
for (PathWaypoint waypoint : path.pathWaypoints) { for (PathWaypoint waypoint : path.pathWaypoints) {
SpawnerNode* node = new SpawnerNode(); SpawnerNode* node = new SpawnerNode();
@ -121,7 +113,8 @@ void Zone::LoadZoneIntoMemory() {
node->config = waypoint.config; node->config = waypoint.config;
for (LDFBaseData* data : waypoint.config) { for (LDFBaseData* data : waypoint.config) {
if (data) { if (!data) continue;
if (data->GetKey() == u"spawner_node_id") { if (data->GetKey() == u"spawner_node_id") {
node->nodeID = std::stoi(data->GetValueAsString()); node->nodeID = std::stoi(data->GetValueAsString());
} else if (data->GetKey() == u"spawner_max_per_node") { } else if (data->GetKey() == u"spawner_max_per_node") {
@ -139,7 +132,6 @@ void Zone::LoadZoneIntoMemory() {
info.name = data->GetValueAsString(); info.name = data->GetValueAsString();
} }
} }
}
info.nodes.push_back(node); info.nodes.push_back(node);
} }
info.templateID = path.spawner.spawnedLOT; info.templateID = path.spawner.spawnedLOT;
@ -152,8 +144,6 @@ void Zone::LoadZoneIntoMemory() {
Spawner* spawner = new Spawner(info); Spawner* spawner = new Spawner(info);
Game::zoneManager->AddSpawner(info.spawnerID, spawner); Game::zoneManager->AddSpawner(info.spawnerID, spawner);
} }
}
} }
} else { } else {
LOG("Failed to open: %s", m_ZoneFilePath.c_str()); LOG("Failed to open: %s", m_ZoneFilePath.c_str());
@ -180,16 +170,16 @@ std::string Zone::GetFilePathForZoneID() {
uint32_t Zone::CalculateChecksum() { uint32_t Zone::CalculateChecksum() {
uint32_t sum1 = 0xffff, sum2 = 0xffff; uint32_t sum1 = 0xffff, sum2 = 0xffff;
for (std::map<LWOSCENEID, uint32_t>::const_iterator it = m_MapRevisions.cbegin(); it != m_MapRevisions.cend(); ++it) { for (const auto& [scene, sceneRevision] : m_MapRevisions) {
uint32_t sceneID = it->first.GetSceneID(); uint32_t sceneID = scene.GetSceneID();
sum2 += sum1 += (sceneID >> 16); sum2 += sum1 += (sceneID >> 16);
sum2 += sum1 += (sceneID & 0xffff); sum2 += sum1 += (sceneID & 0xffff);
uint32_t layerID = it->first.GetLayerID(); uint32_t layerID = scene.GetLayerID();
sum2 += sum1 += (layerID >> 16); sum2 += sum1 += (layerID >> 16);
sum2 += sum1 += (layerID & 0xffff); sum2 += sum1 += (layerID & 0xffff);
uint32_t revision = it->second; uint32_t revision = sceneRevision;
sum2 += sum1 += (revision >> 16); sum2 += sum1 += (revision >> 16);
sum2 += sum1 += (revision & 0xffff); sum2 += sum1 += (revision & 0xffff);
} }
@ -201,30 +191,20 @@ uint32_t Zone::CalculateChecksum() {
} }
void Zone::LoadLevelsIntoMemory() { void Zone::LoadLevelsIntoMemory() {
for (std::map<LWOSCENEID, SceneRef>::iterator it = m_Scenes.begin(); it != m_Scenes.end(); ++it) { for (auto& [sceneID, scene] : m_Scenes) {
if (it->second.level == nullptr) { if (scene.level) continue;
it->second.level = new Level(this, m_ZonePath + it->second.filename); scene.level = new Level(this, m_ZonePath + scene.filename);
if (it->second.level->m_ChunkHeaders.size() > 0) { if (scene.level->m_ChunkHeaders.empty()) continue;
it->second.level->m_ChunkHeaders.begin()->second.lwoSceneID = it->first;
AddRevision(it->second.level->m_ChunkHeaders.begin()->second.lwoSceneID, it->second.level->m_ChunkHeaders.begin()->second.fileInfo->revision); scene.level->m_ChunkHeaders.begin()->second.lwoSceneID = sceneID;
} AddRevision(scene.level->m_ChunkHeaders.begin()->second.lwoSceneID, scene.level->m_ChunkHeaders.begin()->second.fileInfo.revision);
}
} }
} }
void Zone::AddRevision(LWOSCENEID sceneID, uint32_t revision) { void Zone::AddRevision(LWOSCENEID sceneID, uint32_t revision) {
for (std::pair<LWOSCENEID, uint32_t> item : m_MapRevisions) { if (m_MapRevisions.find(sceneID) == m_MapRevisions.end()) {
if (item.first == sceneID) return; m_MapRevisions.insert(std::make_pair(sceneID, revision));
}
m_MapRevisions[LWOSCENEID(sceneID)] = revision;
}
const void Zone::PrintAllGameObjects() {
for (std::pair<LWOSCENEID, SceneRef> scene : m_Scenes) {
LOG("In sceneID: %i", scene.first.GetSceneID());
scene.second.level->PrintAllObjects();
} }
} }
@ -233,17 +213,10 @@ void Zone::LoadScene(std::istream& file) {
scene.level = nullptr; scene.level = nullptr;
LWOSCENEID lwoSceneID(LWOZONEID_INVALID, 0); LWOSCENEID lwoSceneID(LWOZONEID_INVALID, 0);
uint8_t sceneFilenameLength; BinaryIO::ReadString<uint8_t>(file, scene.filename, BinaryIO::ReadType::String);
BinaryIO::BinaryRead(file, sceneFilenameLength);
scene.filename = BinaryIO::ReadString(file, sceneFilenameLength);
std::string luTriggersPath = scene.filename.substr(0, scene.filename.size() - 4) + ".lutriggers"; std::string luTriggersPath = scene.filename.substr(0, scene.filename.size() - 4) + ".lutriggers";
std::vector<LUTriggers::Trigger*> triggers; if (Game::assetManager->HasFile((m_ZonePath + luTriggersPath).c_str())) LoadLUTriggers(luTriggersPath, scene);
if(Game::assetManager->HasFile((m_ZonePath + luTriggersPath).c_str())) triggers = LoadLUTriggers(luTriggersPath, scene.id);
for (LUTriggers::Trigger* trigger : triggers) {
scene.triggers.insert({ trigger->id, trigger });
}
if (m_FileFormatVersion >= Zone::FileFormatVersion::LatePreAlpha || m_FileFormatVersion < Zone::FileFormatVersion::PrePreAlpha) { if (m_FileFormatVersion >= Zone::FileFormatVersion::LatePreAlpha || m_FileFormatVersion < Zone::FileFormatVersion::PrePreAlpha) {
BinaryIO::BinaryRead(file, scene.id); BinaryIO::BinaryRead(file, scene.id);
@ -253,9 +226,7 @@ void Zone::LoadScene(std::istream& file) {
BinaryIO::BinaryRead(file, scene.sceneType); BinaryIO::BinaryRead(file, scene.sceneType);
lwoSceneID.SetLayerID(scene.sceneType); lwoSceneID.SetLayerID(scene.sceneType);
uint8_t sceneNameLength; BinaryIO::ReadString<uint8_t>(file, scene.name, BinaryIO::ReadType::String);
BinaryIO::BinaryRead(file, sceneNameLength);
scene.name = BinaryIO::ReadString(file, sceneNameLength);
} }
if (m_FileFormatVersion == Zone::FileFormatVersion::LatePreAlpha) { if (m_FileFormatVersion == Zone::FileFormatVersion::LatePreAlpha) {
@ -270,17 +241,14 @@ void Zone::LoadScene(std::istream& file) {
} }
m_Scenes.insert(std::make_pair(lwoSceneID, scene)); m_Scenes.insert(std::make_pair(lwoSceneID, scene));
m_NumberOfScenesLoaded++;
} }
std::vector<LUTriggers::Trigger*> Zone::LoadLUTriggers(std::string triggerFile, LWOSCENEID sceneID) { void Zone::LoadLUTriggers(std::string triggerFile, SceneRef& scene) {
std::vector<LUTriggers::Trigger*> lvlTriggers;
auto buffer = Game::assetManager->GetFileAsBuffer((m_ZonePath + triggerFile).c_str()); auto buffer = Game::assetManager->GetFileAsBuffer((m_ZonePath + triggerFile).c_str());
if (!buffer.m_Success) { if (!buffer.m_Success) {
LOG("Failed to load %s from disk. Skipping loading triggers", (m_ZonePath + triggerFile).c_str()); LOG("Failed to load %s from disk. Skipping loading triggers", (m_ZonePath + triggerFile).c_str());
return lvlTriggers; return;
} }
std::istream file(&buffer); std::istream file(&buffer);
@ -289,42 +257,43 @@ std::vector<LUTriggers::Trigger*> Zone::LoadLUTriggers(std::string triggerFile,
buffer.close(); buffer.close();
if (data.str().size() == 0) return lvlTriggers; data.seekg(0, std::ios::end);
int32_t size = data.tellg();
data.seekg(0, std::ios::beg);
tinyxml2::XMLDocument* doc = new tinyxml2::XMLDocument(); if (size == 0) return;
if (!doc) return lvlTriggers;
if (doc->Parse(data.str().c_str(), data.str().size()) == 0) { tinyxml2::XMLDocument doc;
//LOG("Loaded LUTriggers from file %s!", triggerFile.c_str());
} else { if (doc.Parse(data.str().c_str(), size) != tinyxml2::XML_SUCCESS) {
LOG("Failed to load LUTriggers from file %s", triggerFile.c_str()); LOG("Failed to load LUTriggers from file %s", triggerFile.c_str());
return lvlTriggers; return;
} }
tinyxml2::XMLElement* triggers = doc->FirstChildElement("triggers"); auto* triggers = doc.FirstChildElement("triggers");
if (!triggers) return lvlTriggers; if (!triggers) return;
auto currentTrigger = triggers->FirstChildElement("trigger"); auto* currentTrigger = triggers->FirstChildElement("trigger");
while (currentTrigger) { while (currentTrigger) {
LUTriggers::Trigger* newTrigger = new LUTriggers::Trigger(); LUTriggers::Trigger* newTrigger = new LUTriggers::Trigger();
currentTrigger->QueryAttribute("enabled", &newTrigger->enabled); currentTrigger->QueryAttribute("enabled", &newTrigger->enabled);
currentTrigger->QueryAttribute("id", &newTrigger->id); currentTrigger->QueryAttribute("id", &newTrigger->id);
auto currentEvent = currentTrigger->FirstChildElement("event"); auto* currentEvent = currentTrigger->FirstChildElement("event");
while (currentEvent) { while (currentEvent) {
LUTriggers::Event* newEvent = new LUTriggers::Event(); LUTriggers::Event* newEvent = new LUTriggers::Event();
newEvent->id = TriggerEventType::StringToTriggerEventType(currentEvent->Attribute("id")); newEvent->id = TriggerEventType::StringToTriggerEventType(currentEvent->Attribute("id"));
auto currentCommand = currentEvent->FirstChildElement("command"); auto* currentCommand = currentEvent->FirstChildElement("command");
while (currentCommand) { while (currentCommand) {
LUTriggers::Command* newCommand = new LUTriggers::Command(); LUTriggers::Command* newCommand = new LUTriggers::Command();
newCommand->id = TriggerCommandType::StringToTriggerCommandType(currentCommand->Attribute("id")); newCommand->id = TriggerCommandType::StringToTriggerCommandType(currentCommand->Attribute("id"));
newCommand->target = currentCommand->Attribute("target"); newCommand->target = currentCommand->Attribute("target");
if (currentCommand->Attribute("targetName") != NULL) { if (currentCommand->Attribute("targetName")) {
newCommand->targetName = currentCommand->Attribute("targetName"); newCommand->targetName = currentCommand->Attribute("targetName");
} } else if (currentCommand->Attribute("args")) {
if (currentCommand->Attribute("args") != NULL) {
newCommand->args = currentCommand->Attribute("args"); newCommand->args = currentCommand->Attribute("args");
} }
newEvent->commands.push_back(newCommand); newEvent->commands.push_back(newCommand);
currentCommand = currentCommand->NextSiblingElement("command"); currentCommand = currentCommand->NextSiblingElement("command");
} }
@ -332,19 +301,18 @@ std::vector<LUTriggers::Trigger*> Zone::LoadLUTriggers(std::string triggerFile,
currentEvent = currentEvent->NextSiblingElement("event"); currentEvent = currentEvent->NextSiblingElement("event");
} }
currentTrigger = currentTrigger->NextSiblingElement("trigger"); currentTrigger = currentTrigger->NextSiblingElement("trigger");
lvlTriggers.push_back(newTrigger); scene.triggers.insert(std::make_pair(newTrigger->id, newTrigger));
} }
delete doc;
return lvlTriggers;
} }
LUTriggers::Trigger* Zone::GetTrigger(uint32_t sceneID, uint32_t triggerID) { LUTriggers::Trigger* Zone::GetTrigger(uint32_t sceneID, uint32_t triggerID) {
if (m_Scenes.find(sceneID) == m_Scenes.end()) return nullptr; auto scene = m_Scenes.find(sceneID);
if (m_Scenes[sceneID].triggers.find(triggerID) == m_Scenes[sceneID].triggers.end()) return nullptr; if (scene == m_Scenes.end()) return nullptr;
return m_Scenes[sceneID].triggers[triggerID]; auto trigger = scene->second.triggers.find(triggerID);
if (trigger == scene->second.triggers.end()) return nullptr;
return trigger->second;
} }
const Path* Zone::GetPath(std::string name) const { const Path* Zone::GetPath(std::string name) const {
@ -360,15 +328,14 @@ const Path* Zone::GetPath(std::string name) const {
void Zone::LoadSceneTransition(std::istream& file) { void Zone::LoadSceneTransition(std::istream& file) {
SceneTransition sceneTrans; SceneTransition sceneTrans;
if (m_FileFormatVersion < Zone::FileFormatVersion::Auramar) { if (m_FileFormatVersion < Zone::FileFormatVersion::Auramar) {
uint8_t length; BinaryIO::ReadString<uint8_t>(file, sceneTrans.name, BinaryIO::ReadType::String);
BinaryIO::BinaryRead(file, length);
sceneTrans.name = BinaryIO::ReadString(file, length);
BinaryIO::BinaryRead(file, sceneTrans.width); BinaryIO::BinaryRead(file, sceneTrans.width);
} }
//BR<42>THER MAY I HAVE SOME L<><4C>PS? //BR<42>THER MAY I HAVE SOME L<><4C>PS?
uint8_t loops = (m_FileFormatVersion <= Zone::FileFormatVersion::LatePreAlpha || m_FileFormatVersion >= Zone::FileFormatVersion::Launch) ? 2 : 5; uint8_t loops = (m_FileFormatVersion <= Zone::FileFormatVersion::LatePreAlpha || m_FileFormatVersion >= Zone::FileFormatVersion::Launch) ? 2 : 5;
sceneTrans.points.reserve(loops);
for (uint8_t i = 0; i < loops; ++i) { for (uint8_t i = 0; i < loops; ++i) {
sceneTrans.points.push_back(LoadSceneTransitionInfo(file)); sceneTrans.points.push_back(LoadSceneTransitionInfo(file));
} }
@ -388,13 +355,7 @@ void Zone::LoadPath(std::istream& file) {
BinaryIO::BinaryRead(file, path.pathVersion); BinaryIO::BinaryRead(file, path.pathVersion);
uint8_t stringLength; BinaryIO::ReadString<uint8_t>(file, path.pathName, BinaryIO::ReadType::WideString);
BinaryIO::BinaryRead(file, stringLength);
for (uint8_t i = 0; i < stringLength; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
path.pathName.push_back(character);
}
BinaryIO::BinaryRead(file, path.pathType); BinaryIO::BinaryRead(file, path.pathType);
BinaryIO::BinaryRead(file, path.flags); BinaryIO::BinaryRead(file, path.flags);
@ -404,13 +365,7 @@ void Zone::LoadPath(std::istream& file) {
if (path.pathVersion >= 18) { if (path.pathVersion >= 18) {
BinaryIO::BinaryRead(file, path.movingPlatform.timeBasedMovement); BinaryIO::BinaryRead(file, path.movingPlatform.timeBasedMovement);
} else if (path.pathVersion >= 13) { } else if (path.pathVersion >= 13) {
uint8_t count; BinaryIO::ReadString<uint8_t>(file, path.movingPlatform.platformTravelSound, BinaryIO::ReadType::WideString);
BinaryIO::BinaryRead(file, count);
for (uint8_t i = 0; i < count; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
path.movingPlatform.platformTravelSound.push_back(character);
}
} }
} else if (path.pathType == PathType::Property) { } else if (path.pathType == PathType::Property) {
BinaryIO::BinaryRead(file, path.property.pathType); BinaryIO::BinaryRead(file, path.property.pathType);
@ -419,20 +374,8 @@ void Zone::LoadPath(std::istream& file) {
BinaryIO::BinaryRead(file, path.property.associatedZone); BinaryIO::BinaryRead(file, path.property.associatedZone);
if (path.pathVersion >= 5) { if (path.pathVersion >= 5) {
uint8_t count1; BinaryIO::ReadString<uint8_t>(file, path.property.displayName, BinaryIO::ReadType::WideString);
BinaryIO::BinaryRead(file, count1); BinaryIO::ReadString<uint32_t>(file, path.property.displayDesc, BinaryIO::ReadType::WideString);
for (uint8_t i = 0; i < count1; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
path.property.displayName.push_back(character);
}
uint32_t count2;
BinaryIO::BinaryRead(file, count2);
for (uint8_t i = 0; i < count2; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
path.property.displayDesc.push_back(character);
}
} }
if (path.pathVersion >= 6) BinaryIO::BinaryRead(file, path.property.type); if (path.pathVersion >= 6) BinaryIO::BinaryRead(file, path.property.type);
@ -449,13 +392,7 @@ void Zone::LoadPath(std::istream& file) {
BinaryIO::BinaryRead(file, path.property.maxBuildHeight); BinaryIO::BinaryRead(file, path.property.maxBuildHeight);
} }
} else if (path.pathType == PathType::Camera) { } else if (path.pathType == PathType::Camera) {
uint8_t count; BinaryIO::ReadString<uint8_t>(file, path.camera.nextPath, BinaryIO::ReadType::WideString);
BinaryIO::BinaryRead(file, count);
for (uint8_t i = 0; i < count; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
path.camera.nextPath.push_back(character);
}
if (path.pathVersion >= 14) { if (path.pathVersion >= 14) {
BinaryIO::BinaryRead(file, path.camera.rotatePlayer); BinaryIO::BinaryRead(file, path.camera.rotatePlayer);
@ -472,7 +409,7 @@ void Zone::LoadPath(std::istream& file) {
// Read waypoints // Read waypoints
BinaryIO::BinaryRead(file, path.waypointCount); BinaryIO::BinaryRead(file, path.waypointCount);
path.pathWaypoints.reserve(path.waypointCount);
for (uint32_t i = 0; i < path.waypointCount; ++i) { for (uint32_t i = 0; i < path.waypointCount; ++i) {
PathWaypoint waypoint = PathWaypoint(); PathWaypoint waypoint = PathWaypoint();
@ -493,20 +430,8 @@ void Zone::LoadPath(std::istream& file) {
BinaryIO::BinaryRead(file, waypoint.movingPlatform.speed); BinaryIO::BinaryRead(file, waypoint.movingPlatform.speed);
BinaryIO::BinaryRead(file, waypoint.movingPlatform.wait); BinaryIO::BinaryRead(file, waypoint.movingPlatform.wait);
if (path.pathVersion >= 13) { if (path.pathVersion >= 13) {
uint8_t count1; BinaryIO::ReadString<uint8_t>(file, waypoint.movingPlatform.departSound, BinaryIO::ReadType::WideString);
BinaryIO::BinaryRead(file, count1); BinaryIO::ReadString<uint8_t>(file, waypoint.movingPlatform.arriveSound, BinaryIO::ReadType::WideString);
for (uint8_t i = 0; i < count1; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
waypoint.movingPlatform.departSound.push_back(character);
}
uint8_t count2;
BinaryIO::BinaryRead(file, count2);
for (uint8_t i = 0; i < count2; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
waypoint.movingPlatform.arriveSound.push_back(character);
}
} }
} else if (path.pathType == PathType::Camera) { } else if (path.pathType == PathType::Camera) {
BinaryIO::BinaryRead(file, waypoint.camera.time); BinaryIO::BinaryRead(file, waypoint.camera.time);
@ -529,22 +454,11 @@ void Zone::LoadPath(std::istream& file) {
uint32_t count; uint32_t count;
BinaryIO::BinaryRead(file, count); BinaryIO::BinaryRead(file, count);
for (uint32_t i = 0; i < count; ++i) { for (uint32_t i = 0; i < count; ++i) {
uint8_t count1;
std::string parameter; std::string parameter;
BinaryIO::ReadString<uint8_t>(file, parameter, BinaryIO::ReadType::WideString);
std::string value; std::string value;
BinaryIO::BinaryRead(file, count1); BinaryIO::ReadString<uint8_t>(file, value, BinaryIO::ReadType::WideString);
for (uint8_t i = 0; i < count1; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
parameter.push_back(character);
}
uint8_t count2;
BinaryIO::BinaryRead(file, count2);
for (uint8_t i = 0; i < count2; ++i) {
uint16_t character;
BinaryIO::BinaryRead(file, character);
value.push_back(character);
}
LDFBaseData* ldfConfig = nullptr; LDFBaseData* ldfConfig = nullptr;
if (path.pathType == PathType::Movement || path.pathType == PathType::Rail) { if (path.pathType == PathType::Movement || path.pathType == PathType::Rail) {

View File

@ -210,7 +210,6 @@ public:
void AddRevision(LWOSCENEID sceneID, uint32_t revision); void AddRevision(LWOSCENEID sceneID, uint32_t revision);
const LWOZONEID& GetZoneID() const { return m_ZoneID; } const LWOZONEID& GetZoneID() const { return m_ZoneID; }
const uint32_t GetChecksum() const { return m_CheckSum; } const uint32_t GetChecksum() const { return m_CheckSum; }
const void PrintAllGameObjects();
LUTriggers::Trigger* GetTrigger(uint32_t sceneID, uint32_t triggerID); LUTriggers::Trigger* GetTrigger(uint32_t sceneID, uint32_t triggerID);
const Path* GetPath(std::string name) const; const Path* GetPath(std::string name) const;
@ -228,7 +227,6 @@ public:
private: private:
LWOZONEID m_ZoneID; LWOZONEID m_ZoneID;
std::string m_ZoneFilePath; std::string m_ZoneFilePath;
uint32_t m_NumberOfScenesLoaded;
uint32_t m_NumberOfObjectsLoaded; uint32_t m_NumberOfObjectsLoaded;
uint32_t m_NumberOfSceneTransitionsLoaded; uint32_t m_NumberOfSceneTransitionsLoaded;
FileFormatVersion m_FileFormatVersion; FileFormatVersion m_FileFormatVersion;
@ -254,7 +252,7 @@ private:
//private ("helper") functions: //private ("helper") functions:
void LoadScene(std::istream& file); void LoadScene(std::istream& file);
std::vector<LUTriggers::Trigger*> LoadLUTriggers(std::string triggerFile, LWOSCENEID sceneID); void LoadLUTriggers(std::string triggerFile, SceneRef& scene);
void LoadSceneTransition(std::istream& file); void LoadSceneTransition(std::istream& file);
SceneTransitionInfo LoadSceneTransitionInfo(std::istream& file); SceneTransitionInfo LoadSceneTransitionInfo(std::istream& file);
void LoadPath(std::istream& file); void LoadPath(std::istream& file);

View File

@ -96,33 +96,6 @@ void dZoneManager::LoadZone(const LWOZONEID& zoneID) {
m_pZone = new Zone(zoneID.GetMapID(), zoneID.GetInstanceID(), zoneID.GetCloneID()); m_pZone = new Zone(zoneID.GetMapID(), zoneID.GetInstanceID(), zoneID.GetCloneID());
} }
void dZoneManager::NotifyZone(const dZoneNotifier& notifier, const LWOOBJID& objectID) {
switch (notifier) {
case dZoneNotifier::SpawnedObjectDestroyed:
break;
case dZoneNotifier::SpawnedChildObjectDestroyed:
break;
case dZoneNotifier::ReloadZone:
LOG("Forcing reload of zone %i", m_ZoneID.GetMapID());
LoadZone(m_ZoneID);
m_pZone->Initalize();
break;
case dZoneNotifier::UserJoined:
break;
case dZoneNotifier::UserMoved:
break;
case dZoneNotifier::PrintAllGameObjects:
m_pZone->PrintAllGameObjects();
break;
case dZoneNotifier::InvalidNotifier:
LOG("Got an invalid zone notifier.");
break;
default:
LOG("Unknown zone notifier: %i", int(notifier));
}
}
void dZoneManager::AddSpawner(LWOOBJID id, Spawner* spawner) { void dZoneManager::AddSpawner(LWOOBJID id, Spawner* spawner) {
m_Spawners.insert_or_assign(id, spawner); m_Spawners.insert_or_assign(id, spawner);
} }

View File

@ -14,7 +14,6 @@ public:
ReloadZone, //Forces the server and all connects clients to reload the map ReloadZone, //Forces the server and all connects clients to reload the map
UserJoined, UserJoined,
UserMoved, UserMoved,
PrintAllGameObjects, //Using this is a BAD idea in production
InvalidNotifier InvalidNotifier
}; };
@ -30,7 +29,6 @@ public:
Zone* GetZone(); //Gets a pointer to the currently loaded zone. Zone* GetZone(); //Gets a pointer to the currently loaded zone.
void LoadZone(const LWOZONEID& zoneID); //Discard the current zone (if any) and loads a new zone. void LoadZone(const LWOZONEID& zoneID); //Discard the current zone (if any) and loads a new zone.
void NotifyZone(const dZoneNotifier& notifier, const LWOOBJID& objectID); //Notifies the zone of a certain event or command.
void AddSpawner(LWOOBJID id, Spawner* spawner); void AddSpawner(LWOOBJID id, Spawner* spawner);
LWOZONEID GetZoneID() const; LWOZONEID GetZoneID() const;
LWOOBJID MakeSpawner(SpawnerInfo info); LWOOBJID MakeSpawner(SpawnerInfo info);