Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/simulation2/components/CCmpAIManager.cpp
/* Copyright (C) 2019 Wildfire Games. | /* Copyright (C) 2020 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 0 A.D. is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU General Public License as published by | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
▲ Show 20 Lines • Show All 201 Lines • ▼ Show 20 Lines | public: | ||||
}; | }; | ||||
CAIWorker() : | CAIWorker() : | ||||
m_ScriptInterface(new ScriptInterface("Engine", "AI", g_ScriptRuntime)), | m_ScriptInterface(new ScriptInterface("Engine", "AI", g_ScriptRuntime)), | ||||
m_TurnNum(0), | m_TurnNum(0), | ||||
m_CommandsComputed(true), | m_CommandsComputed(true), | ||||
m_HasLoadedEntityTemplates(false), | m_HasLoadedEntityTemplates(false), | ||||
m_HasSharedComponent(false), | m_HasSharedComponent(false), | ||||
m_SerializablePrototypes(new ObjectIdCache<std::wstring>(g_ScriptRuntime)), | |||||
m_EntityTemplates(g_ScriptRuntime->m_rt), | m_EntityTemplates(g_ScriptRuntime->m_rt), | ||||
m_SharedAIObj(g_ScriptRuntime->m_rt), | m_SharedAIObj(g_ScriptRuntime->m_rt), | ||||
m_PassabilityMapVal(g_ScriptRuntime->m_rt), | m_PassabilityMapVal(g_ScriptRuntime->m_rt), | ||||
m_TerritoryMapVal(g_ScriptRuntime->m_rt) | m_TerritoryMapVal(g_ScriptRuntime->m_rt) | ||||
{ | { | ||||
m_ScriptInterface->ReplaceNondeterministicRNG(m_RNG); | m_ScriptInterface->ReplaceNondeterministicRNG(m_RNG); | ||||
m_ScriptInterface->SetCallbackData(static_cast<void*> (this)); | m_ScriptInterface->SetCallbackData(static_cast<void*> (this)); | ||||
m_SerializablePrototypes->init(); | |||||
JS_AddExtraGCRootsTracer(m_ScriptInterface->GetJSRuntime(), Trace, this); | JS_AddExtraGCRootsTracer(m_ScriptInterface->GetJSRuntime(), Trace, this); | ||||
m_ScriptInterface->RegisterFunction<void, int, JS::HandleValue, CAIWorker::PostCommand>("PostCommand"); | m_ScriptInterface->RegisterFunction<void, int, JS::HandleValue, CAIWorker::PostCommand>("PostCommand"); | ||||
m_ScriptInterface->RegisterFunction<void, std::wstring, CAIWorker::IncludeModule>("IncludeModule"); | m_ScriptInterface->RegisterFunction<void, std::wstring, CAIWorker::IncludeModule>("IncludeModule"); | ||||
m_ScriptInterface->RegisterFunction<void, CAIWorker::ExitProgram>("Exit"); | m_ScriptInterface->RegisterFunction<void, CAIWorker::ExitProgram>("Exit"); | ||||
m_ScriptInterface->RegisterFunction<JS::Value, JS::HandleValue, JS::HandleValue, pass_class_t, CAIWorker::ComputePath>("ComputePath"); | m_ScriptInterface->RegisterFunction<JS::Value, JS::HandleValue, JS::HandleValue, pass_class_t, CAIWorker::ComputePath>("ComputePath"); | ||||
▲ Show 20 Lines • Show All 416 Lines • ▼ Show 20 Lines | void Serialize(std::ostream& stream, bool isDebug) | ||||
{ | { | ||||
CDebugSerializer serializer(*m_ScriptInterface, stream); | CDebugSerializer serializer(*m_ScriptInterface, stream); | ||||
serializer.Indent(4); | serializer.Indent(4); | ||||
SerializeState(serializer); | SerializeState(serializer); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
CStdSerializer serializer(*m_ScriptInterface, stream); | CStdSerializer serializer(*m_ScriptInterface, stream); | ||||
// TODO: see comment in Deserialize() | |||||
serializer.SetSerializablePrototypes(m_SerializablePrototypes); | |||||
SerializeState(serializer); | SerializeState(serializer); | ||||
} | } | ||||
} | } | ||||
void SerializeState(ISerializer& serializer) | void SerializeState(ISerializer& serializer) | ||||
{ | { | ||||
if (m_Players.empty()) | if (m_Players.empty()) | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | for (size_t i = 0; i < numAis; ++i) | ||||
m_Players.back()->m_Commands.reserve(numCommands); | m_Players.back()->m_Commands.reserve(numCommands); | ||||
for (size_t j = 0; j < numCommands; ++j) | for (size_t j = 0; j < numCommands; ++j) | ||||
{ | { | ||||
JS::RootedValue val(cx); | JS::RootedValue val(cx); | ||||
deserializer.ScriptVal("command", &val); | deserializer.ScriptVal("command", &val); | ||||
m_Players.back()->m_Commands.push_back(m_ScriptInterface->WriteStructuredClone(val)); | m_Players.back()->m_Commands.push_back(m_ScriptInterface->WriteStructuredClone(val)); | ||||
} | } | ||||
// TODO: this is yucky but necessary while the AIs are sharing data between contexts; | |||||
// ideally a new (de)serializer instance would be created for each player | |||||
// so they would have a single, consistent script context to use and serializable | |||||
// prototypes could be stored in their ScriptInterface | |||||
deserializer.SetSerializablePrototypes(m_DeserializablePrototypes); | |||||
bool hasCustomDeserialize = m_ScriptInterface->HasProperty(m_Players.back()->m_Obj, "Deserialize"); | bool hasCustomDeserialize = m_ScriptInterface->HasProperty(m_Players.back()->m_Obj, "Deserialize"); | ||||
if (hasCustomDeserialize) | if (hasCustomDeserialize) | ||||
{ | { | ||||
JS::RootedValue scriptData(cx); | JS::RootedValue scriptData(cx); | ||||
deserializer.ScriptVal("data", &scriptData); | deserializer.ScriptVal("data", &scriptData); | ||||
if (m_Players[i]->m_UseSharedComponent) | if (m_Players[i]->m_UseSharedComponent) | ||||
{ | { | ||||
if (!m_ScriptInterface->CallFunctionVoid(m_Players.back()->m_Obj, "Deserialize", scriptData, m_SharedAIObj)) | if (!m_ScriptInterface->CallFunctionVoid(m_Players.back()->m_Obj, "Deserialize", scriptData, m_SharedAIObj)) | ||||
Show All 22 Lines | void Deserialize(std::istream& stream, u32 numAis) | ||||
m_HierarchicalPathfinder.Recompute(&m_PassabilityMap, m_NonPathfindingPassClasses, m_PathfindingPassClasses); | m_HierarchicalPathfinder.Recompute(&m_PassabilityMap, m_NonPathfindingPassClasses, m_PathfindingPassClasses); | ||||
} | } | ||||
int getPlayerSize() | int getPlayerSize() | ||||
{ | { | ||||
return m_Players.size(); | return m_Players.size(); | ||||
} | } | ||||
void RegisterSerializablePrototype(std::wstring name, JS::HandleValue proto) | |||||
{ | |||||
// Require unique prototype and name (for reverse lookup) | |||||
// TODO: this is yucky - see comment in Deserialize() | |||||
ENSURE(proto.isObject() && "A serializable prototype has to be an object!"); | |||||
JSContext* cx = m_ScriptInterface->GetContext(); | |||||
JSAutoRequest rq(cx); | |||||
JS::RootedObject obj(cx, &proto.toObject()); | |||||
if (m_SerializablePrototypes->has(obj) || m_DeserializablePrototypes.find(name) != m_DeserializablePrototypes.end()) | |||||
{ | |||||
LOGERROR("RegisterSerializablePrototype called with same prototype multiple times: p=%p n='%s'", (void *)obj.get(), utf8_from_wstring(name)); | |||||
return; | |||||
} | |||||
m_SerializablePrototypes->add(cx, obj, name); | |||||
m_DeserializablePrototypes[name] = JS::Heap<JSObject*>(obj); | |||||
} | |||||
private: | private: | ||||
static void Trace(JSTracer *trc, void *data) | static void Trace(JSTracer *trc, void *data) | ||||
{ | { | ||||
reinterpret_cast<CAIWorker*>(data)->TraceMember(trc); | reinterpret_cast<CAIWorker*>(data)->TraceMember(trc); | ||||
} | } | ||||
void TraceMember(JSTracer *trc) | void TraceMember(JSTracer *trc) | ||||
{ | { | ||||
for (std::pair<const std::wstring, JS::Heap<JSObject*>>& prototype : m_DeserializablePrototypes) | |||||
JS_CallObjectTracer(trc, &prototype.second, "CAIWorker::m_DeserializablePrototypes"); | |||||
for (std::pair<const VfsPath, JS::Heap<JS::Value>>& metadata : m_PlayerMetadata) | for (std::pair<const VfsPath, JS::Heap<JS::Value>>& metadata : m_PlayerMetadata) | ||||
JS_CallValueTracer(trc, &metadata.second, "CAIWorker::m_PlayerMetadata"); | JS_CallValueTracer(trc, &metadata.second, "CAIWorker::m_PlayerMetadata"); | ||||
} | } | ||||
void LoadMetadata(const VfsPath& path, JS::MutableHandleValue out) | void LoadMetadata(const VfsPath& path, JS::MutableHandleValue out) | ||||
{ | { | ||||
if (m_PlayerMetadata.find(path) == m_PlayerMetadata.end()) | if (m_PlayerMetadata.find(path) == m_PlayerMetadata.end()) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | private: | ||||
std::map<std::string, pass_class_t> m_NonPathfindingPassClasses; | std::map<std::string, pass_class_t> m_NonPathfindingPassClasses; | ||||
std::map<std::string, pass_class_t> m_PathfindingPassClasses; | std::map<std::string, pass_class_t> m_PathfindingPassClasses; | ||||
HierarchicalPathfinder m_HierarchicalPathfinder; | HierarchicalPathfinder m_HierarchicalPathfinder; | ||||
LongPathfinder m_LongPathfinder; | LongPathfinder m_LongPathfinder; | ||||
bool m_CommandsComputed; | bool m_CommandsComputed; | ||||
shared_ptr<ObjectIdCache<std::wstring> > m_SerializablePrototypes; | |||||
std::map<std::wstring, JS::Heap<JSObject*> > m_DeserializablePrototypes; | |||||
CTemplateLoader m_TemplateLoader; | CTemplateLoader m_TemplateLoader; | ||||
}; | }; | ||||
/** | /** | ||||
* Implementation of ICmpAIManager. | * Implementation of ICmpAIManager. | ||||
*/ | */ | ||||
class CCmpAIManager : public ICmpAIManager | class CCmpAIManager : public ICmpAIManager | ||||
▲ Show 20 Lines • Show All 251 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator