Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "JSInterface_Mod.h"
21 :
22 : #include "ps/Mod.h"
23 : #include "ps/Pyrogenesis.h"
24 : #include "scriptinterface/FunctionWrapper.h"
25 : #include "scriptinterface/JSON.h"
26 : #include "scriptinterface/ScriptConversions.h"
27 :
28 : extern void RestartEngine();
29 :
30 : // To avoid copying data needlessly in GetEngineInfo, implement a ToJSVal for pointer types.
31 : using ModDataCPtr = const Mod::ModData*;
32 :
33 : template<>
34 0 : void Script::ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret, const ModDataCPtr& data)
35 : {
36 0 : ret.set(Script::CreateObject(rq));
37 0 : Script::SetProperty(rq, ret, "mod", data->m_Pathname);
38 0 : Script::SetProperty(rq, ret, "name", data->m_Name);
39 0 : Script::SetProperty(rq, ret, "version", data->m_Version);
40 0 : Script::SetProperty(rq, ret, "ignoreInCompatibilityChecks", data->m_IgnoreInCompatibilityChecks);
41 0 : }
42 :
43 : // Required by JSVAL_VECTOR, but can't be implemented.
44 : template<>
45 0 : bool Script::FromJSVal(const ScriptRequest &, const JS::HandleValue, ModDataCPtr&)
46 : {
47 0 : LOGERROR("Not implemented");
48 0 : return false;
49 : }
50 :
51 0 : JSVAL_VECTOR(const Mod::ModData*);
52 :
53 : // Implement FromJSVal as a non-pointer type.
54 : template<>
55 0 : void Script::ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret, const Mod::ModData& data)
56 : {
57 0 : ret.set(Script::CreateObject(rq));
58 0 : Script::SetProperty(rq, ret, "mod", data.m_Pathname);
59 0 : Script::SetProperty(rq, ret, "name", data.m_Name);
60 0 : Script::SetProperty(rq, ret, "version", data.m_Version);
61 0 : Script::SetProperty(rq, ret, "ignoreInCompatibilityChecks", data.m_IgnoreInCompatibilityChecks);
62 0 : }
63 :
64 : template<>
65 0 : bool Script::FromJSVal(const ScriptRequest& rq, const JS::HandleValue val, Mod::ModData& data)
66 : {
67 : // To avoid errors & for convenience, some retro-compatibility when reading
68 : // TODO: remove this once we hit A26.
69 0 : JS::RootedObject obj(rq.cx, val.toObjectOrNull());
70 0 : bool isArray = false;
71 0 : if (JS::IsArray(rq.cx, obj, &isArray) && isArray)
72 : {
73 0 : if (!Script::GetPropertyInt(rq, val, 0, data.m_Pathname))
74 0 : return false;
75 0 : if (!Script::GetPropertyInt(rq, val, 1, data.m_Version))
76 0 : return false;
77 : // Set a sane default for this.
78 0 : data.m_Name = data.m_Pathname;
79 0 : return true;
80 : }
81 :
82 : // This property is not set in mod.json files, so don't fail if it's not there.
83 0 : if (Script::HasProperty(rq, val, "mod") && !Script::GetProperty(rq, val, "mod", data.m_Pathname))
84 0 : return false;
85 :
86 0 : if (!Script::GetProperty(rq, val, "version", data.m_Version))
87 0 : return false;
88 0 : if (!Script::GetProperty(rq, val, "name", data.m_Name))
89 0 : return false;
90 :
91 : // Optional - this makes the mod 'GUI-only'.
92 0 : if (Script::HasProperty(rq, val, "ignoreInCompatibilityChecks"))
93 : {
94 0 : if (!Script::GetProperty(rq, val, "ignoreInCompatibilityChecks", data.m_IgnoreInCompatibilityChecks))
95 0 : return false;
96 : }
97 : else
98 0 : data.m_IgnoreInCompatibilityChecks = false;
99 :
100 0 : return true;
101 : }
102 :
103 0 : JSVAL_VECTOR(Mod::ModData);
104 :
105 : namespace JSI_Mod
106 : {
107 0 : Mod* ModGetter(const ScriptRequest&, JS::CallArgs&)
108 : {
109 0 : return &g_Mods;
110 : }
111 :
112 0 : JS::Value GetEngineInfo(const ScriptInterface& scriptInterface)
113 : {
114 0 : ScriptRequest rq(scriptInterface);
115 :
116 0 : JS::RootedValue mods(rq.cx);
117 0 : Script::ToJSVal(rq, &mods, g_Mods.GetEnabledModsData());
118 0 : JS::RootedValue metainfo(rq.cx);
119 :
120 0 : Script::CreateObject(
121 : rq,
122 : &metainfo,
123 : "engine_version", engine_version,
124 : "mods", mods);
125 :
126 0 : Script::FreezeObject(rq, metainfo, true);
127 :
128 0 : return metainfo;
129 : }
130 :
131 0 : JS::Value GetAvailableMods(const ScriptRequest& rq)
132 : {
133 0 : JS::RootedValue ret(rq.cx, Script::CreateObject(rq));
134 0 : for (const Mod::ModData& data : g_Mods.GetAvailableMods())
135 : {
136 0 : JS::RootedValue json(rq.cx);
137 0 : if (!Script::ParseJSON(rq, data.m_Text, &json))
138 : {
139 0 : ScriptException::Raise(rq, "Error parsing mod.json of '%s'", data.m_Pathname.c_str());
140 0 : continue;
141 : }
142 0 : Script::SetProperty(rq, ret, data.m_Pathname.c_str(), json);
143 : }
144 0 : return ret.get();
145 : }
146 :
147 0 : bool AreModsPlayCompatible(const std::vector<Mod::ModData>& a, const std::vector<Mod::ModData>& b)
148 : {
149 0 : std::vector<const Mod::ModData*> modsA, modsB;
150 0 : modsA.reserve(a.size());
151 0 : for (const Mod::ModData& mod : a)
152 0 : modsA.push_back(&mod);
153 0 : modsB.reserve(b.size());
154 0 : for (const Mod::ModData& mod : b)
155 0 : modsB.push_back(&mod);
156 0 : return Mod::AreModsPlayCompatible(modsA, modsB);
157 : }
158 :
159 0 : bool SetModsAndRestartEngine(const std::vector<CStr>& mods)
160 : {
161 0 : if (!g_Mods.EnableMods(mods, false))
162 0 : return false;
163 :
164 0 : RestartEngine();
165 0 : return true;
166 : }
167 :
168 0 : bool HasIncompatibleMods()
169 : {
170 0 : return g_Mods.GetIncompatibleMods().size() > 0;
171 : }
172 :
173 12 : void RegisterScriptFunctions(const ScriptRequest& rq)
174 : {
175 12 : ScriptFunction::Register<GetEngineInfo>(rq, "GetEngineInfo");
176 12 : ScriptFunction::Register<GetAvailableMods>(rq, "GetAvailableMods");
177 12 : ScriptFunction::Register<&Mod::GetEnabledMods, ModGetter>(rq, "GetEnabledMods");
178 12 : ScriptFunction::Register<AreModsPlayCompatible>(rq, "AreModsPlayCompatible");
179 12 : ScriptFunction::Register<HasIncompatibleMods> (rq, "HasIncompatibleMods");
180 12 : ScriptFunction::Register<&Mod::GetIncompatibleMods, ModGetter>(rq, "GetIncompatibleMods");
181 12 : ScriptFunction::Register<&SetModsAndRestartEngine>(rq, "SetModsAndRestartEngine");
182 12 : }
183 3 : }
|