Skip to content

Commit eb05b79

Browse files
committed
Implement boilerplate for script handled scene compatibility without chicanery. Add Required Area ini property. Script handling should work already as far as I can tell, doesn't for some reason.
1 parent 5c457c1 commit eb05b79

File tree

4 files changed

+126
-78
lines changed

4 files changed

+126
-78
lines changed

Source/Activities/GAScripted.cpp

Lines changed: 40 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,6 @@ int GAScripted::Create() {
6161
return -1;
6262
}
6363

64-
// Scan the script file for any mentions/uses of Areas.
65-
CollectRequiredAreas();
66-
6764
// If the GAScripted has a OnSave() function, we assume it can be saved by default
6865
ReloadScripts();
6966
m_AllowsUserSaving = HasSaveFunction();
@@ -100,6 +97,11 @@ int GAScripted::ReadProperty(const std::string_view& propName, Reader& reader) {
10097
MatchProperty("AddPieSlice", {
10198
m_PieSlicesToAdd.emplace_back(std::unique_ptr<PieSlice>(dynamic_cast<PieSlice*>(g_PresetMan.ReadReflectedPreset(reader))));
10299
});
100+
MatchProperty("AddRequiredArea", {
101+
std::string requiredArea;
102+
reader >> requiredArea;
103+
m_RequiredAreas.insert(requiredArea);
104+
});
103105

104106
EndPropertyList;
105107
}
@@ -117,6 +119,10 @@ int GAScripted::Save(Writer& writer) const {
117119
writer.NewPropertyWithValue("AddPieSlice", pieSliceToAdd.get());
118120
}
119121

122+
for (const std::string& requiredArea: m_RequiredAreas) {
123+
writer.NewPropertyWithValue("AddRequiredArea", requiredArea);
124+
}
125+
120126
return 0;
121127
}
122128

@@ -141,7 +147,6 @@ int GAScripted::ReloadScripts() {
141147
}
142148

143149
int error = 0;
144-
CollectRequiredAreas();
145150

146151
// If it hasn't been yet, run the file that specifies the Lua functions for this' operating logic (including the scene test function)
147152
if (!g_LuaMan.GetMasterScriptState().GlobalIsDefined(m_LuaClassName)) {
@@ -187,21 +192,6 @@ bool GAScripted::SceneIsCompatible(Scene* pScene, int teams) {
187192
return false;
188193
}
189194

190-
// Check if all Areas required by this are defined in the Scene
191-
for (std::set<std::string>::iterator itr = m_RequiredAreas.begin(); itr != m_RequiredAreas.end(); ++itr) {
192-
// If Area is missing, this Scene is not up to par
193-
if (!pScene->HasArea(*itr)) {
194-
return false;
195-
}
196-
}
197-
198-
// Temporarily store the scene so the Lua state can access it and check for the necessary Areas etc.
199-
g_LuaMan.GetMasterScriptState().SetTempEntity(pScene);
200-
// Cast the test scene it to a Scene object in Lua
201-
if (g_LuaMan.GetMasterScriptState().RunScriptString("TestScene = ToScene(LuaMan.TempEntity);") < 0) {
202-
return false;
203-
}
204-
205195
// If it hasn't been yet, run the file that specifies the Lua functions for this' operating logic (including the scene test function)
206196
if (!g_LuaMan.GetMasterScriptState().GlobalIsDefined(m_LuaClassName)) {
207197
// Temporarily store this Activity so the Lua state can access it
@@ -218,10 +208,31 @@ bool GAScripted::SceneIsCompatible(Scene* pScene, int teams) {
218208
}
219209

220210
// Call the defined function, but only after first checking if it exists
221-
g_LuaMan.GetMasterScriptState().RunScriptString("if " + m_LuaClassName + ".SceneTest then " + m_LuaClassName + ":SceneTest(); end");
211+
bool conditionMet = false;
212+
int error = RunLuaConditionalTest("IsCompatibleScene", conditionMet, {pScene}, {}, {});
213+
if (error < 0) {
214+
RTEAbort("Error")
215+
return false;
216+
} else if (error == 0) {
217+
//RTEAbort("Missing")
218+
conditionMet = true;
219+
} else {
220+
RTEAbort("Passed")
221+
}
222222

223-
// If the test left the Scene pointer still set, it means it passed the test
224-
return g_LuaMan.GetMasterScriptState().GlobalIsDefined("TestScene");
223+
if (!conditionMet) {
224+
return false;
225+
}
226+
227+
// Check if all Areas required by this are defined in the Scene
228+
for (std::set<std::string>::iterator itr = m_RequiredAreas.begin(); itr != m_RequiredAreas.end(); ++itr) {
229+
// If Area is missing, this Scene is not up to par
230+
if (!pScene->HasArea(*itr)) {
231+
return false;
232+
}
233+
}
234+
235+
return true;
225236
}
226237

227238
void GAScripted::HandleCraftEnteringOrbit(ACraft* orbitedCraft) {
@@ -378,60 +389,16 @@ int GAScripted::RunLuaFunction(const std::string& functionName, const std::vecto
378389
return error;
379390
}
380391

381-
void GAScripted::CollectRequiredAreas() {
382-
// Open the script file so we can check it out
383-
std::ifstream scriptFile = std::ifstream(g_PresetMan.GetFullModulePath(m_ScriptPath.c_str()));
384-
if (!scriptFile.good()) {
385-
return;
392+
int GAScripted::RunLuaConditionalTest(const std::string& functionName, bool& returnParam, const std::vector<const Entity*>& functionEntityArguments, const std::vector<std::string_view>& functionLiteralArguments, const std::vector<LuabindObjectWrapper*>& functionObjectArguments) {
393+
// Call the defined function, but only after first checking if it exists
394+
auto funcItr = m_ScriptFunctions.find(functionName);
395+
if (funcItr == m_ScriptFunctions.end()) {
396+
return 0;
386397
}
387398

388-
// Harvest the required Area:s from the file
389-
m_RequiredAreas.clear();
390-
391-
bool blockCommented = false;
399+
int error = g_LuaMan.GetMasterScriptState().RunScriptConditionalTestFunctionObject(funcItr->second.get(), "_G", m_LuaClassName, returnParam, functionEntityArguments, functionLiteralArguments, functionObjectArguments);
392400

393-
while (!scriptFile.eof()) {
394-
// Go through the script file, line by line
395-
char rawLine[512];
396-
scriptFile.getline(rawLine, 512);
397-
std::string line = rawLine;
398-
std::string::size_type pos = 0;
399-
std::string::size_type endPos = 0;
400-
std::string::size_type commentPos = std::string::npos;
401-
402-
// Check for block comments
403-
if (!blockCommented && (commentPos = line.find("--[[", 0)) != std::string::npos) {
404-
blockCommented = true;
405-
}
406-
407-
// Find the end of the block comment
408-
if (blockCommented) {
409-
if ((commentPos = line.find("]]", commentPos == std::string::npos ? 0 : commentPos)) != std::string::npos) {
410-
blockCommented = false;
411-
pos = commentPos;
412-
}
413-
}
414-
415-
// Process the line as usual
416-
if (!blockCommented) {
417-
// See if this line is commented out anywhere
418-
commentPos = line.find("--", 0);
419-
do {
420-
// Find the beginning of a mentioned Area name
421-
pos = line.find(":GetArea(\"", pos);
422-
if (pos != std::string::npos && pos < commentPos) {
423-
// Move position forward to the actual Area name
424-
pos += 10;
425-
// Find the end of the Area name
426-
endPos = line.find_first_of('"', pos);
427-
// Copy it out and put into the list
428-
if (endPos != std::string::npos) {
429-
m_RequiredAreas.insert(line.substr(pos, endPos - pos));
430-
}
431-
}
432-
} while (pos != std::string::npos && pos < commentPos);
433-
}
434-
}
401+
return error;
435402
}
436403

437404
void GAScripted::AddPieSlicesToActiveActorPieMenus() {

Source/Activities/GAScripted.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ namespace RTE {
2323

2424
/// Public member variable, method and friend function declarations
2525
public:
26-
ScriptFunctionNames("StartActivity", "UpdateActivity", "PauseActivity", "EndActivity", "OnSave", "CraftEnteredOrbit", "OnMessage", "OnGlobalMessage");
26+
ScriptFunctionNames("StartActivity", "UpdateActivity", "PauseActivity", "EndActivity", "OnSave", "CraftEnteredOrbit", "OnMessage", "IsCompatibleScene", "OnGlobalMessage");
2727

2828
// Concrete allocation and cloning definitions
2929
EntityAllocation(GAScripted);
@@ -132,13 +132,10 @@ namespace RTE {
132132
void Draw(BITMAP* pTargetBitmap, const Vector& targetPos = Vector()) override;
133133

134134
int RunLuaFunction(const std::string& functionName, const std::vector<const Entity*>& functionEntityArguments = std::vector<const Entity*>(), const std::vector<std::string_view>& functionLiteralArguments = std::vector<std::string_view>(), const std::vector<LuabindObjectWrapper*>& functionObjectArguments = std::vector<LuabindObjectWrapper*>());
135+
int RunLuaConditionalTest(const std::string& functionName, bool& returnParam, const std::vector<const Entity*>& functionEntityArguments = std::vector<const Entity*>(), const std::vector<std::string_view>& functionLiteralArguments = std::vector<std::string_view>(), const std::vector<LuabindObjectWrapper*>& functionObjectArguments = std::vector<LuabindObjectWrapper*>());
135136

136137
/// Protected member variable and method declarations
137138
protected:
138-
/// Goes through the script file and checks for any mentions and uses of
139-
/// Area:s that are required for this Activity to run in a Scene.
140-
void CollectRequiredAreas();
141-
142139
/// Does nothing - we do this in script! Just overrides the base behaviour.
143140
void InitAIs() override{};
144141

Source/Managers/LuaMan.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,80 @@ int LuaStateWrapper::RunScriptFunctionObject(const LuabindObjectWrapper* functio
625625
return status;
626626
}
627627

628+
int LuaStateWrapper::RunScriptConditionalTestFunctionObject(const LuabindObjectWrapper* functionObject, const std::string& selfGlobalTableName, const std::string& selfGlobalTableKey, bool& returnParam, const std::vector<const Entity*>& functionEntityArguments, const std::vector<std::string_view>& functionLiteralArguments, const std::vector<LuabindObjectWrapper*>& functionObjectArguments) {
629+
int status = 0;
630+
631+
std::lock_guard<std::recursive_mutex> lock(m_Mutex);
632+
s_currentLuaState = this;
633+
m_CurrentlyRunningScriptPath = functionObject->GetFilePath();
634+
635+
lua_pushcfunction(m_State, &AddFileAndLineToError);
636+
functionObject->GetLuabindObject()->push(m_State);
637+
638+
int argumentCount = functionEntityArguments.size() + functionLiteralArguments.size() + functionObjectArguments.size();
639+
if (!selfGlobalTableName.empty() && TableEntryIsDefined(selfGlobalTableName, selfGlobalTableKey)) {
640+
lua_getglobal(m_State, selfGlobalTableName.c_str());
641+
lua_getfield(m_State, -1, selfGlobalTableKey.c_str());
642+
lua_remove(m_State, -2);
643+
argumentCount++;
644+
}
645+
646+
for (const Entity* functionEntityArgument: functionEntityArguments) {
647+
std::unique_ptr<LuabindObjectWrapper> downCastEntityAsLuabindObjectWrapper(LuaAdaptersEntityCast::s_EntityToLuabindObjectCastFunctions.at(functionEntityArgument->GetClassName())(const_cast<Entity*>(functionEntityArgument), m_State));
648+
downCastEntityAsLuabindObjectWrapper->GetLuabindObject()->push(m_State);
649+
}
650+
651+
for (const std::string_view& functionLiteralArgument: functionLiteralArguments) {
652+
char* stringToDoubleConversionFailed = nullptr;
653+
if (functionLiteralArgument == "nil") {
654+
lua_pushnil(m_State);
655+
} else if (functionLiteralArgument == "true" || functionLiteralArgument == "false") {
656+
lua_pushboolean(m_State, functionLiteralArgument == "true" ? 1 : 0);
657+
} else if (double argumentAsNumber = std::strtod(functionLiteralArgument.data(), &stringToDoubleConversionFailed); !*stringToDoubleConversionFailed) {
658+
lua_pushnumber(m_State, argumentAsNumber);
659+
} else {
660+
lua_pushlstring(m_State, functionLiteralArgument.data(), functionLiteralArgument.size());
661+
}
662+
}
663+
664+
for (const LuabindObjectWrapper* functionObjectArgument: functionObjectArguments) {
665+
if (functionObjectArgument->GetLuabindObject()->interpreter() != m_State) {
666+
LuabindObjectWrapper copy = functionObjectArgument->GetCopyForState(*m_State);
667+
copy.GetLuabindObject()->push(m_State);
668+
} else {
669+
functionObjectArgument->GetLuabindObject()->push(m_State);
670+
}
671+
}
672+
673+
const std::string& path = functionObject->GetFilePath();
674+
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
675+
{
676+
ZoneScoped;
677+
ZoneName(path.c_str(), path.length());
678+
679+
if (lua_pcall(m_State, argumentCount, LUA_MULTRET, -argumentCount - 2) > 0) {
680+
//returnParam = 1 == lua_toboolean(m_State, 1);
681+
m_LastError = lua_tostring(m_State, -1);
682+
lua_pop(m_State, 1);
683+
g_ConsoleMan.PrintString("ERROR: " + m_LastError);
684+
ClearErrors();
685+
status = -1;
686+
}
687+
}
688+
std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
689+
690+
// only track time in non-MT scripts, for now
691+
if (&g_LuaMan.GetMasterScriptState() == this) {
692+
m_ScriptTimings[path].m_Time += std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count();
693+
m_ScriptTimings[path].m_CallCount++;
694+
}
695+
696+
lua_pop(m_State, 1);
697+
698+
m_CurrentlyRunningScriptPath = "";
699+
return status;
700+
}
701+
628702
int LuaStateWrapper::RunScriptFile(const std::string& filePath, bool consoleErrors, bool doInSandboxedEnvironment) {
629703
const std::string fullScriptPath = g_PresetMan.GetFullModulePath(filePath);
630704
if (fullScriptPath.empty()) {

Source/Managers/LuaMan.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ namespace RTE {
112112
/// @param functionLiteralArguments Optional vector of strings that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty.
113113
/// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal.
114114
int RunScriptFunctionObject(const LuabindObjectWrapper* functionObjectWrapper, const std::string& selfGlobalTableName, const std::string& selfGlobalTableKey, const std::vector<const Entity*>& functionEntityArguments = std::vector<const Entity*>(), const std::vector<std::string_view>& functionLiteralArguments = std::vector<std::string_view>(), const std::vector<LuabindObjectWrapper*>& functionObjectArguments = std::vector<LuabindObjectWrapper*>());
115+
116+
/// Runs the given Lua function object. The first argument to the function will always be the self object.
117+
/// If either argument list has entries, they will be passed into the function in order, with entity arguments first.
118+
/// @param functionObjectWrapper The LuabindObjectWrapper containing the Lua function to be run.
119+
/// @param selfGlobalTableName The name of the global Lua table that gives access to the self object.
120+
/// @param selfGlobalTableKey The key for this object in the respective global Lua table.
121+
/// @param functionEntityArguments Optional vector of entity pointers that should be passed into the Lua function. Their internal Lua states will not be accessible. Defaults to empty.
122+
/// @param functionLiteralArguments Optional vector of strings that should be passed into the Lua function. Entries must be surrounded with escaped quotes (i.e.`\"`) they'll be passed in as-is, allowing them to act as booleans, etc.. Defaults to empty.
123+
/// @return An error return value signaling success or any particular failure. Anything below 0 is an error signal.
124+
int RunScriptConditionalTestFunctionObject(const LuabindObjectWrapper* functionObjectWrapper, const std::string& selfGlobalTableName, const std::string& selfGlobalTableKey, bool& returnParam, const std::vector<const Entity*>& functionEntityArguments = std::vector<const Entity*>(), const std::vector<std::string_view>& functionLiteralArguments = std::vector<std::string_view>(), const std::vector<LuabindObjectWrapper*>& functionObjectArguments = std::vector<LuabindObjectWrapper*>());
115125

116126
/// Opens and loads a file containing a script and runs it on the state.
117127
/// @param filePath The path to the file to load and run.

0 commit comments

Comments
 (0)