/*
GameEngine and Editor
by John Ryland
Copyright (c) 2023
*/
////////////////////////////////////////////////////////////////////////////////////
// ECS System
#include "EcsSystem.h"
#include "XmlParser.h"
#include "SchemaEdit.h"
#include "Utils.h"
#include "Utilities.h"
#include <cassert>
#include <cstring>
namespace GameEngine {
// virtual
EcsSystem::EcsSystem()
{
}
// virtual
EcsSystem::~EcsSystem()
{
}
// virtual
void EcsSystem::Initialize()
{
}
// virtual
void EcsSystem::Shutdown()
{
}
// virtual
void EcsSystem::Prepare()
{
}
// virtual
void EcsSystem::Update()
{
}
// virtual
void EcsSystem::Render()
{
}
// virtual
void EcsSystem::Present()
{
}
const EntityRef& EcsSystem::GetEntity(EntityId entity) const
{
return entities.at(entity);
}
const ComponentType& EcsSystem::GetComponentType(ComponentTypeId componentType) const
{
return components.at(componentType);
}
struct SceneObject
{
EntityId Self;
EntityId Parent;
EntityId FirstChild;
EntityId NextSibling;
char Name[64];
uint64_t Layer;
};
void EcsSystem::LoadEntitiesFromXml(XmlTag* rootTag)
{
printf("Root tag: %s\n", rootTag->tag.c_str());
assert(rootTag->tag == "root");
printf("Root tag: %s\n", rootTag->children[0].tag.c_str());
for (auto& child : rootTag->children)
{
assert(child.tag == "xml");
for (auto& tag : child.children)
{
if (tag.tag == "entity")
{
assert(tag.attribValues.size() == 1);
printf("Entity%s:\n", tag.attribValues["id"].c_str());
uint64_t id = StringToUInt64(tag.attribValues["id"]);
auto& entRef = entities[id];
entRef.id = id;
for (auto& comp : tag.children)
{
ComponentTypeId compId = FindComponentTypeByName(comp.tag);
if (compId == INVALID_COMPONENT_TYPE_ID)
{
printf(" %s NOT FOUND\n", comp.tag.c_str());
}
else
{
// TODO: instead of malloc, the data should come from the component type's component pool
void* entData = malloc(components[compId].size); // TODO: leaking at the moment - temp until we have component data pools working
memset(entData, 0, components[compId].size);
entRef.components[compId] = entData;
auto& compType = GetComponentType(compId);
for (int i = 0; i < compType.fields.numberOfFields; ++i)
{
auto& varDesc = schema->variables->variables[compType.fields.variableDescriptionTableOffset + i];
auto varName = ConvertRuntimeString(schema, varDesc.name);
bool found = false;
if (varDesc.variableType == VariableTypeEnum::E_Struct)
{
for (auto& child : comp.children)
{
if (child.tag == varName)
found = true;
}
}
if (!found && !comp.attribValues.count(varName))
{
printf("WARNING: Can't find the field '%s' of the component type '%s' defined for entity '%s' (defaulting to 0)\n",
varName.c_str(), comp.tag.c_str(), tag.attribValues["id"].c_str());
}
else if (!found)
{
auto varVal = comp.attribValues[varName];
switch (varDesc.variableType)
{
case VariableTypeEnum::E_Void:
break;
case VariableTypeEnum::E_Bool:
if (varVal == "1" || varVal == "Yes" || varVal == "YES" || varVal == "yes"
|| varVal == "True" || varVal == "TRUE" || varVal == "true"
|| varVal == "On" || varVal == "ON" || varVal == "on")
*(uint8_t*)entData = 1;
break;
case VariableTypeEnum::E_Enum:
{
NameValue* enumValues = schema->enums->values + varDesc.enumDescription.nameValueTableOffset;
uint64_t maxEnumValue = 0ULL;
uint64_t setEnumValue = 0ULL;
for (int i = 0; i < varDesc.enumDescription.numberOfEnumValues; ++i)
{
if (enumValues[i].value > maxEnumValue)
maxEnumValue = enumValues[i].value;
if (varVal == ConvertRuntimeString(schema, enumValues[i].name))
setEnumValue = enumValues[i].value;
}
// If use indirection instead then could do this:
// maxEnumValue = enumDesc.numberOfEnumValues;
if (maxEnumValue <= UINT8_MAX)
*(uint8_t*)entData = setEnumValue;
else if (maxEnumValue <= UINT16_MAX)
*(uint16_t*)entData = setEnumValue;
else if (maxEnumValue <= UINT32_MAX)
*(uint32_t*)entData = setEnumValue;
else
*(uint64_t*)entData = setEnumValue;
break;
}
case VariableTypeEnum::E_String:
::strncpy((char*)entData, varVal.c_str(), varDesc.stringDescription.maximumCharacterCount);
break;
case VariableTypeEnum::E_Number:
switch (varDesc.numberDescription.numberType)
{
case E_UInt8: *(uint8_t*) entData = Utilities::to_uint32(varVal); break;
case E_UInt16: *(uint16_t*)entData = Utilities::to_uint32(varVal); break;
case E_UInt32: *(uint32_t*)entData = Utilities::to_uint32(varVal); break;
case E_UInt64: *(uint64_t*)entData = Utilities::to_uint64(varVal); break;
case E_Int8: *(int8_t*) entData = Utilities::to_int32(varVal); break;
case E_Int16: *(int16_t*) entData = Utilities::to_int32(varVal); break;
case E_Int32: *(int32_t*) entData = Utilities::to_int32(varVal); break;
case E_Int64: *(int64_t*) entData = Utilities::to_int64(varVal); break;
case E_Float64: *(double*) entData = Utilities::to_double(varVal); break;
case E_Float32:
case E_Angle:
case E_Ratio: *(float*) entData = Utilities::to_float(varVal); break;
}
break;
case VariableTypeEnum::E_Color:
switch (varDesc.colorDescription.colorType)
{
case E_RGBA32: *(uint32_t*)entData = Utilities::to_uint32(varVal); break;
case E_RGB32: *(uint32_t*)entData = Utilities::to_uint32(varVal); break;
}
break;
case VariableTypeEnum::E_Entity:
*(uint64_t*)entData = StringToUInt64(varVal);
break;
case VariableTypeEnum::E_Asset:
break;
case VariableTypeEnum::E_Struct:
break;
case VariableTypeEnum::E_Component:
break;
}
}
entData = (uint8_t*)entData + CalculateFieldSize(*schema, (size_t)entData, varDesc);
}
/*
if (comp.tag == "SceneObject")
{
assert(components[compId].size == sizeof(SceneObject));
SceneObject* sceneObj = (SceneObject*)entData;
sceneObj->Self = StringToUInt64(comp.attribValues["Self"]);
sceneObj->Parent = StringToUInt64(comp.attribValues["Parent"]);
sceneObj->FirstChild = StringToUInt64(comp.attribValues["FirstChild"]);
sceneObj->NextSibling = StringToUInt64(comp.attribValues["NextSibling"]);
::strncpy(sceneObj->Name, comp.attribValues["Name"].c_str(), 64);
sceneObj->Layer = StringToUInt64(comp.attribValues["Layer"]);
}
*/
printf(" %s\n", comp.tag.c_str());
}
}
}
else
{
printf("Error, unknown tag type: %s\n", tag.tag.c_str());
}
}
}
}
void EcsSystem::SetSchema(GameRuntime::Schema* _schema)
{
schema = _schema;
// Populate the component types from the schema
for (int i = 0; i < schema->variables->count; ++i)
{
auto& varDesc = schema->variables->variables[i];
if (varDesc.variableType == GameRuntime::VariableTypeEnum::E_Component)
{
++lastUsedComponentTypeId;
auto& compType = components[lastUsedComponentTypeId];
compType.name = ConvertRuntimeString(schema, varDesc.name);
compType.fields = varDesc.componentDescription;
compType.id = lastUsedComponentTypeId;
compType.size = CalculateComponentSize(*schema, compType.fields);
compType.enabled = true;
}
}
#ifndef _NDEBUG
// Debug
StringList comps = AllComponentTypeNames();
for (auto& comp : comps)
printf("Component: %s\n", comp.c_str());
#endif
}
void EcsSystem::LoadScene(const char* sceneFilename)
{
// Load entities
XmlTag root;
root.tag = "root";
if (ParseXmlFile(sceneFilename, &root))
{
LoadEntitiesFromXml(&root);
}
else
{
printf("Error loading the scene\n");
}
}
void EcsSystem::RegisterComponentType()
{
}
StringList EcsSystem::AllComponentTypeNames() const
{
StringList compTypeNames;
for (auto& compType : components)
{
if (compType.second.enabled)
compTypeNames.emplace_back(compType.second.name);
}
return compTypeNames;
}
ComponentTypeId EcsSystem::FindComponentTypeByName(String componentName) const
{
for (auto& compType : components)
{
if (compType.second.enabled && compType.second.name == componentName)
return compType.second.id;
}
return INVALID_COMPONENT_TYPE_ID;
}
Array<EntityId> EcsSystem::AllEntities() const
{
Array<EntityId> ents;
for (auto& ent : entities)
ents.push_back(ent.first);
return ents;
}
// TODO: this should be more efficient - should be a direct way to do this
Array<EntityId> EcsSystem::FindEntitiesWithComponentType(ComponentTypeId componentType) const
{
Array<EntityId> ents;
for (auto& ent : entities)
{
if (ent.second.components.count(componentType))
ents.push_back(ent.first);
}
return ents;
}
StringList EcsSystem::EntityComponents(EntityId entity) const
{
StringList compTypeNames;
for (auto& compType : entities.at(entity).components)
compTypeNames.emplace_back(components.at(compType.first).name);
return compTypeNames;
}
// unused
void EcsSystem::NewEntity()
{
}
// unused
void EcsSystem::NewComponent()
{
}
// unused
void EcsSystem::RemoveStaleComponentReferences()
{
for (auto& compType : components)
{
if (!compType.second.enabled)
{
for (auto& ent : entities)
{
if (ent.second.components.count(compType.second.id))
{
free(ent.second.components[compType.second.id]);
ent.second.components.erase(compType.second.id);
}
}
}
}
}
} // GameEngine namespace