Skip to content

Commit 8caaf02

Browse files
authored
Merge pull request #58 from CapsCollective/feature/distributed-scenes
Implemented distributed scene format
2 parents 75aa68b + a351fbe commit 8caaf02

36 files changed

+551
-333
lines changed

engine/core/entity/EntityPtr.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class EntityPtr
3030
explicit EntityPtr(E* pointer) : pointer(pointer)
3131
{
3232
static_assert(std::is_base_of_v<Entity, E>);
33-
if (pointer) index = pointer->GetIndex();
33+
InitialiseIndex();
3434
}
3535

3636
// Operator overloads
@@ -101,6 +101,15 @@ class EntityPtr
101101
index = other ? other->GetIndex() : GenerationalIndex();
102102
}
103103

104+
/**
105+
* Attempts to initialise the stored generational index
106+
* based on the current entity pointer
107+
*/
108+
void InitialiseIndex()
109+
{
110+
if (pointer) index = pointer->GetIndex();
111+
}
112+
104113
private:
105114

106115
// Private fields

engine/core/entity/EntitySystem.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ bool EntitySystem::IsLive(Entity* entity)
6262
{
6363
EntitySystem* system = FindInGlobalRegister(entity);
6464
if (system) return system->IsLive(entity->GetIndex());
65-
CC_LOG_WARNING("Could not find storage for {} at {}",
66-
entity->GetName(),
67-
entity->GetIndex().ToString());
65+
CC_LOG_WARNING("Could not find storage for provided entity");
6866
return false;
6967
}
7068

engine/core/scene/SceneFile.cpp

Lines changed: 132 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
#include <algorithm>
1515

1616
#include "../ResourceSystem.h"
17-
#include "../entity/Entity.h"
1817

1918
namespace Siege
2019
{
@@ -27,73 +26,155 @@ void SceneFile::RegisterSerialisable(const String& name,
2726

2827
bool SceneFile::Serialise(const std::vector<Entity*>& entities)
2928
{
30-
return FileSystem::Save(MakeScenePath(sceneName), SerialiseToString(entities));
29+
// Create the required scene file directory
30+
FileSystem::CreateDirectoryRecursive(MakeScenePath(sceneName));
31+
32+
// Flush any removed entities from disk
33+
for (const auto& pair : entityPaths)
34+
{
35+
if (!pair.first) FileSystem::Remove(pair.second);
36+
}
37+
38+
bool succeeded = true;
39+
for (auto& entity : entities)
40+
{
41+
String fileData;
42+
bool result = SerialiseToString(entity, fileData);
43+
44+
if (!result)
45+
{
46+
succeeded = false;
47+
continue;
48+
}
49+
50+
String filepath = GetOrCreateEntityFilepath(entity);
51+
FileSystem::Save(filepath, fileData);
52+
entityPaths[EntityPtr(entity)] = filepath;
53+
}
54+
return succeeded;
3155
}
3256

33-
String SceneFile::SerialiseToString(const std::vector<Entity*>& entities)
57+
bool SceneFile::SerialiseToString(Entity* entity, String& fileData)
3458
{
3559
// Iterate over each entity in the scene
36-
String fileData;
3760
auto& serialisables = GetSerialisables();
38-
for (auto& entity : entities)
61+
62+
// Only serialise entities that register a serialisable interface
63+
auto it = serialisables.find(entity->GetName());
64+
if (it == serialisables.end()) return false;
65+
66+
// Serialise the general entity information
67+
fileData += (entity->GetName() + LINE_SEP);
68+
fileData += DefineField("POSITION", ToString(entity->GetPosition()));
69+
fileData += DefineField("ROTATION", String::FromFloat(entity->GetRotation().y));
70+
fileData += DefineField("Z-INDEX", String::FromInt(entity->GetZIndex()));
71+
72+
// Apply its serialiser if it
73+
Serialiser serialiser = it->second.first;
74+
if (serialiser) fileData += serialiser(entity);
75+
76+
return true;
77+
}
78+
79+
bool SceneFile::Deserialise(std::vector<Entity*>& entities)
80+
{
81+
// Clear out the held entity paths before repopulating
82+
entityPaths.clear();
83+
84+
bool succeeded = true;
85+
auto deserialiseEntity = [this, &entities, &succeeded](const std::filesystem::path& path) {
86+
if (path.extension() != ENTITY_FILE_EXT) return;
87+
88+
String fileData = FileSystem::Read(path.c_str());
89+
Entity* newEntity = DeserialiseFromString(fileData);
90+
91+
if (!newEntity)
92+
{
93+
succeeded = false;
94+
return;
95+
}
96+
97+
entities.push_back(newEntity);
98+
entityPaths[EntityPtr(newEntity)] = path.c_str();
99+
};
100+
bool result = FileSystem::ForEachFileInDir(MakeScenePath(sceneName), deserialiseEntity);
101+
return succeeded && result;
102+
}
103+
104+
Entity* SceneFile::DeserialiseFromString(const String& fileData)
105+
{
106+
if (fileData.IsEmpty())
39107
{
40-
// Only serialise entities that register a serialisable interface
41-
auto it = serialisables.find(entity->GetName());
42-
if (it == serialisables.end()) continue;
43-
44-
// Serialise the general entity information
45-
fileData += (entity->GetName() + SEP);
46-
fileData += DefineField("POSITION", ToString(entity->GetPosition()));
47-
fileData += DefineField("ROTATION", String::FromFloat(entity->GetRotation().y));
48-
fileData += DefineField("Z-INDEX", String::FromInt(entity->GetZIndex()));
49-
50-
// Apply its serialiser if it
51-
Serialiser serialiser = it->second.first;
52-
if (serialiser) fileData += serialiser(entity);
53-
54-
// End the serialisation entry
55-
fileData += "\n";
108+
CC_LOG_WARNING("Found empty entity during deserialisation");
109+
return nullptr;
56110
}
57-
return fileData;
111+
112+
// Split the file into arguments and strip the labels from each item
113+
std::vector<String> args = fileData.Split(LINE_SEP);
114+
for (String& arg : args) arg = arg.SubString((int) arg.Find(NAME_SEP) + 1);
115+
116+
// Get the standard entity fields
117+
EntityData data;
118+
if (!(args.size() >= 4 && args[ENTITY_ROT].GetFloat(data.rotation) &&
119+
args[ENTITY_Z_IDX].GetInt(data.zIndex) && FromString(data.position, args[ENTITY_POS])))
120+
{
121+
CC_LOG_WARNING("Failed to deserialise fields for entity \"{}\"", args[ENTITY_NAME]);
122+
}
123+
124+
// Check if the entity has a relevant serialisable interface registered
125+
auto& serialisables = GetSerialisables();
126+
auto it = serialisables.find(args[ENTITY_NAME]);
127+
if (it != serialisables.end())
128+
{
129+
// Apply its deserialiser
130+
Deserialiser deserialiser = it->second.second;
131+
if (deserialiser) return deserialiser(data, args);
132+
}
133+
else CC_LOG_WARNING("\"{}\" has no deserialisation protocols defined", args[ENTITY_NAME]);
134+
return nullptr;
58135
}
59136

60-
bool SceneFile::Deserialise(std::vector<Entity*>& entities)
137+
const String& SceneFile::GetSceneName()
61138
{
62-
// Iterate over each line of the file
63-
String lines = FileSystem::Read(MakeScenePath(sceneName));
64-
if (!lines) return false;
65-
DeserialiseLines(lines.Split('\n'), entities);
66-
return true;
139+
return sceneName;
140+
}
141+
142+
void SceneFile::SetSceneName(const String& name)
143+
{
144+
sceneName = name;
67145
}
68146

69-
void SceneFile::DeserialiseLines(const std::vector<String>& lines, std::vector<Entity*>& entities)
147+
void SceneFile::InitialiseEntityPathMappings()
70148
{
71-
for (const String& line : lines)
149+
for (std::pair<EntityPtr<Entity>, String> pair : entityPaths)
72150
{
73-
// Split the line into arguments and strip the labels from each item
74-
std::vector<String> args = line.Split(SEP);
75-
for (String& arg : args) arg = arg.SubString((int) arg.Find(NAME_SEP) + 1);
76-
77-
// Get the standard entity fields
78-
EntityData data;
79-
if (!(args.size() >= 4 && args[ENTITY_ROT].GetFloat(data.rotation) &&
80-
args[ENTITY_Z_IDX].GetInt(data.zIndex) &&
81-
FromString(data.position, args[ENTITY_POS])))
82-
{
83-
CC_LOG_WARNING("Failed to deserialise fields for entity \"{}\"", args[ENTITY_NAME]);
84-
}
151+
pair.first.InitialiseIndex();
152+
}
153+
}
154+
155+
String SceneFile::GetOrCreateEntityFilepath(Entity* entity)
156+
{
157+
// Try to find the entity path amongst the deserialised
158+
auto it = entityPaths.find(EntityPtr(entity));
159+
if (it != entityPaths.end()) return it->second;
85160

86-
// Check if the entity has a relevant serialisable interface registered
87-
auto& serialisables = GetSerialisables();
88-
auto it = serialisables.find(args[ENTITY_NAME]);
89-
if (it != serialisables.end())
161+
// Search through the scene file for the next available file index
162+
int newFileIndex = 1;
163+
auto findNextFileIndex = [&newFileIndex](const std::filesystem::path& path) {
164+
if (path.extension() != ENTITY_FILE_EXT) return;
165+
std::vector<String> filename = String(path.filename().c_str()).Split('.');
166+
167+
int nameIndex;
168+
if (filename[1].GetInt(nameIndex) && nameIndex >= newFileIndex)
90169
{
91-
// Apply its deserialiser
92-
Deserialiser deserialiser = it->second.second;
93-
if (deserialiser) entities.push_back(deserialiser(data, args));
170+
newFileIndex = nameIndex + 1;
94171
}
95-
else CC_LOG_WARNING("\"{}\" has no deserialisation protocols defined", args[ENTITY_NAME]);
96-
}
172+
};
173+
bool result = FileSystem::ForEachFileInDir(MakeScenePath(sceneName), findNextFileIndex);
174+
175+
// Failed attempts to find a file index are serialised as 0
176+
String index = result ? String::FromInt(newFileIndex) : "0";
177+
return MakeScenePath(sceneName) + '/' + entity->GetName() + '.' + index + ENTITY_FILE_EXT;
97178
}
98179

99180
String SceneFile::MakeScenePath(const String& sceneName)

engine/core/scene/SceneFile.h

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include <utility>
1919
#include <vector>
2020

21+
#include "core/entity/EntityPtr.h"
22+
2123
// Define macros
2224
#define REGISTER_SERIALISATION_INTERFACE(name, serialiser, deserialiser) \
2325
static Siege::SerialisationInterfaceRegisterer CONCAT_SYMBOL(_si_reg_, __LINE__)(name, \
@@ -26,9 +28,10 @@
2628
namespace Siege
2729
{
2830
// Define constants
29-
static constexpr const char SEP = '|';
31+
static constexpr const char LINE_SEP = '\n';
3032
static constexpr const char NAME_SEP = ':';
3133
static constexpr const char* SCENE_FILE_EXT = ".scene";
34+
static constexpr const char* ENTITY_FILE_EXT = ".entity";
3235

3336
// Define types
3437
typedef struct EntityData EntityData;
@@ -87,12 +90,12 @@ class SceneFile
8790
bool Serialise(const std::vector<class Entity*>& entities);
8891

8992
/**
90-
* Serialises a list of entities into the scene file format as
91-
* a string
92-
* @param entities - the list of entities to serialise
93-
* @return a string representation of the scene
93+
* Serialises an entity into the scene file format as a string
94+
* @param entity - the entity to serialise
95+
* @param fileData - a string to populate with the serialised data
96+
* @return true if the operation was successful
9497
*/
95-
static String SerialiseToString(const std::vector<Entity*>& entities);
98+
static bool SerialiseToString(Entity* entity, OUT String& fileData);
9699

97100
/**
98101
* Deserialises a scene file's content into a list of
@@ -104,19 +107,47 @@ class SceneFile
104107
bool Deserialise(OUT std::vector<Entity*>& entities);
105108

106109
/**
107-
* Deserialises a scene file's content from a vector
108-
* of strings
109-
* @param lines - strings for each line of the scene file
110-
* @param entities - a reference to a vector of entity
111-
* pointers to populate
110+
* Deserialises an entity from a given string
111+
* @param fileData - a string representing a serialised entity
112+
* @return a pointer to the deserialised entity
113+
*/
114+
static Entity* DeserialiseFromString(const String& fileData);
115+
116+
/**
117+
* Returns the name of the currently set scene
118+
* @return the scene name
119+
*/
120+
const String& GetSceneName();
121+
122+
/**
123+
* Replaces the currently set scene name
124+
* @param name - the new scene name as a string
112125
*/
113-
static void DeserialiseLines(const std::vector<String>& lines,
114-
OUT std::vector<Entity*>& entities);
126+
void SetSceneName(const String& name);
127+
128+
/**
129+
* Lifetime method required to properly initialise entity path
130+
* mappings so that they can be used in cross-referencing on
131+
* re-serialisation.
132+
*
133+
* Should be called after the entities have had their generational
134+
* index initialised.
135+
*/
136+
void InitialiseEntityPathMappings();
115137

116138
private:
117139

118140
// Private methods
119141

142+
/**
143+
* Finds or generates a filepath for a given entity, first performing
144+
* a lookup across active entity path mappings, then finding the next
145+
* available entity file index to create.
146+
* @param entity - the entity to generate a filepath for
147+
* @return a filepath as a string
148+
*/
149+
String GetOrCreateEntityFilepath(Entity* entity);
150+
120151
/**
121152
* Creates a filepath to the specified scene relative to
122153
* the base resource directory
@@ -141,6 +172,11 @@ class SceneFile
141172
* The name of the scene file
142173
*/
143174
String sceneName;
175+
176+
/**
177+
* Path mappings from entity files to entity pointers
178+
*/
179+
std::map<EntityPtr<Entity>, String> entityPaths;
144180
};
145181

146182
/**
@@ -202,7 +238,7 @@ struct SerialisationInterfaceRegisterer
202238
*/
203239
inline String DefineField(const String& name, const String& content)
204240
{
205-
return name + NAME_SEP + content + SEP;
241+
return name + NAME_SEP + content + LINE_SEP;
206242
}
207243
} // namespace Siege
208244

0 commit comments

Comments
 (0)