Retire tpt.beginGetScript in favour of tpt.installScriptManager

There had never been a legit use of tpt.beginGetScript (or indeed, tpt.getscript) that didn't involve the exact parameters 1, "autorun.lua", 1. The most infamous alternative parametrization was 2, "autorun.lua", 1, which would install TPTMP where you'd normally install the script manager. Absolute madness.

The callback parameter also goes away with this change, because no other script needs to know when the script manager is done being installed. I also fixed a problem where the function might be called from the wrong context and thus get delayed in handling the completion of the HTTP request. With the earlier removal of checking for existing destination files and bailing out if found, this function is now bullet-proof(tm).

This restores the tpt.getscript(1, "autorun.lua", 1) usage pattern, albeit only from the console.
This commit is contained in:
Tamás Bálint Misius 2023-12-20 19:56:56 +01:00
parent 95b83ffd61
commit 3ecc1c6c6f
No account linked to committer's email address
3 changed files with 68 additions and 142 deletions

View File

@ -366,7 +366,7 @@ LuaScriptInterface::LuaScriptInterface(GameController * c, GameModel * m):
{"setfire", &luatpt_setfire},
{"setdebug", &luatpt_setdebug},
{"setfpscap",&luatpt_setfpscap},
{"beginGetScript",&luatpt_getscript},
{"installScriptManager",&installScriptManager},
{"setwindowsize",&luatpt_setwindowsize},
{"watertest",&luatpt_togglewater},
{"screenshot",&luatpt_screenshot},
@ -4704,90 +4704,76 @@ bool LuaScriptInterface::HandleEvent(const GameControllerEvent &event)
return cont;
}
struct GetScriptStatus
{
struct Ok
{
};
struct Cancelled
{
};
struct GetFailed
{
String error;
};
struct RunFailed
{
String error;
};
using Value = std::variant<
Ok,
Cancelled,
GetFailed,
RunFailed
>;
Value value;
};
void LuaScriptInterface::OnTick()
{
if (scriptDownload && scriptDownload->CheckDone())
if (scriptManagerDownload && scriptManagerDownload->CheckDone())
{
auto ret = scriptDownload->StatusCode();
ByteString scriptData;
auto complete = [this](GetScriptStatus status) {
if (std::get_if<GetScriptStatus::Ok>(&status.value))
struct Status
{
struct Ok
{
new InformationMessage("Script download", "Script successfully downloaded", false);
}
if (auto *requestFailed = std::get_if<GetScriptStatus::GetFailed>(&status.value))
};
struct GetFailed
{
new ErrorMessage("Script download", "Failed to get script: " + requestFailed->error);
}
if (auto *runFailed = std::get_if<GetScriptStatus::RunFailed>(&status.value))
String error;
};
struct RunFailed
{
new ErrorMessage("Script download", "Failed to run script: " + runFailed->error);
}
scriptDownloadComplete(status);
String error;
};
using Value = std::variant<
Ok,
GetFailed,
RunFailed
>;
Value value;
};
auto handleResponse = [this, &scriptData, &ret, &complete]() {
auto complete = [](Status status) {
if (std::get_if<Status::Ok>(&status.value))
{
new InformationMessage("Install script manager", "Script manager successfully installed", false);
}
if (auto *requestFailed = std::get_if<Status::GetFailed>(&status.value))
{
new ErrorMessage("Install script manager", "Failed to get script manager: " + requestFailed->error);
}
if (auto *runFailed = std::get_if<Status::RunFailed>(&status.value))
{
new ErrorMessage("Install script manager", "Failed to run script manager: " + runFailed->error);
}
};
try
{
auto ret = scriptManagerDownload->StatusCode();
auto scriptData = scriptManagerDownload->Finish().second;
if (!scriptData.size())
{
complete({ GetScriptStatus::GetFailed{ "Server did not return data" } });
complete({ Status::GetFailed{ "Server did not return data" } });
return;
}
if (ret != 200)
{
complete({ GetScriptStatus::GetFailed{ ByteString(http::StatusText(ret)).FromUtf8() } });
complete({ Status::GetFailed{ ByteString(http::StatusText(ret)).FromUtf8() } });
return;
}
if (!Platform::WriteFile(std::vector<char>(scriptData.begin(), scriptData.end()), scriptDownloadFilename))
ByteString filename = "autorun.lua";
if (!Platform::WriteFile(std::vector<char>(scriptData.begin(), scriptData.end()), filename))
{
complete({ GetScriptStatus::GetFailed{ "Unable to write to file" } });
complete({ Status::GetFailed{ String::Build("Unable to write to ", filename.FromUtf8()) } });
return;
}
if (scriptDownloadRunScript)
if (tpt_lua_dostring(l, ByteString::Build("dofile('", filename, "')")))
{
if (tpt_lua_dostring(l, ByteString::Build("dofile('", scriptDownloadFilename, "')")))
{
complete({ GetScriptStatus::RunFailed{ luacon_geterror() } });
return;
}
complete({ Status::RunFailed{ luacon_geterror() } });
return;
}
complete({ GetScriptStatus::Ok{} });
};
try
{
scriptData = scriptDownload->Finish().second;
handleResponse();
complete({ Status::Ok{} });
}
catch (const http::RequestError &ex)
{
complete({ GetScriptStatus::GetFailed{ ByteString(ex.what()).FromUtf8() } });
complete({ Status::GetFailed{ ByteString(ex.what()).FromUtf8() } });
}
scriptDownload.reset();
scriptDownloadComplete = nullptr;
scriptDownloadPending = false;
scriptManagerDownload.reset();
}
lua_getglobal(l, "simulation");
if (lua_istable(l, -1))
@ -5206,82 +5192,21 @@ CommandInterface *CommandInterface::Create(GameController * c, GameModel * m)
return new LuaScriptInterface(c, m);
}
int LuaScriptInterface::luatpt_getscript(lua_State* l)
int LuaScriptInterface::installScriptManager(lua_State* l)
{
auto *luacon_ci = static_cast<LuaScriptInterface *>(commandInterface);
if (luacon_ci->scriptDownloadPending)
if (luacon_ci->scriptManagerDownload)
{
new ErrorMessage("Script download", "A script download is already pending");
lua_pushnil(l);
lua_pushliteral(l, "pending");
return 2;
return 0;
}
int scriptID = luaL_checkinteger(l, 1);
auto filename = tpt_lua_checkByteString(l, 2);
auto runScript = PickIfType(l, 3, false);
auto cb = std::make_shared<LuaSmartRef>(); // * Bind to main lua state (might be different from l).
cb->Assign(l, lua_gettop(l));
luacon_ci->scriptDownloadComplete = [cb](const GetScriptStatus &status) {
auto *luacon_ci = static_cast<LuaScriptInterface *>(commandInterface);
auto l = luacon_ci->l;
cb->Push(l);
if (lua_isfunction(l, -1))
{
int nargs = 0;
if (std::get_if<GetScriptStatus::Ok>(&status.value))
{
lua_pushliteral(l, "ok");
nargs = 1;
}
if (std::get_if<GetScriptStatus::Cancelled>(&status.value))
{
lua_pushliteral(l, "cancelled");
nargs = 1;
}
if (auto *requestFailed = std::get_if<GetScriptStatus::GetFailed>(&status.value))
{
lua_pushliteral(l, "get_failed");
tpt_lua_pushString(l, requestFailed->error);
nargs = 2;
}
if (auto *runFailed = std::get_if<GetScriptStatus::RunFailed>(&status.value))
{
lua_pushliteral(l, "run_failed");
tpt_lua_pushString(l, runFailed->error);
nargs = 2;
}
if (tpt_lua_pcall(l, nargs, 0, 0, false))
{
luacon_ci->Log(CommandInterface::LogError, luacon_geterror());
}
}
else
{
lua_pop(l, 1);
}
};
ByteString url = ByteString::Build(SCHEME, "starcatcher.us/scripts/main.lua?get=", scriptID);
new ConfirmPrompt("Do you want to install this script?", url.FromUtf8(), {
[filename, runScript, url]() {
auto *luacon_ci = static_cast<LuaScriptInterface *>(commandInterface);
luacon_ci->scriptDownload = std::make_unique<http::Request>(url);
luacon_ci->scriptDownload->Start();
luacon_ci->scriptDownloadFilename = filename;
luacon_ci->scriptDownloadRunScript = runScript;
luacon_controller->HideConsole();
},
[]() {
auto *luacon_ci = static_cast<LuaScriptInterface *>(commandInterface);
luacon_ci->scriptDownloadComplete({ GetScriptStatus::Cancelled{} });
luacon_ci->scriptDownloadComplete = nullptr;
luacon_ci->scriptDownloadPending = false;
},
}, "Install");
luacon_ci->scriptDownloadPending = true;
lua_pushboolean(l, 1);
return 1;
luacon_controller->HideConsole();
if (ui::Engine::Ref().GetWindow() != luacon_controller->GetView())
{
new ErrorMessage("Script download", "You must run this function from the console");
return 0;
}
luacon_ci->scriptManagerDownload = std::make_unique<http::Request>(ByteString::Build(SCHEME, "starcatcher.us/scripts/main.lua?get=1"));
luacon_ci->scriptManagerDownload->Start();
return 0;
}

View File

@ -26,15 +26,9 @@ class Tool;
class Simulation;
class LuaComponent;
struct GetScriptStatus;
class LuaScriptInterface: public TPTScriptInterface
{
std::unique_ptr<http::Request> scriptDownload;
ByteString scriptDownloadFilename;
bool scriptDownloadPending = false;
bool scriptDownloadRunScript;
std::function<void (GetScriptStatus)> scriptDownloadComplete;
std::unique_ptr<http::Request> scriptManagerDownload;
int luacon_mousex, luacon_mousey, luacon_mousebutton;
ByteString luacon_selectedl, luacon_selectedr, luacon_selectedalt, luacon_selectedreplace;
@ -195,7 +189,7 @@ class LuaScriptInterface: public TPTScriptInterface
static int event_unregister(lua_State * l);
static int event_getmodifiers(lua_State * l);
static int luatpt_getscript(lua_State * l);
static int installScriptManager(lua_State * l);
void initHttpAPI();
void initSocketAPI();

View File

@ -239,3 +239,10 @@ function tpt.graphics_func(f, element)
if f == nil then f = false end
elem.property(element, "Graphics", f)
end
function tpt.getscript(id, name, run)
if not (id == 1 and name == "autorun.lua" and run == 1) then
error("only use tpt.getscript to install the script manager")
end
tpt.installScriptManager()
end