CScriptValRooted ScriptInterface::ParseJSON(const std::string& string_utf8) { std::wstring attrsW = wstring_from_utf8(string_utf8); utf16string string(attrsW.begin(), attrsW.end()); jsval vp; JSONParser* parser = JS_BeginJSONParse(m->m_cx, &vp); if (!parser) { LOGERROR(L"ParseJSON failed to begin"); return CScriptValRooted(); } if (!JS_ConsumeJSONText(m->m_cx, parser, reinterpret_cast<const jschar*>(string.c_str()), (uint32)string.size())) { LOGERROR(L"ParseJSON failed to consume"); return CScriptValRooted(); } if (!JS_FinishJSONParse(m->m_cx, parser, JSVAL_NULL)) { LOGERROR(L"ParseJSON failed to finish"); return CScriptValRooted(); } return CScriptValRooted(m->m_cx, vp); }
void CComponentManager::RegisterComponentType(InterfaceId iid, ComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc, const char* name, const std::string& schema) { ComponentType c = { CT_Native, iid, alloc, dealloc, name, schema, CScriptValRooted() }; m_ComponentTypesById.insert(std::make_pair(cid, c)); m_ComponentTypeIdsByName[name] = cid; }
jsval CMessage::ToJSValCached(ScriptInterface& scriptInterface) const { if (m_Cached.uninitialised()) m_Cached = CScriptValRooted(scriptInterface.GetContext(), ToJSVal(scriptInterface)); return m_Cached.get(); }
bool Initialise(bool callConstructor) { if (!LoadScripts(m_AIName)) return false; OsPath path = L"simulation/ai/" + m_AIName + L"/data.json"; CScriptValRooted metadata = m_Worker.LoadMetadata(path); if (metadata.uninitialised()) { LOGERROR(L"Failed to create AI player: can't find %ls", path.string().c_str()); return false; } // Get the constructor name from the metadata std::string constructor; if (!m_ScriptInterface.GetProperty(metadata.get(), "constructor", constructor)) { LOGERROR(L"Failed to create AI player: %ls: missing 'constructor'", path.string().c_str()); return false; } // Get the constructor function from the loaded scripts CScriptVal ctor; if (!m_ScriptInterface.GetProperty(m_ScriptInterface.GetGlobalObject(), constructor.c_str(), ctor) || ctor.undefined()) { LOGERROR(L"Failed to create AI player: %ls: can't find constructor '%hs'", path.string().c_str(), constructor.c_str()); return false; } CScriptVal obj; if (callConstructor) { // Set up the data to pass as the constructor argument CScriptVal settings; m_ScriptInterface.Eval(L"({})", settings); m_ScriptInterface.SetProperty(settings.get(), "player", m_Player, false); ENSURE(m_Worker.m_HasLoadedEntityTemplates); m_ScriptInterface.SetProperty(settings.get(), "templates", m_Worker.m_EntityTemplates, false); obj = m_ScriptInterface.CallConstructor(ctor.get(), settings.get()); } else { // For deserialization, we want to create the object with the correct prototype // but don't want to actually run the constructor again // XXX: actually we don't currently use this path for deserialization - maybe delete it? obj = m_ScriptInterface.NewObjectFromConstructor(ctor.get()); } if (obj.undefined()) { LOGERROR(L"Failed to create AI player: %ls: error calling constructor '%hs'", path.string().c_str(), constructor.c_str()); return false; } m_Obj = CScriptValRooted(m_ScriptInterface.GetContext(), obj); return true; }
void CGUIManager::PushPage(const CStrW& pageName, CScriptVal initData) { m_PageStack.push_back(SGUIPage()); m_PageStack.back().name = pageName; m_PageStack.back().initData = CScriptValRooted(m_ScriptInterface.GetContext(), initData); LoadPage(m_PageStack.back()); }
static std::vector<SimulationCommand> CloneCommandsFromOtherContext(ScriptInterface& oldScript, ScriptInterface& newScript, const std::vector<SimulationCommand>& commands) { std::vector<SimulationCommand> newCommands = commands; for (size_t i = 0; i < newCommands.size(); ++i) { newCommands[i].data = CScriptValRooted(newScript.GetContext(), newScript.CloneValueFromOtherContext(oldScript, newCommands[i].data.get())); } return newCommands; }
CScriptValRooted ScriptInterface::ReadJSONFile(const VfsPath& path) { if (!VfsFileExists(path)) { LOGERROR(L"File '%ls' does not exist", path.string().c_str()); return CScriptValRooted(); } 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 CScriptValRooted(); } std::string content(file.DecodeUTF8()); // assume it's UTF-8 return ParseJSON(content); }
CMessage* CComponentManager::ConstructMessage(int mtid, CScriptVal data) { if (mtid == MT__Invalid || mtid > (int)m_MessageTypeIdsByName.size()) // (IDs start at 1 so use '>' here) LOGERROR(L"PostMessage with invalid message type ID '%d'", mtid); if (mtid < MT__LastNative) { return CMessageFromJSVal(mtid, m_ScriptInterface, data.get()); } else { return new CMessageScripted(mtid, m_MessageTypeNamesById[mtid], CScriptValRooted(m_ScriptInterface.GetContext(), data)); } }
/** * Send GUI message queue when queried. */ CScriptValRooted XmppClient::GuiPollMessage(ScriptInterface& scriptInterface) { if (m_GuiMessageQueue.empty()) return CScriptValRooted(); GUIMessage message = m_GuiMessageQueue.front(); CScriptValRooted messageVal; scriptInterface.Eval("({})", messageVal); scriptInterface.SetProperty(messageVal.get(), "type", message.type); if (!message.from.empty()) scriptInterface.SetProperty(messageVal.get(), "from", message.from); if (!message.text.empty()) scriptInterface.SetProperty(messageVal.get(), "text", message.text); if (!message.level.empty()) scriptInterface.SetProperty(messageVal.get(), "level", message.level); if (!message.message.empty()) scriptInterface.SetProperty(messageVal.get(), "message", message.message); if (!message.data.empty()) scriptInterface.SetProperty(messageVal.get(), "data", message.data); m_GuiMessageQueue.pop_front(); return messageVal; }
void CComponentManager::Script_RegisterComponentType(void* cbdata, int iid, std::string cname, CScriptVal ctor) { CComponentManager* componentManager = static_cast<CComponentManager*> (cbdata); // Find the C++ component that wraps the interface int cidWrapper = componentManager->GetScriptWrapper(iid); if (cidWrapper == CID__Invalid) { componentManager->m_ScriptInterface.ReportError("Invalid interface id"); return; } const ComponentType& ctWrapper = componentManager->m_ComponentTypesById[cidWrapper]; bool mustReloadComponents = false; // for hotloading ComponentTypeId cid = componentManager->LookupCID(cname); if (cid == CID__Invalid) { // Allocate a new cid number cid = componentManager->m_NextScriptComponentTypeId++; componentManager->m_ComponentTypeIdsByName[cname] = cid; } else { // Component type is already loaded, so do hotloading: if (!componentManager->m_CurrentlyHotloading) { componentManager->m_ScriptInterface.ReportError("Registering component type with already-registered name"); // TODO: report the actual name return; } const ComponentType& ctPrevious = componentManager->m_ComponentTypesById[cid]; // We can only replace scripted component types, not native ones if (ctPrevious.type != CT_Script) { componentManager->m_ScriptInterface.ReportError("Hotloading script component type with same name as native component"); return; } // We don't support changing the IID of a component type (it would require fiddling // around with m_ComponentsByInterface and being careful to guarantee uniqueness per entity) if (ctPrevious.iid != iid) { // ...though it only matters if any components exist with this type if (!componentManager->m_ComponentsByTypeId[cid].empty()) { componentManager->m_ScriptInterface.ReportError("Hotloading script component type mustn't change interface ID"); return; } } // Remove the old component type's message subscriptions std::map<MessageTypeId, std::vector<ComponentTypeId> >::iterator it; for (it = componentManager->m_LocalMessageSubscriptions.begin(); it != componentManager->m_LocalMessageSubscriptions.end(); ++it) { std::vector<ComponentTypeId>& types = it->second; std::vector<ComponentTypeId>::iterator ctit = find(types.begin(), types.end(), cid); if (ctit != types.end()) types.erase(ctit); } for (it = componentManager->m_GlobalMessageSubscriptions.begin(); it != componentManager->m_GlobalMessageSubscriptions.end(); ++it) { std::vector<ComponentTypeId>& types = it->second; std::vector<ComponentTypeId>::iterator ctit = find(types.begin(), types.end(), cid); if (ctit != types.end()) types.erase(ctit); } mustReloadComponents = true; } std::string schema = "<empty/>"; { CScriptValRooted prototype; if (componentManager->m_ScriptInterface.GetProperty(ctor.get(), "prototype", prototype) && componentManager->m_ScriptInterface.HasProperty(prototype.get(), "Schema")) { componentManager->m_ScriptInterface.GetProperty(prototype.get(), "Schema", schema); } } // Construct a new ComponentType, using the wrapper's alloc functions ComponentType ct = { CT_Script, iid, ctWrapper.alloc, ctWrapper.dealloc, cname, schema, CScriptValRooted(componentManager->m_ScriptInterface.GetContext(), ctor) }; componentManager->m_ComponentTypesById[cid] = ct; componentManager->m_CurrentComponent = cid; // needed by Subscribe // Find all the ctor prototype's On* methods, and subscribe to the appropriate messages: CScriptVal proto; if (!componentManager->m_ScriptInterface.GetProperty(ctor.get(), "prototype", proto)) return; // error std::vector<std::string> methods; if (!componentManager->m_ScriptInterface.EnumeratePropertyNamesWithPrefix(proto.get(), "On", methods)) return; // error for (std::vector<std::string>::const_iterator it = methods.begin(); it != methods.end(); ++it) { std::string name = (*it).substr(2); // strip the "On" prefix // Handle "OnGlobalFoo" functions specially bool isGlobal = false; if (name.substr(0, 6) == "Global") { isGlobal = true; name = name.substr(6); } std::map<std::string, MessageTypeId>::const_iterator mit = componentManager->m_MessageTypeIdsByName.find(name); if (mit == componentManager->m_MessageTypeIdsByName.end()) { std::string msg = "Registered component has unrecognised '" + *it + "' message handler method"; componentManager->m_ScriptInterface.ReportError(msg.c_str()); return; } if (isGlobal) componentManager->SubscribeGloballyToMessageType(mit->second); else componentManager->SubscribeToMessageType(mit->second); } componentManager->m_CurrentComponent = CID__Invalid; if (mustReloadComponents) { // For every script component with this cid, we need to switch its // prototype from the old constructor's prototype property to the new one's const std::map<entity_id_t, IComponent*>& comps = componentManager->m_ComponentsByTypeId[cid]; std::map<entity_id_t, IComponent*>::const_iterator eit = comps.begin(); for (; eit != comps.end(); ++eit) { jsval instance = eit->second->GetJSInstance(); if (!JSVAL_IS_NULL(instance)) componentManager->m_ScriptInterface.SetPrototype(instance, proto.get()); } } }
template<> bool ScriptInterface::FromJSVal<CScriptValRooted>(JSContext* cx, jsval v, CScriptValRooted& out) { out = CScriptValRooted(cx, v); return true; }
bool Initialise(bool callConstructor) { // LoadScripts will only load each script once even though we call it for each player if (!m_Worker.LoadScripts(m_AIName)) return false; OsPath path = L"simulation/ai/" + m_AIName + L"/data.json"; CScriptValRooted metadata = m_Worker.LoadMetadata(path); if (metadata.uninitialised()) { LOGERROR(L"Failed to create AI player: can't find %ls", path.string().c_str()); return false; } // Get the constructor name from the metadata // If the AI doesn't use modules, we look for the constructor in the global object // TODO: All AIs should use modules. Remove the condition if this requirement is met. std::string moduleName; std::string constructor; CScriptVal objectWithConstructor; // object that should contain the constructor function CScriptVal ctor; if (!m_ScriptInterface->HasProperty(metadata.get(), "moduleName")) { objectWithConstructor = m_ScriptInterface->GetGlobalObject(); } else { m_ScriptInterface->GetProperty(metadata.get(), "moduleName", moduleName); if(!m_ScriptInterface->GetProperty(m_ScriptInterface->GetGlobalObject(), moduleName.c_str(), objectWithConstructor) || objectWithConstructor.undefined()) { LOGERROR(L"Failed to create AI player: %ls: can't find the module that should contain the constructor: '%hs'", path.string().c_str(), moduleName.c_str()); return false; } } if (!m_ScriptInterface->GetProperty(metadata.get(), "constructor", constructor)) { LOGERROR(L"Failed to create AI player: %ls: missing 'constructor'", path.string().c_str()); return false; } // Get the constructor function from the loaded scripts if (!m_ScriptInterface->GetProperty(objectWithConstructor.get(), constructor.c_str(), ctor) || ctor.undefined()) { LOGERROR(L"Failed to create AI player: %ls: can't find constructor '%hs'", path.string().c_str(), constructor.c_str()); return false; } m_ScriptInterface->GetProperty(metadata.get(), "useShared", m_UseSharedComponent); CScriptVal obj; if (callConstructor) { // Set up the data to pass as the constructor argument CScriptVal settings; m_ScriptInterface->Eval(L"({})", settings); m_ScriptInterface->SetProperty(settings.get(), "player", m_Player, false); m_ScriptInterface->SetProperty(settings.get(), "difficulty", m_Difficulty, false); ENSURE(m_Worker.m_HasLoadedEntityTemplates); m_ScriptInterface->SetProperty(settings.get(), "templates", m_Worker.m_EntityTemplates, false); obj = m_ScriptInterface->CallConstructor(ctor.get(), settings.get()); } else { // For deserialization, we want to create the object with the correct prototype // but don't want to actually run the constructor again // XXX: actually we don't currently use this path for deserialization - maybe delete it? obj = m_ScriptInterface->NewObjectFromConstructor(ctor.get()); } if (obj.undefined()) { LOGERROR(L"Failed to create AI player: %ls: error calling constructor '%hs'", path.string().c_str(), constructor.c_str()); return false; } m_Obj = CScriptValRooted(m_ScriptInterface->GetContext(), obj); return true; }
~CAIPlayer() { // Clean up rooted objects before destroying their script context m_Obj = CScriptValRooted(); m_Commands.clear(); }
void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationCommand>& commands) { PROFILE3("sim update"); PROFILE2_ATTR("turn %d", (int)m_TurnNumber); fixed turnLengthFixed = fixed::FromInt(turnLength) / 1000; /* * In serialization test mode, we save the original (primary) simulation state before each turn update. * We run the update, then load the saved state into a secondary context. * We serialize that again and compare to the original serialization (to check that * serialize->deserialize->serialize is equivalent to serialize). * Then we run the update on the secondary context, and check that its new serialized * state matches the primary context after the update (to check that the simulation doesn't depend * on anything that's not serialized). */ const bool serializationTestDebugDump = false; // set true to save human-readable state dumps before an error is detected, for debugging (but slow) const bool serializationTestHash = true; // set true to save and compare hash of state SerializationTestState primaryStateBefore; if (m_EnableSerializationTest) { ENSURE(m_ComponentManager.SerializeState(primaryStateBefore.state)); if (serializationTestDebugDump) ENSURE(m_ComponentManager.DumpDebugState(primaryStateBefore.debug, false)); if (serializationTestHash) ENSURE(m_ComponentManager.ComputeStateHash(primaryStateBefore.hash, false)); } UpdateComponents(m_SimContext, turnLengthFixed, commands); if (m_EnableSerializationTest) { // Initialise the secondary simulation CTerrain secondaryTerrain; CSimContext secondaryContext; secondaryContext.m_Terrain = &secondaryTerrain; CComponentManager secondaryComponentManager(secondaryContext, m_ComponentManager.GetScriptInterface().GetRuntime()); secondaryComponentManager.LoadComponentTypes(); ENSURE(LoadDefaultScripts(secondaryComponentManager, NULL)); ResetComponentState(secondaryComponentManager, false, false); // Load the map into the secondary simulation LDR_BeginRegistering(); CMapReader* mapReader = new CMapReader; // automatically deletes itself // TODO: this duplicates CWorld::RegisterInit and could probably be cleaned up a bit std::string mapType; m_ComponentManager.GetScriptInterface().GetProperty(m_InitAttributes.get(), "mapType", mapType); if (mapType == "random") { // TODO: support random map scripts debug_warn(L"Serialization test mode only supports scenarios"); } else { std::wstring mapFile; m_ComponentManager.GetScriptInterface().GetProperty(m_InitAttributes.get(), "map", mapFile); VfsPath mapfilename = VfsPath(mapFile).ChangeExtension(L".pmp"); mapReader->LoadMap(mapfilename, CScriptValRooted(), &secondaryTerrain, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &secondaryContext, INVALID_PLAYER, true); // throws exception on failure } LDR_EndRegistering(); ENSURE(LDR_NonprogressiveLoad() == INFO::OK); ENSURE(secondaryComponentManager.DeserializeState(primaryStateBefore.state)); SerializationTestState secondaryStateBefore; ENSURE(secondaryComponentManager.SerializeState(secondaryStateBefore.state)); if (serializationTestDebugDump) ENSURE(secondaryComponentManager.DumpDebugState(secondaryStateBefore.debug, false)); if (serializationTestHash) ENSURE(secondaryComponentManager.ComputeStateHash(secondaryStateBefore.hash, false)); if (primaryStateBefore.state.str() != secondaryStateBefore.state.str() || primaryStateBefore.hash != secondaryStateBefore.hash) { ReportSerializationFailure(&primaryStateBefore, NULL, &secondaryStateBefore, NULL); } SerializationTestState primaryStateAfter; ENSURE(m_ComponentManager.SerializeState(primaryStateAfter.state)); if (serializationTestHash) ENSURE(m_ComponentManager.ComputeStateHash(primaryStateAfter.hash, false)); UpdateComponents(secondaryContext, turnLengthFixed, CloneCommandsFromOtherContext(m_ComponentManager.GetScriptInterface(), secondaryComponentManager.GetScriptInterface(), commands)); SerializationTestState secondaryStateAfter; ENSURE(secondaryComponentManager.SerializeState(secondaryStateAfter.state)); if (serializationTestHash) ENSURE(secondaryComponentManager.ComputeStateHash(secondaryStateAfter.hash, false)); if (primaryStateAfter.state.str() != secondaryStateAfter.state.str() || primaryStateAfter.hash != secondaryStateAfter.hash) { // Only do the (slow) dumping now we know we're going to need to report it ENSURE(m_ComponentManager.DumpDebugState(primaryStateAfter.debug, false)); ENSURE(secondaryComponentManager.DumpDebugState(secondaryStateAfter.debug, false)); ReportSerializationFailure(&primaryStateBefore, &primaryStateAfter, &secondaryStateBefore, &secondaryStateAfter); } } // if (m_TurnNumber == 0) // m_ComponentManager.GetScriptInterface().DumpHeap(); // Run the GC occasionally // (TODO: we ought to schedule this for a frame where we're not // running the sim update, to spread the load) if (m_TurnNumber % 1 == 0) m_ComponentManager.GetScriptInterface().MaybeIncrementalRuntimeGC(); if (m_EnableOOSLog) DumpState(); // Start computing AI for the next turn CmpPtr<ICmpAIManager> cmpAIManager(m_SimContext, SYSTEM_ENTITY); if (cmpAIManager) cmpAIManager->StartComputation(); ++m_TurnNumber; }
void CStdDeserializer::ScriptVal(const char* UNUSED(name), CScriptValRooted& out) { out = CScriptValRooted(m_ScriptInterface.GetContext(), ReadScriptVal(NULL)); }