Add: [Script] GSAsyncMode to set async mode of gamescript commands (#10913)

In asynchronous mode, don't wait for result of executed command,
just fire-and-forget, and return estimated cost/result
This commit is contained in:
Jonathan G Rennison
2023-06-04 10:15:35 +01:00
committed by GitHub
parent 5821194ad1
commit 3effb8931c
6 changed files with 189 additions and 10 deletions

View File

@@ -82,6 +82,22 @@ ScriptObject::ActiveInstance::~ActiveInstance()
return GetStorage()->mode_instance;
}
/* static */ void ScriptObject::SetDoCommandAsyncMode(ScriptAsyncModeProc *proc, ScriptObject *instance)
{
GetStorage()->async_mode = proc;
GetStorage()->async_mode_instance = instance;
}
/* static */ ScriptAsyncModeProc *ScriptObject::GetDoCommandAsyncMode()
{
return GetStorage()->async_mode;
}
/* static */ ScriptObject *ScriptObject::GetDoCommandAsyncModeInstance()
{
return GetStorage()->async_mode_instance;
}
/* static */ void ScriptObject::SetLastCommand(const CommandDataBuffer &data, Commands cmd)
{
ScriptStorage *s = GetStorage();
@@ -239,7 +255,7 @@ ScriptObject::ActiveInstance::~ActiveInstance()
return ScriptObject::GetActiveInstance()->GetDoCommandCallback();
}
std::tuple<bool, bool, bool> ScriptObject::DoCommandPrep()
std::tuple<bool, bool, bool, bool> ScriptObject::DoCommandPrep()
{
if (!ScriptObject::CanSuspend()) {
throw Script_FatalError("You are not allowed to execute any DoCommand (even indirect) in your constructor, Save(), Load(), and any valuator.");
@@ -248,17 +264,20 @@ std::tuple<bool, bool, bool> ScriptObject::DoCommandPrep()
/* Are we only interested in the estimate costs? */
bool estimate_only = GetDoCommandMode() != nullptr && !GetDoCommandMode()();
/* Should the command be executed asynchronously? */
bool asynchronous = GetDoCommandAsyncMode() != nullptr && GetDoCommandAsyncMode()();
bool networking = _networking && !_generating_world;
if (!ScriptCompanyMode::IsDeity() && !ScriptCompanyMode::IsValid()) {
ScriptObject::SetLastError(ScriptError::ERR_PRECONDITION_INVALID_COMPANY);
return { true, estimate_only, networking };
return { true, estimate_only, asynchronous, networking };
}
return { false, estimate_only, networking };
return { false, estimate_only, asynchronous, networking };
}
bool ScriptObject::DoCommandProcessResult(const CommandCost &res, Script_SuspendCallbackProc *callback, bool estimate_only)
bool ScriptObject::DoCommandProcessResult(const CommandCost &res, Script_SuspendCallbackProc *callback, bool estimate_only, bool asynchronous)
{
/* Set the default callback to return a true/false result of the DoCommand */
if (callback == nullptr) callback = &ScriptInstance::DoCommandReturn;
@@ -282,8 +301,13 @@ bool ScriptObject::DoCommandProcessResult(const CommandCost &res, Script_Suspend
SetLastCost(res.GetCost());
SetLastCommandRes(true);
if (_generating_world) {
if (_generating_world || asynchronous) {
IncreaseDoCommandCosts(res.GetCost());
if (!_generating_world) {
/* Charge a nominal fee for asynchronously executed commands */
Squirrel *engine = ScriptObject::GetActiveInstance()->engine;
Squirrel::DecreaseOps(engine->GetVM(), 100);
}
if (callback != nullptr) {
/* Insert return value into to stack and throw a control code that
* the return value in the stack should be used. */