1
0
forked from 0ad/0ad

Convert CCmpTemplateManager to use ASCII strings internally, to save memory and serialisation cost.

This was SVN commit r7577.
This commit is contained in:
Ykkrosh 2010-05-25 18:17:12 +00:00
parent a9963dee56
commit 6fdb947ff0
8 changed files with 90 additions and 82 deletions

View File

@ -259,6 +259,19 @@ template <> void XMLWriter_File::ElementAttribute<CStr>(const char* name, const
{
ElementAttribute(name, value.c_str(), newelement);
}
template <> void XMLWriter_File::ElementAttribute<std::string>(const char* name, const std::string& value, bool newelement)
{
ElementAttribute(name, value.c_str(), newelement);
}
// Encode Unicode strings as UTF-8
template <> void XMLWriter_File::ElementAttribute<CStrW>(const char* name, const CStrW& value, bool newelement)
{
ElementAttribute(name, value.ToUTF8(), newelement);
}
template <> void XMLWriter_File::ElementAttribute<std::wstring>(const char* name, const std::wstring& value, bool newelement)
{
ElementAttribute(name, utf8_from_wstring(value).c_str(), newelement);
}
// Use CStr's conversion for most types:
#define TYPE2(ID_T, ARG_T) \
@ -272,13 +285,3 @@ TYPE(int)
TYPE(unsigned int)
TYPE(float)
TYPE(double)
// Encode Unicode strings as UTF-8
template <> void XMLWriter_File::ElementAttribute<CStrW>(const char* name, const CStrW& value, bool newelement)
{
ElementAttribute(name, value.ToUTF8(), newelement);
}
template <> void XMLWriter_File::ElementAttribute<std::wstring>(const char* name, const std::wstring& value, bool newelement)
{
ElementAttribute(name, utf8_from_wstring(value).c_str(), newelement);
}

View File

@ -61,7 +61,7 @@ public:
{
size_t count = 0;
for (std::map<entity_id_t, std::wstring>::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it)
for (std::map<entity_id_t, std::string>::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it)
{
if (ENTITY_IS_LOCAL(it->first))
continue;
@ -69,13 +69,14 @@ public:
}
serialize.NumberU32_Unbounded("num entities", (u32)count);
for (std::map<entity_id_t, std::wstring>::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it)
for (std::map<entity_id_t, std::string>::const_iterator it = m_LatestTemplates.begin(); it != m_LatestTemplates.end(); ++it)
{
if (ENTITY_IS_LOCAL(it->first))
continue;
serialize.NumberU32_Unbounded("id", it->first);
serialize.String("template", it->second, 0, 256);
serialize.StringASCII("template", it->second, 0, 256);
}
// TODO: maybe we should do some kind of interning thing instead of printing so many strings?
// TODO: will need to serialize techs too, because we need to be giving out
// template data before other components (like the tech components) have been deserialized
@ -90,9 +91,9 @@ public:
for (u32 i = 0; i < numEntities; ++i)
{
entity_id_t ent;
std::wstring templateName;
std::string templateName;
deserialize.NumberU32_Unbounded(ent);
deserialize.String(templateName, 0, 256);
deserialize.StringASCII(templateName, 0, 256);
m_LatestTemplates[ent] = templateName;
}
}
@ -113,13 +114,13 @@ public:
}
}
virtual const CParamNode* LoadTemplate(entity_id_t ent, const std::wstring& templateName, int playerID);
virtual const CParamNode* LoadTemplate(entity_id_t ent, const std::string& templateName, int playerID);
virtual const CParamNode* GetTemplate(std::wstring templateName);
virtual const CParamNode* GetTemplate(std::string templateName);
virtual const CParamNode* LoadLatestTemplate(entity_id_t ent);
virtual std::wstring GetCurrentTemplateName(entity_id_t ent);
virtual std::string GetCurrentTemplateName(entity_id_t ent);
virtual std::vector<std::wstring> FindAllTemplates();
@ -131,26 +132,26 @@ private:
// loaded non-broken template data. This includes files that will fail schema validation.
// (Failed loads won't remove existing entries under the same name, so we behave more nicely
// when hotloading broken files)
std::map<std::wstring, CParamNode> m_TemplateFileData;
std::map<std::string, CParamNode> m_TemplateFileData;
// Map from template name to schema validation status.
// (Some files, e.g. inherited parent templates, may not be valid themselves but we still need to load
// them and use them; we only reject invalid templates that were requested directly by GetTemplate/etc)
std::map<std::wstring, bool> m_TemplateSchemaValidity;
std::map<std::string, bool> m_TemplateSchemaValidity;
// Remember the template used by each entity, so we can return them
// again for deserialization.
// TODO: should store player ID etc.
std::map<entity_id_t, std::wstring> m_LatestTemplates;
std::map<entity_id_t, std::string> m_LatestTemplates;
// (Re)loads the given template, regardless of whether it exists already,
// and saves into m_TemplateFileData. Also loads any parents that are not yet
// loaded. Returns false on error.
// @param templateName XML filename to load (not a |-separated string)
bool LoadTemplateFile(const std::wstring& templateName, int depth);
bool LoadTemplateFile(const std::string& templateName, int depth);
// Constructs a standard static-decorative-object template for the given actor
void ConstructTemplateActor(const std::wstring& actorName, CParamNode& out);
void ConstructTemplateActor(const std::string& actorName, CParamNode& out);
// Copy the non-interactive components of an entity template (position, actor, etc) into
// a new entity template
@ -163,7 +164,7 @@ private:
REGISTER_COMPONENT_TYPE(TemplateManager)
const CParamNode* CCmpTemplateManager::LoadTemplate(entity_id_t ent, const std::wstring& templateName, int UNUSED(playerID))
const CParamNode* CCmpTemplateManager::LoadTemplate(entity_id_t ent, const std::string& templateName, int UNUSED(playerID))
{
m_LatestTemplates[ent] = templateName;
@ -176,18 +177,18 @@ const CParamNode* CCmpTemplateManager::LoadTemplate(entity_id_t ent, const std::
return templateRoot;
}
const CParamNode* CCmpTemplateManager::GetTemplate(std::wstring templateName)
const CParamNode* CCmpTemplateManager::GetTemplate(std::string templateName)
{
// Load the template if necessary
if (!LoadTemplateFile(templateName, 0))
{
LOGERROR(L"Failed to load entity template '%ls'", templateName.c_str());
LOGERROR(L"Failed to load entity template '%hs'", templateName.c_str());
return NULL;
}
// Compute validity, if it's not computed before
if (m_TemplateSchemaValidity.find(templateName) == m_TemplateSchemaValidity.end())
m_TemplateSchemaValidity[templateName] = m_Validator.Validate(templateName, m_TemplateFileData[templateName].ToXML());
m_TemplateSchemaValidity[templateName] = m_Validator.Validate(CStrW(templateName), m_TemplateFileData[templateName].ToXML());
// Refuse to return invalid templates
if (!m_TemplateSchemaValidity[templateName])
return NULL;
@ -195,7 +196,7 @@ const CParamNode* CCmpTemplateManager::GetTemplate(std::wstring templateName)
const CParamNode& templateRoot = m_TemplateFileData[templateName].GetChild("Entity");
if (!templateRoot.IsOk())
{
LOGERROR(L"Invalid root element in entity template '%ls'", templateName.c_str());
LOGERROR(L"Invalid root element in entity template '%hs'", templateName.c_str());
return NULL;
}
// TODO: the template ought to be validated with some schema, so we don't
@ -206,21 +207,21 @@ const CParamNode* CCmpTemplateManager::GetTemplate(std::wstring templateName)
const CParamNode* CCmpTemplateManager::LoadLatestTemplate(entity_id_t ent)
{
std::map<entity_id_t, std::wstring>::const_iterator it = m_LatestTemplates.find(ent);
std::map<entity_id_t, std::string>::const_iterator it = m_LatestTemplates.find(ent);
if (it == m_LatestTemplates.end())
return NULL;
return LoadTemplate(ent, it->second, -1);
}
std::wstring CCmpTemplateManager::GetCurrentTemplateName(entity_id_t ent)
std::string CCmpTemplateManager::GetCurrentTemplateName(entity_id_t ent)
{
std::map<entity_id_t, std::wstring>::const_iterator it = m_LatestTemplates.find(ent);
std::map<entity_id_t, std::string>::const_iterator it = m_LatestTemplates.find(ent);
if (it == m_LatestTemplates.end())
return L"";
return "";
return it->second;
}
bool CCmpTemplateManager::LoadTemplateFile(const std::wstring& templateName, int depth)
bool CCmpTemplateManager::LoadTemplateFile(const std::string& templateName, int depth)
{
// If this file was already loaded, we don't need to do anything
if (m_TemplateFileData.find(templateName) != m_TemplateFileData.end())
@ -229,25 +230,25 @@ bool CCmpTemplateManager::LoadTemplateFile(const std::wstring& templateName, int
// Handle infinite loops more gracefully than running out of stack space and crashing
if (depth > 100)
{
LOGERROR(L"Probable infinite inheritance loop in entity template '%ls'", templateName.c_str());
LOGERROR(L"Probable infinite inheritance loop in entity template '%hs'", templateName.c_str());
return false;
}
// Handle special case "actor|foo"
if (templateName.find(L"actor|") == 0)
if (templateName.find("actor|") == 0)
{
ConstructTemplateActor(templateName.substr(6), m_TemplateFileData[templateName]);
return true;
}
// Handle special case "preview|foo"
if (templateName.find(L"preview|") == 0)
if (templateName.find("preview|") == 0)
{
// Load the base entity template, if it wasn't already loaded
std::wstring baseName = templateName.substr(8);
std::string baseName = templateName.substr(8);
if (!LoadTemplateFile(baseName, depth+1))
{
LOGERROR(L"Failed to load entity template '%ls'", baseName.c_str());
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
return false;
}
// Copy a subset to the requested template
@ -256,13 +257,13 @@ bool CCmpTemplateManager::LoadTemplateFile(const std::wstring& templateName, int
}
// Handle special case "foundation|foo"
if (templateName.find(L"foundation|") == 0)
if (templateName.find("foundation|") == 0)
{
// Load the base entity template, if it wasn't already loaded
std::wstring baseName = templateName.substr(11);
std::string baseName = templateName.substr(11);
if (!LoadTemplateFile(baseName, depth+1))
{
LOGERROR(L"Failed to load entity template '%ls'", baseName.c_str());
LOGERROR(L"Failed to load entity template '%hs'", baseName.c_str());
return false;
}
// Copy a subset to the requested template
@ -272,7 +273,7 @@ bool CCmpTemplateManager::LoadTemplateFile(const std::wstring& templateName, int
// Normal case: templateName is an XML file:
VfsPath path = VfsPath(TEMPLATE_ROOT) / (templateName + L".xml");
VfsPath path = VfsPath(TEMPLATE_ROOT) / (std::wstring)CStrW(templateName + ".xml");
CXeromyces xero;
PSRETURN ok = xero.Load(path);
if (ok != PSRETURN_OK)
@ -282,19 +283,19 @@ bool CCmpTemplateManager::LoadTemplateFile(const std::wstring& templateName, int
utf16string parentStr = xero.GetRoot().GetAttributes().GetNamedItem(attr_parent);
if (!parentStr.empty())
{
std::wstring parentName(parentStr.begin(), parentStr.end());
std::string parentName(parentStr.begin(), parentStr.end());
// To prevent needless complexity in template design, we don't allow |-separated strings as parents
if (parentName.find('|') != parentName.npos)
{
LOGERROR(L"Invalid parent '%ls' in entity template '%ls'", parentName.c_str(), templateName.c_str());
LOGERROR(L"Invalid parent '%hs' in entity template '%hs'", parentName.c_str(), templateName.c_str());
return false;
}
// Ensure the parent is loaded
if (!LoadTemplateFile(parentName, depth+1))
{
LOGERROR(L"Failed to load parent '%ls' of entity template '%ls'", parentName.c_str(), templateName.c_str());
LOGERROR(L"Failed to load parent '%hs' of entity template '%hs'", parentName.c_str(), templateName.c_str());
return false;
}
@ -310,9 +311,9 @@ bool CCmpTemplateManager::LoadTemplateFile(const std::wstring& templateName, int
return true;
}
void CCmpTemplateManager::ConstructTemplateActor(const std::wstring& actorName, CParamNode& out)
void CCmpTemplateManager::ConstructTemplateActor(const std::string& actorName, CParamNode& out)
{
std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(actorName));
std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(CStrW(actorName)));
std::string xml = "<?xml version='1.0' encoding='utf-8'?>"
"<Entity>"
"<Position>"

View File

@ -22,7 +22,7 @@
#include "simulation2/system/InterfaceScripted.h"
BEGIN_INTERFACE_WRAPPER(TemplateManager)
DEFINE_INTERFACE_METHOD_1("GetTemplate", const CParamNode*, ICmpTemplateManager, GetTemplate, std::wstring)
DEFINE_INTERFACE_METHOD_1("GetCurrentTemplateName", std::wstring, ICmpTemplateManager, GetCurrentTemplateName, entity_id_t)
DEFINE_INTERFACE_METHOD_1("GetTemplate", const CParamNode*, ICmpTemplateManager, GetTemplate, std::string)
DEFINE_INTERFACE_METHOD_1("GetCurrentTemplateName", std::string, ICmpTemplateManager, GetCurrentTemplateName, entity_id_t)
DEFINE_INTERFACE_METHOD_0("FindAllTemplates", std::vector<std::wstring>, ICmpTemplateManager, FindAllTemplates)
END_INTERFACE_WRAPPER(TemplateManager)

View File

@ -25,6 +25,10 @@
/**
* Template manager: Handles the loading of entity template files for the initialisation
* and deserialization of entity components.
*
* Template names are intentionally restricted to ASCII strings for storage/serialization
* efficiency (we have a lot of strings so this is significant);
* they correspond to filenames so they shouldn't contain non-ASCII anyway.
*/
class ICmpTemplateManager : public IComponent
{
@ -49,7 +53,7 @@ public:
*
* @return NULL on error
*/
virtual const CParamNode* LoadTemplate(entity_id_t ent, const std::wstring& templateName, int playerID) = 0;
virtual const CParamNode* LoadTemplate(entity_id_t ent, const std::string& templateName, int playerID) = 0;
/**
* Loads the template XML file identified by 'templateName' (including inheritance
@ -57,7 +61,7 @@ public:
*
* @return NULL on error
*/
virtual const CParamNode* GetTemplate(std::wstring templateName) = 0;
virtual const CParamNode* GetTemplate(std::string templateName) = 0;
/**
* Returns the template most recently specified for the entity 'ent'.
@ -70,7 +74,7 @@ public:
/**
* Returns the name of the template most recently specified for the entity 'ent'.
*/
virtual std::wstring GetCurrentTemplateName(entity_id_t ent) = 0;
virtual std::string GetCurrentTemplateName(entity_id_t ent) = 0;
/**
* Returns a list of strings that could be validly passed as @c templateName to LoadTemplate.

View File

@ -559,7 +559,7 @@ entity_id_t CComponentManager::AddEntity(const std::wstring& templateName, entit
// TODO: should assert that ent doesn't exist
const CParamNode* tmpl = tempMan->LoadTemplate(ent, templateName, -1);
const CParamNode* tmpl = tempMan->LoadTemplate(ent, CStr8(templateName), -1);
if (!tmpl)
return INVALID_ENTITY; // LoadTemplate will have reported the error

View File

@ -63,31 +63,31 @@ public:
ICmpTemplateManager* tempMan = static_cast<ICmpTemplateManager*> (man.QueryInterface(ent1, IID_TemplateManager));
TS_ASSERT(tempMan != NULL);
const CParamNode* basic = tempMan->LoadTemplate(ent2, L"basic", -1);
const CParamNode* basic = tempMan->LoadTemplate(ent2, "basic", -1);
TS_ASSERT(basic != NULL);
TS_ASSERT_WSTR_EQUALS(basic->ToXML(), L"<Test1A>12345</Test1A>");
const CParamNode* inherit2 = tempMan->LoadTemplate(ent2, L"inherit2", -1);
const CParamNode* inherit2 = tempMan->LoadTemplate(ent2, "inherit2", -1);
TS_ASSERT(inherit2 != NULL);
TS_ASSERT_WSTR_EQUALS(inherit2->ToXML(), L"<Test1A a=\"a2\" b=\"b1\" c=\"c1\"><d>d2</d><e>e1</e><f>f1</f><g>g2</g></Test1A>");
const CParamNode* inherit1 = tempMan->LoadTemplate(ent2, L"inherit1", -1);
const CParamNode* inherit1 = tempMan->LoadTemplate(ent2, "inherit1", -1);
TS_ASSERT(inherit1 != NULL);
TS_ASSERT_WSTR_EQUALS(inherit1->ToXML(), L"<Test1A a=\"a1\" b=\"b1\" c=\"c1\"><d>d1</d><e>e1</e><f>f1</f></Test1A>");
const CParamNode* actor = tempMan->LoadTemplate(ent2, L"actor|example1", -1);
const CParamNode* actor = tempMan->LoadTemplate(ent2, "actor|example1", -1);
TS_ASSERT(actor != NULL);
TS_ASSERT_WSTR_EQUALS(actor->ToXML(), L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position><VisualActor><Actor>example1</Actor></VisualActor>");
const CParamNode* preview = tempMan->LoadTemplate(ent2, L"preview|unit", -1);
const CParamNode* preview = tempMan->LoadTemplate(ent2, "preview|unit", -1);
TS_ASSERT(preview != NULL);
TS_ASSERT_WSTR_EQUALS(preview->ToXML(), L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position><VisualActor><Actor>example</Actor></VisualActor>");
const CParamNode* previewobstruct = tempMan->LoadTemplate(ent2, L"preview|unitobstruct", -1);
const CParamNode* previewobstruct = tempMan->LoadTemplate(ent2, "preview|unitobstruct", -1);
TS_ASSERT(previewobstruct != NULL);
TS_ASSERT_WSTR_EQUALS(previewobstruct->ToXML(), L"<Footprint><Circle radius=\"4\"></Circle><Height>1.0</Height></Footprint><Obstruction><Inactive></Inactive><Unit radius=\"4\"></Unit></Obstruction><Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position><VisualActor><Actor>example</Actor></VisualActor>");
const CParamNode* previewactor = tempMan->LoadTemplate(ent2, L"preview|actor|example2", -1);
const CParamNode* previewactor = tempMan->LoadTemplate(ent2, "preview|actor|example2", -1);
TS_ASSERT(previewactor != NULL);
TS_ASSERT_WSTR_EQUALS(previewactor->ToXML(), L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position><VisualActor><Actor>example2</Actor></VisualActor>");
}
@ -108,19 +108,19 @@ public:
TestLogger logger;
TS_ASSERT(tempMan->LoadTemplate(ent2, L"illformed", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "illformed", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, L"invalid", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "invalid", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, L"nonexistent", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "nonexistent", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, L"inherit-loop", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-loop", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, L"inherit-broken", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-broken", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, L"inherit-special", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-special", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, L"preview|nonexistent", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "preview|nonexistent", -1) == NULL);
}
void test_LoadTemplate_multiple()
@ -137,28 +137,28 @@ public:
ICmpTemplateManager* tempMan = static_cast<ICmpTemplateManager*> (man.QueryInterface(ent1, IID_TemplateManager));
TS_ASSERT(tempMan != NULL);
const CParamNode* basicA = tempMan->LoadTemplate(ent2, L"basic", -1);
const CParamNode* basicA = tempMan->LoadTemplate(ent2, "basic", -1);
TS_ASSERT(basicA != NULL);
const CParamNode* basicB = tempMan->LoadTemplate(ent2, L"basic", -1);
const CParamNode* basicB = tempMan->LoadTemplate(ent2, "basic", -1);
TS_ASSERT(basicA == basicB);
const CParamNode* inherit2A = tempMan->LoadTemplate(ent2, L"inherit2", -1);
const CParamNode* inherit2A = tempMan->LoadTemplate(ent2, "inherit2", -1);
TS_ASSERT(inherit2A != NULL);
const CParamNode* inherit2B = tempMan->LoadTemplate(ent2, L"inherit2", -1);
const CParamNode* inherit2B = tempMan->LoadTemplate(ent2, "inherit2", -1);
TS_ASSERT(inherit2A == inherit2B);
TestLogger logger;
TS_ASSERT(tempMan->LoadTemplate(ent2, L"nonexistent", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, L"nonexistent", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "nonexistent", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "nonexistent", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, L"inherit-loop", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, L"inherit-loop", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-loop", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-loop", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, L"inherit-broken", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, L"inherit-broken", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-broken", -1) == NULL);
TS_ASSERT(tempMan->LoadTemplate(ent2, "inherit-broken", -1) == NULL);
}
};
@ -196,7 +196,7 @@ public:
{
std::wstring name = templates[i];
printf("# %ls\n", name.c_str());
const CParamNode* p = cmpTempMan->GetTemplate(name);
const CParamNode* p = cmpTempMan->GetTemplate(CStr8(name));
TS_ASSERT(p != NULL);
}
}

View File

@ -725,7 +725,7 @@ public:
TS_ASSERT(man.AddComponent(SYSTEM_ENTITY, CID_TemplateManager, noParam));
ICmpTemplateManager* tempMan = static_cast<ICmpTemplateManager*> (man.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager));
const CParamNode* testParam = tempMan->LoadTemplate(ent2, L"template-serialize", -1);
const CParamNode* testParam = tempMan->LoadTemplate(ent2, "template-serialize", -1);
man.AddComponent(ent2, man.LookupCID("TestScript1_consts"), testParam->GetChild("TestScript1_consts"));

View File

@ -591,7 +591,7 @@ BEGIN_COMMAND(DeleteObject)
{
// Saved copy of the important aspects of a unit, to allow undo
entity_id_t m_EntityID;
std::wstring m_TemplateName;
std::string m_TemplateName;
int32_t m_Owner;
CFixedVector3D m_Pos;
CFixedVector3D m_Rot;
@ -633,9 +633,9 @@ BEGIN_COMMAND(DeleteObject)
void Undo()
{
CSimulation2& sim = *g_Game->GetSimulation2();
entity_id_t ent = sim.AddEntity(m_TemplateName, m_EntityID);
entity_id_t ent = sim.AddEntity(CStrW(m_TemplateName), m_EntityID);
if (ent == INVALID_ENTITY)
LOGERROR(L"Failed to load entity template '%ls'", m_TemplateName.c_str());
LOGERROR(L"Failed to load entity template '%hs'", m_TemplateName.c_str());
else
{
CmpPtr<ICmpPosition> cmpPosition(sim, m_EntityID);