Example #1
0
void JSI_Lobby::LobbyBan(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring nick, std::wstring reason)
{
	if (!g_XmppClient)
		return;

	g_XmppClient->ban(utf8_from_wstring(nick), utf8_from_wstring(reason));
}
Example #2
0
void JSI_Lobby::StartRegisterXmppClient(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring username, std::wstring password)
{
	ENSURE(!g_XmppClient);

	g_XmppClient = IXmppClient::create(utf8_from_wstring(username), utf8_from_wstring(password),
		"", "", 0, true);
}
Example #3
0
void JSI_Lobby::StartXmppClient(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring username, std::wstring password, std::wstring room, std::wstring nick, int historyRequestSize)
{
	ENSURE(!g_XmppClient);

	g_XmppClient = IXmppClient::create(utf8_from_wstring(username), utf8_from_wstring(password),
		utf8_from_wstring(room), utf8_from_wstring(nick), historyRequestSize);
	g_rankedGame = true;
}
Example #4
0
void JSI_Lobby::LobbySetPlayerPresence(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring presence)
{
	if (!g_XmppClient)
		return;

	g_XmppClient->SetPresence(utf8_from_wstring(presence));
}
Example #5
0
void JSI_Lobby::LobbySendMessage(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring message)
{
	if (!g_XmppClient)
		return;
		
	g_XmppClient->SendMUCMessage(utf8_from_wstring(message));
}
Example #6
0
bool ScriptInterface::ParseJSON(const std::string& string_utf8, JS::MutableHandleValue out)
{
	JSAutoRequest rq(m->m_cx);
	std::wstring attrsW = wstring_from_utf8(string_utf8);
 	utf16string string(attrsW.begin(), attrsW.end());
	if (JS_ParseJSON(m->m_cx, reinterpret_cast<const jschar*>(string.c_str()), (u32)string.size(), out))
		return true;

	LOGERROR("JS_ParseJSON failed!");
	if (!JS_IsExceptionPending(m->m_cx))
		return false;

	JS::RootedValue exc(m->m_cx);
	if (!JS_GetPendingException(m->m_cx, &exc))
		return false;

	JS_ClearPendingException(m->m_cx);
	// We expect an object of type SyntaxError
	if (!exc.isObject())
		return false;

	JS::RootedValue rval(m->m_cx);
	JS::RootedObject excObj(m->m_cx, &exc.toObject());
	if (!JS_CallFunctionName(m->m_cx, excObj, "toString", JS::HandleValueArray::empty(), &rval))
		return false;

	std::wstring error;
	ScriptInterface::FromJSVal(m->m_cx, rval, error);
	LOGERROR("%s", utf8_from_wstring(error));
	return false;
}
Example #7
0
/**
 * Send a request to register a game to the server.
 *
 * @param data A JS array of game attributes
 */
void XmppClient::SendIqRegisterGame(ScriptInterface& scriptInterface, CScriptVal data)
{
	glooxwrapper::JID xpartamuppJid(m_xpartamuppId);
	jsval dataval = data.get();

	// Setup some base stanza attributes
	GameListQuery* g = new GameListQuery();
	g->m_Command = "register";
	glooxwrapper::Tag* game = glooxwrapper::Tag::allocate("game");
	// Add a fake ip which will be overwritten by the ip stamp XMPP module on the server.
	game->addAttribute("ip", "fake");

	// Iterate through all the properties reported and add them to the stanza.
	std::vector<std::string> properties;
	scriptInterface.EnumeratePropertyNamesWithPrefix(dataval, "", properties);
	for (std::vector<int>::size_type i = 0; i != properties.size(); i++)
	{
		std::wstring value;
		scriptInterface.GetProperty(dataval, properties[i].c_str(), value);
		game->addAttribute(properties[i], utf8_from_wstring(value));
	}

	// Push the stanza onto the IQ
	g->m_GameList.push_back(game);

	// Send IQ
	glooxwrapper::IQ iq(gloox::IQ::Set, xpartamuppJid);
	iq.addExtension(g);
	DbgXMPP("SendIqRegisterGame [" << tag_xml(iq) << "]");
	m_client->send(iq);
}
Example #8
0
/**
 * Send game report containing numerous game properties to the server.
 *
 * @param data A JS array of game statistics
 */
void XmppClient::SendIqGameReport(ScriptInterface& scriptInterface, CScriptVal data)
{
	glooxwrapper::JID xpartamuppJid(m_xpartamuppId);
	jsval dataval = data.get();

	// Setup some base stanza attributes
	GameReport* game = new GameReport();
	glooxwrapper::Tag* report = glooxwrapper::Tag::allocate("game");

	// Iterate through all the properties reported and add them to the stanza.
	std::vector<std::string> properties;
	scriptInterface.EnumeratePropertyNamesWithPrefix(dataval, "", properties);
	for (std::vector<int>::size_type i = 0; i != properties.size(); i++)
	{
		std::wstring value;
		scriptInterface.GetProperty(dataval, properties[i].c_str(), value);
		report->addAttribute(properties[i], utf8_from_wstring(value));
	}

	// Add stanza to IQ
	game->m_GameReport.push_back(report);

	// Send IQ
	glooxwrapper::IQ iq(gloox::IQ::Set, xpartamuppJid);
	iq.addExtension(game);
	DbgXMPP("SendGameReport [" << tag_xml(iq) << "]");
	m_client->send(iq);
};
Example #9
0
static std::vector<std::string> GetJSONData(const VfsPath& path)
{
	VfsPaths pathnames;
	Status ret = vfs::GetPathnames(g_VFS, path, L"*.json", pathnames);
	if (ret != INFO::OK)
	{
		// Some error reading directory
		wchar_t error[200];
		LOGERROR("Error reading directory '%s': %s", path.string8(), utf8_from_wstring(StatusDescription(ret, error, ARRAY_SIZE(error))));
		return std::vector<std::string>();
	}

	std::vector<std::string> data;
	for (const VfsPath& p : pathnames)
	{
		// Load JSON file
		CVFSFile file;
		PSRETURN ret = file.Load(g_VFS, p);
		if (ret != PSRETURN_OK)
		{
			LOGERROR("GetJSONData: Failed to load file '%s': %s", p.string8(), GetErrorString(ret));
			continue;
		}

		data.push_back(file.DecodeUTF8()); // assume it's UTF-8
	}

	return data;
}
Example #10
0
void CTemplateLoader::CopyMirageSubset(CParamNode& out, const CParamNode& in)
{
	// Currently used for mirage entities replacing real ones in fog-of-war

	std::set<std::string> permittedComponentTypes;
	permittedComponentTypes.insert("Footprint");
	permittedComponentTypes.insert("Minimap");
	permittedComponentTypes.insert("Ownership");
	permittedComponentTypes.insert("OverlayRenderer");
	permittedComponentTypes.insert("Position");
	permittedComponentTypes.insert("Selectable");
	permittedComponentTypes.insert("StatusBars");
	permittedComponentTypes.insert("Visibility");
	permittedComponentTypes.insert("VisualActor");

	CParamNode::LoadXMLString(out, "<Entity/>");
	out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);

	// Select a subset of identity data. We don't want to have, for example, a CC mirage
	// that has also the CC class and then prevents construction of other CCs
	std::set<std::string> identitySubset;
	identitySubset.insert("Civ");
	identitySubset.insert("GenericName");
	identitySubset.insert("SpecificName");
	identitySubset.insert("Tooltip");
	identitySubset.insert("History");
	identitySubset.insert("Icon");
	CParamNode identity;
	CParamNode::LoadXMLString(identity, "<Identity/>");
	identity.CopyFilteredChildrenOfChild(in.GetChild("Entity"), "Identity", identitySubset);
	CParamNode::LoadXMLString(out, ("<Entity>"+utf8_from_wstring(identity.ToXML())+"</Entity>").c_str());

	// Set the entity as mirage entity
	CParamNode::LoadXMLString(out, "<Entity><Mirage/></Entity>");
}
Example #11
0
void CTemplateLoader::ConstructTemplateActor(const std::string& actorName, CParamNode& out)
{
	// Load the base actor template if necessary
	const char* templateName = "special/actor";
	if (!LoadTemplateFile(templateName, 0))
	{
		LOGERROR("Failed to load entity template '%s'", templateName);
		return;
	}

	// Copy the actor template
	out = m_TemplateFileData[templateName];

	// Initialise the actor's name and make it an Atlas selectable entity.
	std::wstring actorNameW = wstring_from_utf8(actorName);
	std::string name = utf8_from_wstring(CParamNode::EscapeXMLString(actorNameW));
	std::string xml = "<Entity>"
	                      "<VisualActor><Actor>" + name + "</Actor><ActorOnly/></VisualActor>"
						  // arbitrary-sized Footprint definition to make actors' selection outlines show up in Atlas
						  "<Footprint><Circle radius='2.0'/><Height>1.0</Height></Footprint>"
	                      "<Selectable>"
	                          "<EditorOnly/>"
	                          "<Overlay><Texture><MainTexture>actor.png</MainTexture><MainTextureMask>actor_mask.png</MainTextureMask></Texture></Overlay>"
	                      "</Selectable>"
	                  "</Entity>";

	CParamNode::LoadXMLString(out, xml.c_str(), actorNameW.c_str());
}
Example #12
0
void CNetTurnManager::DisplayOOSError(u32 turn, const std::string& hash, const std::string& expectedHash, bool isReplay, OsPath* path = NULL)
{
	m_HasSyncError = true;

	std::stringstream msg;
	msg << "Out of sync on turn " << turn << ": expected hash " << expectedHash << "\n";

	if (expectedHash != hash || m_CurrentTurn != turn)
		msg << "\nCurrent state: turn " << m_CurrentTurn << ", hash " << hash << "\n\n";

	if (isReplay)
		msg << "\nThe current game state is different from the original game state.\n\n";
	else
	{
		if (expectedHash == hash)
			msg << "Your game state is identical to the hosts game state.\n\n";
		else
			msg << "Your game state is different from the hosts game state.\n\n";
	}

	if (path)
		msg << "Dumping current state to " << utf8_from_wstring(OsPath(*path).string());

	LOGERROR("%s", msg.str());

	if (g_GUI)
		g_GUI->DisplayMessageBox(600, 350, L"Sync error", wstring_from_utf8(msg.str()));
}
Example #13
0
	TDevice* create_rawinput_device(running_machine &machine, PRAWINPUTDEVICELIST rawinputdevice)
	{
		TDevice* devinfo;
		UINT name_length = 0;
		// determine the length of the device name, allocate it, and fetch it if not nameless
		if ((*get_rawinput_device_info)(rawinputdevice->hDevice, RIDI_DEVICENAME, nullptr, &name_length) != 0)
			return nullptr;

		std::unique_ptr<TCHAR[]> tname = std::make_unique<TCHAR[]>(name_length + 1);
		if (name_length > 1 && (*get_rawinput_device_info)(rawinputdevice->hDevice, RIDI_DEVICENAME, tname.get(), &name_length) == -1)
			return nullptr;

		// if this is an RDP name, skip it
		if (_tcsstr(tname.get(), TEXT("Root#RDP_")) != nullptr)
			return nullptr;

		// improve the name and then allocate a device
		std::wstring name = rawinput_device_improve_name(tname.get());

		// convert name to utf8
		auto osd_deleter = [](void *ptr) { osd_free(ptr); };
		auto utf8_name = std::unique_ptr<char, decltype(osd_deleter)>(utf8_from_wstring(name.c_str()), osd_deleter);

		devinfo = devicelist()->create_device<TDevice>(machine, utf8_name.get(), *this);

		// Add the handle
		devinfo->set_handle(rawinputdevice->hDevice);

		return devinfo;
	}
Example #14
0
void L10n::LoadListOfAvailableLocales()
{
	for (Locale* const& locale : availableLocales)
		delete locale;
	availableLocales.clear();

	Locale* defaultLocale = new Locale(Locale::getUS());
	availableLocales.push_back(defaultLocale); // Always available.

	VfsPaths filenames;
	if (vfs::GetPathnames(g_VFS, L"l10n/", L"*.po", filenames) < 0)
		return;

	for (const VfsPath& path : filenames)
	{
		// Note: PO files follow this naming convention: “l10n/<locale code>.<mod name>.po”. For example: “l10n/gl.public.po”.
		std::string filename = utf8_from_wstring(path.string()).substr(strlen("l10n/"));
		size_t lengthToFirstDot = filename.find('.');
		std::string localeCode = filename.substr(0, lengthToFirstDot);
		Locale* locale = new Locale(Locale::createCanonical(localeCode.c_str()));

		auto it = std::find_if(availableLocales.begin(), availableLocales.end(), [&locale](Locale* const& l) {
			return *locale == *l;
		});
		if (it != availableLocales.end())
		{
			delete locale;
			continue;
		}

		availableLocales.push_back(locale);
	}
}
Example #15
0
BOOL CALLBACK sdl_monitor_info::monitor_enum_callback(HMONITOR handle, HDC dc, LPRECT rect, LPARAM data)
{
	osd_monitor_info ***tailptr = (osd_monitor_info ***)data;
	osd_monitor_info *monitor;
	MONITORINFOEX info;
	BOOL result;

	// get the monitor info
	info.cbSize = sizeof(info);
	result = GetMonitorInfo(handle, (LPMONITORINFO)&info);
	assert(result);
	(void)result; // to silence gcc 4.6

	// guess the aspect ratio assuming square pixels
	float aspect = (float)(info.rcMonitor.right - info.rcMonitor.left) / (float)(info.rcMonitor.bottom - info.rcMonitor.top);

	// allocate a new monitor info
	char *temp = utf8_from_wstring(info.szDevice);
	// copy in the data
	monitor = global_alloc(sdl_monitor_info((UINT64) handle, temp, aspect));
	osd_free(temp);

	// hook us into the list
	**tailptr = monitor;
	*tailptr = &monitor->m_next;

	// enumerate all the available monitors so to list their names in verbose mode
	return TRUE;
}
Example #16
0
bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path)
{
	JSAutoRequest rq(m->m_cx);
	JS::RootedObject global(m->m_cx, m->m_glob);
	if (!VfsFileExists(path))
	{
		LOGERROR("File '%s' does not exist", path.string8());
		return false;
	}

	CVFSFile file;

	PSRETURN ret = file.Load(g_VFS, path);

	if (ret != PSRETURN_OK)
	{
		LOGERROR("Failed to load file '%s': %s", path.string8(), GetErrorString(ret));
		return false;
	}

	std::wstring code = wstring_from_utf8(file.DecodeUTF8()); // assume it's UTF-8

	utf16string codeUtf16(code.begin(), code.end());
	uint lineNo = 1;

	JS::RootedValue rval(m->m_cx);
	return JS_EvaluateUCScript(m->m_cx, global,
			reinterpret_cast<const jschar*> (codeUtf16.c_str()), (uint)(codeUtf16.length()),
			utf8_from_wstring(path.string()).c_str(), lineNo, &rval);
}
Example #17
0
bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path)
{
	if (!VfsFileExists(path))
	{
		LOGERROR(L"File '%ls' does not exist", path.string().c_str());
		return false;
	}

	CVFSFile file;

	PSRETURN ret = file.Load(g_VFS, path);

	if (ret != PSRETURN_OK)
	{
		LOGERROR(L"Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
		return false;
	}

	std::wstring code = wstring_from_utf8(file.DecodeUTF8()); // assume it's UTF-8

	// Compile the code in strict mode, to encourage better coding practices and
	// to possibly help SpiderMonkey with optimisations
	std::wstring codeStrict = L"\"use strict\";\n" + code;
	utf16string codeUtf16(codeStrict.begin(), codeStrict.end());
	uintN lineNo = 0; // put the automatic 'use strict' on line 0, so the real code starts at line 1

	jsval rval;
	JSBool ok = JS_EvaluateUCScript(m->m_cx, m->m_glob,
			reinterpret_cast<const jschar*> (codeUtf16.c_str()), (uintN)(codeUtf16.length()),
			utf8_from_wstring(path.string()).c_str(), lineNo, &rval);

	return ok ? true : false;
}
Example #18
0
void JSI_Lobby::LobbySetNick(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring nick)
{
	if (!g_XmppClient)
		return;

	g_XmppClient->SetNick(utf8_from_wstring(nick));
}
Example #19
0
bool ScriptInterface::LoadScript(const VfsPath& filename, const std::string& code)
{

	JSAutoRequest rq(m->m_cx);
	JS::RootedObject global(m->m_cx, m->m_glob);
	utf16string codeUtf16(code.begin(), code.end());
	uint lineNo = 1;
	// CompileOptions does not copy the contents of the filename string pointer.
	// Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary.
	std::string filenameStr(utf8_from_wstring(filename.string()));

	JS::CompileOptions options(m->m_cx);
	options.setFileAndLine(filenameStr.c_str(), lineNo);
	options.setCompileAndGo(true);

	JS::RootedFunction func(m->m_cx,
	JS_CompileUCFunction(m->m_cx, global, NULL, 0, NULL,
			reinterpret_cast<const jschar*> (codeUtf16.c_str()), (uint)(codeUtf16.length()), options)
	);
	if (!func)
		return false;

	JS::RootedValue rval(m->m_cx);
	return JS_CallFunction(m->m_cx, JS::NullPtr(), func, JS::HandleValueArray::empty(), &rval);
}
Example #20
0
// Return a translated version of the items in the specified array.
std::vector<std::wstring> JSI_L10n::TranslateArray(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::vector<std::wstring>& sourceArray)
{
	std::vector<std::wstring> translatedArray;
	for (const std::wstring& elem : sourceArray)
		translatedArray.push_back(wstring_from_utf8(g_L10n.Translate(utf8_from_wstring(elem))));

	return translatedArray;
}
Example #21
0
	static bool callback(const jschar* buf, u32 len, void* data)
	{
		utf16string str(buf, buf+len);
		std::wstring strw(str.begin(), str.end());

		Status err; // ignore Unicode errors
		static_cast<Stringifier*>(data)->stream << utf8_from_wstring(strw, &err);
		return true;
	}
Example #22
0
std::wstring JSI_Lobby::LobbyGetPlayerRole(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring nickname)
{
	if (!g_XmppClient)
		return L"";

	std::string role;
	g_XmppClient->GetRole(utf8_from_wstring(nickname), role);
	return wstring_from_utf8(role);
}
Example #23
0
Status sys_clipboard_set(const wchar_t* text)
{
	Status ret = INFO::OK;

	std::string str = utf8_from_wstring(text);
	bool ok = osx_SendStringToPasteboard(str);
	if (!ok)
		ret = ERR::FAIL;
	return ret;
}
Example #24
0
bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::wstring& code)
{
	JSAutoRequest rq(m->m_cx);
	JS::RootedObject global(m->m_cx, m->m_glob);
	utf16string codeUtf16(code.begin(), code.end());
	uint lineNo = 1;

	JS::RootedValue rval(m->m_cx);
	return JS_EvaluateUCScript(m->m_cx, global,
			reinterpret_cast<const jschar*> (codeUtf16.c_str()), (uint)(codeUtf16.length()),
			utf8_from_wstring(filename.string()).c_str(), lineNo, &rval);
}
Example #25
0
bool RelaxNGValidator::ValidateEncoded(const std::wstring& filename, const std::string& document) const
{
	TIMER_ACCRUE(xml_validation);

	if (!m_Schema)
	{
		LOGERROR("RelaxNGValidator: No grammar loaded");
		return false;
	}

	xmlDocPtr doc = xmlReadMemory(document.c_str(), (int)document.size(), utf8_from_wstring(filename).c_str(), NULL, XML_PARSE_NONET);
	if (doc == NULL)
	{
		LOGERROR("RelaxNGValidator: Failed to parse document '%s'", utf8_from_wstring(filename).c_str());
		return false;
	}

	bool ret = ValidateEncoded(doc);
	xmlFreeDoc(doc);
	return ret;
}
Example #26
0
File: CGUI.cpp Project: 2asoft/0ad
void CGUI::Xeromyces_ReadScript(XMBElement Element, CXeromyces* pFile, boost::unordered_set<VfsPath>& Paths)
{
	// Check for a 'file' parameter
	CStrW file(Element.GetAttributes().GetNamedItem(pFile->GetAttributeID("file")).FromUTF8());

	// If there is a file specified, open and execute it
	if (!file.empty())
	{
		Paths.insert(file);
		try
		{
			m_ScriptInterface->LoadGlobalScriptFile(file);
		}
		catch (PSERROR_Scripting& e)
		{
			LOGERROR("GUI: Error executing script %s: %s", utf8_from_wstring(file), e.what());
		}
	}

	// If it has a directory attribute, read all JS files in that directory
	CStrW directory(Element.GetAttributes().GetNamedItem(pFile->GetAttributeID("directory")).FromUTF8());
	if (!directory.empty())
	{
		VfsPaths pathnames;
		vfs::GetPathnames(g_VFS, directory, L"*.js", pathnames);
		for (const VfsPath& path : pathnames)
		{
			// Only load new files (so when the insert succeeds)
			if (Paths.insert(path).second)
				try
				{
					m_ScriptInterface->LoadGlobalScriptFile(path);
				}
				catch (PSERROR_Scripting& e)
				{
					LOGERROR("GUI: Error executing script %s: %s", path.string8(), e.what());
				}
		}
	}

	// Execute inline scripts
	try
	{
		CStr code(Element.GetText());
		if (!code.empty())
			m_ScriptInterface->LoadGlobalScript(L"Some XML file", code.FromUTF8());
	}
	catch (PSERROR_Scripting& e)
	{
		LOGERROR("GUI: Error executing inline script: %s", e.what());
	}
}
Example #27
0
// get HDATA for the given handle.
// also verifies the type.
// used by most functions accessing handle data.
static Status h_data_tag_type(const Handle h, const H_Type type, HDATA*& hd)
{
	RETURN_STATUS_IF_ERR(h_data_tag(h, hd));

	// h_alloc makes sure type isn't 0, so no need to check that here.
	if(hd->type != type)
	{
		debug_printf("h_mgr: expected type %s, got %s\n", utf8_from_wstring(hd->type->name).c_str(), utf8_from_wstring(type->name).c_str());
		WARN_RETURN(ERR::H_TYPE_MISMATCH);
	}

	return INFO::OK;
}
Example #28
0
Status CGUIManager::ReloadChangedFile(const VfsPath& path)
{
	for (PageStackType::iterator it = m_PageStack.begin(); it != m_PageStack.end(); ++it)
	{
		if (it->inputs.count(path))
		{
			LOGMESSAGE("GUI file '%s' changed - reloading page '%s'", path.string8(), utf8_from_wstring(it->name));
			LoadPage(*it);
			// TODO: this can crash if LoadPage runs an init script which modifies the page stack and breaks our iterators
		}
	}

	return INFO::OK;
}
Example #29
0
bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::string& code)
{
	// Compile the code in strict mode, to encourage better coding practices and
	// to possibly help SpiderMonkey with optimisations
	std::wstring codeStrict = L"\"use strict\";\n" + wstring_from_utf8(code);
	utf16string codeUtf16(codeStrict.begin(), codeStrict.end());
	uintN lineNo = 0; // put the automatic 'use strict' on line 0, so the real code starts at line 1

	jsval rval;
	JSBool ok = JS_EvaluateUCScript(m->m_cx, m->m_glob,
			reinterpret_cast<const jschar*> (codeUtf16.c_str()), (uintN)(codeUtf16.length()),
			utf8_from_wstring(filename.string()).c_str(), lineNo, &rval);

	return ok ? true : false;
}
Example #30
0
entity_id_t CComponentManager::AddEntity(const std::wstring& templateName, entity_id_t ent)
{
	ICmpTemplateManager *cmpTemplateManager = static_cast<ICmpTemplateManager*> (QueryInterface(SYSTEM_ENTITY, IID_TemplateManager));
	if (!cmpTemplateManager)
	{
		debug_warn(L"No ICmpTemplateManager loaded");
		return INVALID_ENTITY;
	}

	// TODO: should assert that ent doesn't exist

	const CParamNode* tmpl = cmpTemplateManager->LoadTemplate(ent, utf8_from_wstring(templateName), -1);
	if (!tmpl)
		return INVALID_ENTITY; // LoadTemplate will have reported the error

	CEntityHandle handle = AllocateEntityHandle(ent);

	// Construct a component for each child of the root element
	const CParamNode::ChildrenMap& tmplChilds = tmpl->GetChildren();
	for (CParamNode::ChildrenMap::const_iterator it = tmplChilds.begin(); it != tmplChilds.end(); ++it)
	{
		// Ignore attributes on the root element
		if (it->first.length() && it->first[0] == '@')
			continue;

		CComponentManager::ComponentTypeId cid = LookupCID(it->first);
		if (cid == CID__Invalid)
		{
			LOGERROR(L"Unrecognised component type name '%hs' in entity template '%ls'", it->first.c_str(), templateName.c_str());
			return INVALID_ENTITY;
		}

		if (!AddComponent(handle, cid, it->second))
		{
			LOGERROR(L"Failed to construct component type name '%hs' in entity template '%ls'", it->first.c_str(), templateName.c_str());
			return INVALID_ENTITY;
		}
		// TODO: maybe we should delete already-constructed components if one of them fails?
	}

	CMessageCreate msg(ent);
	PostMessage(ent, msg);

	return ent;
}