AutoSave 1.2.0: autoload, /autosaver load <name>, new-server detection, watcher reinstall

This commit is contained in:
RallyBlock committed 2026-10-26 12:29:56 -04:00
1 parent b9d0eb6afd
commit b037722432
1 file changed
+239 -32
+239 -32
View File
@@ -36,6 +36,7 @@ static int gChangeAtStart = 0;
static int gSavedCount = -1; static int gSavedCount = -1;
static bool gReady = false; static bool gReady = false;
static bool gPreview = true; static bool gPreview = true;
static bool gAutoLoad = false;
struct Row { struct Row {
ADDR obj; ADDR obj;
@@ -591,8 +592,9 @@ static void prefsToScript()
"$Pref::Server::AutoSave::Interval = %d;" "$Pref::Server::AutoSave::Interval = %d;"
"$Pref::Server::AutoSave::Chunk = %d;" "$Pref::Server::AutoSave::Chunk = %d;"
"$Pref::Server::AutoSave::Tick = %d;" "$Pref::Server::AutoSave::Tick = %d;"
"$Pref::Server::AutoSave::Preview = %d;", "$Pref::Server::AutoSave::Preview = %d;"
gEnabled ? 1 : 0, gInterval, gChunk, gTickMs, gPreview ? 1 : 0); "$Pref::Server::AutoSave::AutoLoad = %d;",
gEnabled ? 1 : 0, gInterval, gChunk, gTickMs, gPreview ? 1 : 0, gAutoLoad ? 1 : 0);
tsf_Eval(code); tsf_Eval(code);
} }
@@ -614,6 +616,9 @@ static void prefsFromScript()
value = varOf("$Pref::Server::AutoSave::Preview"); value = varOf("$Pref::Server::AutoSave::Preview");
if (!value.empty()) if (!value.empty())
gPreview = truthy(value); gPreview = truthy(value);
value = varOf("$Pref::Server::AutoSave::AutoLoad");
if (!value.empty())
gAutoLoad = truthy(value);
clampPrefs(); clampPrefs();
prefsToScript(); prefsToScript();
} }
@@ -1592,12 +1597,12 @@ static int clampInterval()
return gInterval; return gInterval;
} }
static bool gHostSeen = false; static int gServerId = 0;
static void reschedule() static void reschedule()
{ {
char code[128]; char code[128];
int delay = gHostSeen ? clampInterval() * 1000 : 5000; int delay = clampInterval() * 1000;
std::snprintf(code, sizeof(code), "$AutoSave::Schedule = schedule(%d, 0, autoSaveTick);", delay); std::snprintf(code, sizeof(code), "$AutoSave::Schedule = schedule(%d, 0, autoSaveTick);", delay);
tsf_Eval(code); tsf_Eval(code);
} }
@@ -1651,7 +1656,8 @@ function AutoSave_Help(%client)
AutoSave_Tell(%client, "/autosaver tick <milliseconds, 1-1000>"); AutoSave_Tell(%client, "/autosaver tick <milliseconds, 1-1000>");
AutoSave_Tell(%client, "/autosaver screenshot <on or off>"); AutoSave_Tell(%client, "/autosaver screenshot <on or off>");
AutoSave_Tell(%client, "/autosaver save"); AutoSave_Tell(%client, "/autosaver save");
AutoSave_Tell(%client, "/autosaver load"); AutoSave_Tell(%client, "/autosaver load <optional autosave name>");
AutoSave_Tell(%client, "/autosaver autoload <on or off>");
} }
function AutoSave_Busy() function AutoSave_Busy()
{ {
@@ -1697,19 +1703,74 @@ function AutoSave_ReloadTick()
$AutoSave::ReloadFile = ""; $AutoSave::ReloadFile = "";
serverDirectSaveFileLoad(%file, 3, "", 1, 0); serverDirectSaveFileLoad(%file, 3, "", 1, 0);
} }
function AutoSave_Reload(%client) function AutoSave_AutoLoadStart()
{
if (isEventPending($AutoSave::AutoLoadSchedule))
cancel($AutoSave::AutoLoadSchedule);
$AutoSave::AutoLoadTries = 0;
AutoSave_AutoLoadWait();
}
function AutoSave_AutoLoadWait()
{
if (!$missionRunning || !isObject(mainBrickGroup))
{
if ($AutoSave::AutoLoadTries++ < 240)
$AutoSave::AutoLoadSchedule = schedule(500, 0, AutoSave_AutoLoadWait);
else
echo("AutoSave: autoload gave up, the mission did not start");
return;
}
if ($GameModeArg !$= "")
{
echo("AutoSave: autoload skipped, a game mode is running");
return;
}
if ($SaveFileArg !$= "" || AutoSave_Busy() || getBrickCount() > 0)
{
echo("AutoSave: autoload skipped, the server already has bricks");
return;
}
%file = AutoSave_NewestFile();
if (%file $= "")
{
echo("AutoSave: autoload skipped, there is no autosave");
return;
}
echo("AutoSave: autoload " @ %file);
$AutoSave::AutoLoading = 1;
serverDirectSaveFileLoad(%file, 3, "", 1, 0);
}
function AutoSave_Reload(%client, %wanted)
{ {
if ($Game::MissionCleaningUp || AutoSave_Busy() || $AutoSave::ReloadFile !$= "") if ($Game::MissionCleaningUp || AutoSave_Busy() || $AutoSave::ReloadFile !$= "")
{ {
AutoSave_Tell(%client, "Bricks are still being cleared or loaded."); AutoSave_Tell(%client, "Bricks are still being cleared or loaded.");
return; return;
} }
%wanted = trim(%wanted);
if (%wanted $= "")
{
%file = AutoSave_NewestFile(); %file = AutoSave_NewestFile();
if (%file $= "") if (%file $= "")
{ {
AutoSave_Tell(%client, "There is no autosave to load."); AutoSave_Tell(%client, "There is no autosave to load.");
return; return;
} }
}
else
{
%file = autoSaveFind(%wanted);
if (getSubStr(%file, 0, 1) $= "*")
{
AutoSave_Tell(%client, "\c3" @ getSubStr(%file, 1, 10) @ "\c6 autosaves match \c3" @ %wanted @ "\c6. Type more of the name.");
return;
}
if (%file $= "")
{
AutoSave_Tell(%client, "No autosave named \c3" @ %wanted @ "\c6 in saves/autosave/.");
return;
}
}
%name = %client.getPlayerName(); %name = %client.getPlayerName();
messageAll('', "\c6[\c3AutoSave\c6] \c3" @ %name @ "\c6 is loading \c3saves/autosave/" @ fileName(%file) @ "\c6."); messageAll('', "\c6[\c3AutoSave\c6] \c3" @ %name @ "\c6 is loading \c3saves/autosave/" @ fileName(%file) @ "\c6.");
$AutoSave::ReloadFile = %file; $AutoSave::ReloadFile = %file;
@@ -1734,7 +1795,7 @@ function AutoSave_Command(%client, %cmd, %arg)
} }
if (%cmd $= "status") if (%cmd $= "status")
{ {
AutoSave_Tell(%client, "Enabled \c3" @ ($AutoSave::Enabled ? "on" : "off") @ "\c6, every \c3" @ $AutoSave::Interval @ "\c6 seconds, \c3" @ $AutoSave::Chunk @ "\c6 bricks per slice, \c3" @ $AutoSave::Tick @ "\c6 ms between slices, screenshots \c3" @ ($AutoSave::Preview ? "on" : "off") @ "\c6."); AutoSave_Tell(%client, "Enabled \c3" @ ($AutoSave::Enabled ? "on" : "off") @ "\c6, every \c3" @ $AutoSave::Interval @ "\c6 seconds, \c3" @ $AutoSave::Chunk @ "\c6 bricks per slice, \c3" @ $AutoSave::Tick @ "\c6 ms between slices, screenshots \c3" @ ($AutoSave::Preview ? "on" : "off") @ "\c6, autoload \c3" @ ($AutoSave::AutoLoad ? "on" : "off") @ "\c6.");
return; return;
} }
if (!AutoSave_Admin(%client)) if (!AutoSave_Admin(%client))
@@ -1772,7 +1833,24 @@ function AutoSave_Command(%client, %cmd, %arg)
} }
if (%cmd $= "load" || %cmd $= "reload") if (%cmd $= "load" || %cmd $= "reload")
{ {
AutoSave_Reload(%client); AutoSave_Reload(%client, %arg);
return;
}
if (%cmd $= "autoload")
{
%arg = strlwr(%arg);
if (%arg $= "on" || %arg $= "1")
%on = 1;
else if (%arg $= "off" || %arg $= "0")
%on = 0;
else
{
AutoSave_Tell(%client, "Use \c3/autosaver autoload on\c6 or \c3/autosaver autoload off\c6.");
return;
}
$AutoSave::AutoLoad = %on;
autoSaveApply();
messageAll('', "\c6[\c3AutoSave\c6] \c3" @ %name @ "\c6 turned autoload \c3" @ (%on ? "on" : "off") @ "\c6. " @ (%on ? "The newest autosave loads when the server starts." : ""));
return; return;
} }
if (%cmd $= "preview" || %cmd $= "screenshot" || %cmd $= "screenshots") if (%cmd $= "preview" || %cmd $= "screenshot" || %cmd $= "screenshots")
@@ -1834,21 +1912,25 @@ function AutoSave_Command(%client, %cmd, %arg)
} }
AutoSave_Tell(%client, "Unknown command. Type \c3/autosaver help\c6."); AutoSave_Tell(%client, "Unknown command. Type \c3/autosaver help\c6.");
} }
function serverCmdautosaver(%client, %cmd, %arg) function AutoSave_Join(%a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10)
{ {
AutoSave_Command(%client, %cmd, %arg); return trim(%a1 SPC %a2 SPC %a3 SPC %a4 SPC %a5 SPC %a6 SPC %a7 SPC %a8 SPC %a9 SPC %a10);
} }
function serverCmdAutosaver(%client, %cmd, %arg) function serverCmdautosaver(%client, %cmd, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10)
{ {
AutoSave_Command(%client, %cmd, %arg); AutoSave_Command(%client, %cmd, AutoSave_Join(%a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10));
} }
function serverCmdAutoSaver(%client, %cmd, %arg) function serverCmdAutosaver(%client, %cmd, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10)
{ {
AutoSave_Command(%client, %cmd, %arg); AutoSave_Command(%client, %cmd, AutoSave_Join(%a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10));
} }
function serverCmdAUTOSAVER(%client, %cmd, %arg) function serverCmdAutoSaver(%client, %cmd, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10)
{ {
AutoSave_Command(%client, %cmd, %arg); AutoSave_Command(%client, %cmd, AutoSave_Join(%a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10));
}
function serverCmdAUTOSAVER(%client, %cmd, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10)
{
AutoSave_Command(%client, %cmd, AutoSave_Join(%a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10));
} }
$AutoSave::ChatInstalled = 1; $AutoSave::ChatInstalled = 1;
)TS"; )TS";
@@ -1890,9 +1972,9 @@ package AutoSaveWatch
{ {
%client = findLocalClient(); %client = findLocalClient();
if (isObject(%client)) if (isObject(%client))
AutoSave_Command(%client, getWord(%body, 1), getWord(%body, 2)); AutoSave_Command(%client, getWord(%body, 1), getWords(%body, 2));
else else
commandToServer('autosaver', getWord(%body, 1), getWord(%body, 2)); commandToServer('autosaver', getWord(%body, 1), getWords(%body, 2));
Canvas.popDialog(newMessageHud); Canvas.popDialog(newMessageHud);
return; return;
} }
@@ -1918,6 +2000,11 @@ package AutoSaveWatch
Parent::ServerLoadSaveFile_End(); Parent::ServerLoadSaveFile_End();
$AutoSave::Loading = 0; $AutoSave::Loading = 0;
$AutoSave::Change++; $AutoSave::Change++;
if ($AutoSave::AutoLoading)
{
$AutoSave::AutoLoading = 0;
autoSaveBaseline();
}
} }
function serverCmdCancelSaveFileUpload(%client) function serverCmdCancelSaveFileUpload(%client)
{ {
@@ -1925,11 +2012,16 @@ package AutoSaveWatch
if (!isObject($Server_LoadFileObj)) if (!isObject($Server_LoadFileObj))
$AutoSave::Loading = 0; $AutoSave::Loading = 0;
$AutoSave::Change++; $AutoSave::Change++;
$AutoSave::AutoLoading = 0;
} }
function destroyServer() function destroyServer()
{ {
$AutoSave::Loading = 0; $AutoSave::Loading = 0;
$AutoSave::AutoLoading = 0;
if (isEventPending($AutoSave::AutoLoadSchedule))
cancel($AutoSave::AutoLoadSchedule);
Parent::destroyServer(); Parent::destroyServer();
autoSaveRewatch();
} }
function serverCmdAddEvent(%client, %enabled, %inputEventIdx, %delay, %targetIdx, %NTNameIdx, %outputEventIdx, %par1, %par2, %par3, %par4) function serverCmdAddEvent(%client, %enabled, %inputEventIdx, %delay, %targetIdx, %NTNameIdx, %outputEventIdx, %par1, %par2, %par3, %par4)
{ {
@@ -2120,24 +2212,42 @@ static void autoSaveTick(ADDR obj, int argc, const char *argv[])
(void)argc; (void)argc;
(void)argv; (void)argv;
notePreview(); notePreview();
if (!hosting()) { if (gServerId != 0 && gEnabled && gPhase == 0 && hosting())
gHostSeen = false;
reschedule();
return;
}
if (!gHostSeen) {
gHostSeen = true;
prefsFromScript();
BlPrintf("%s: $Pref::Server::AutoSave: %s, every %d seconds, %d bricks per slice, %d ms, preview %s", PROJECT_NAME,
gEnabled ? "on" : "off", gInterval, gChunk, gTickMs, gPreview ? "on" : "off");
reschedule();
return;
}
if (gEnabled && gPhase == 0)
beginSave(); beginSave();
reschedule(); reschedule();
} }
static void scheduleHostCheck()
{
tsf_Eval("if (isEventPending($AutoSave::HostSchedule)) cancel($AutoSave::HostSchedule);"
"$AutoSave::HostSchedule = schedule(5000, 0, autoSaveHostCheck);");
}
/* Each mission creates a new MissionGroup, so its id tells servers apart even
when one stops and the next starts between two save intervals. */
static void autoSaveHostCheck(ADDR obj, int argc, const char *argv[])
{
(void)obj;
(void)argc;
(void)argv;
ADDR missionGroup = hosting() ? tsf_FindObject("MissionGroup") : 0;
int id = missionGroup != 0 ? *(int *)(missionGroup + 0x20) : 0;
if (id != 0 && id != gServerId) {
gServerId = id;
gHaveBaseline = false;
prefsFromScript();
BlPrintf("%s: $Pref::Server::AutoSave: %s, every %d seconds, %d bricks per slice, %d ms, screenshots %s, autoload %s", PROJECT_NAME,
gEnabled ? "on" : "off", gInterval, gChunk, gTickMs, gPreview ? "on" : "off", gAutoLoad ? "on" : "off");
if (gAutoLoad && truthy(callFn1("isFunction", "AutoSave_AutoLoadStart")))
tsf_Eval("AutoSave_AutoLoadStart();");
tsf_Eval("if (isEventPending($AutoSave::Schedule)) cancel($AutoSave::Schedule);");
reschedule();
} else if (id == 0) {
gServerId = 0;
}
scheduleHostCheck();
}
static char gReturnText[32]; static char gReturnText[32];
static const char *autoSaveNow(ADDR obj, int argc, const char *argv[]) static const char *autoSaveNow(ADDR obj, int argc, const char *argv[])
@@ -2161,6 +2271,72 @@ static const char *autoSaveNow(ADDR obj, int argc, const char *argv[])
return gReturnText; return gReturnText;
} }
static char gFoundPath[MAX_PATH];
/* Returns saves/autosave/<name>.bls for an exact name (with or without .bls,
any case) or for the one file whose name contains the text. "*N" means N
files contain it, "" means none. Names cannot leave the folder. */
static const char *autoSaveFind(ADDR obj, int argc, const char *argv[])
{
(void)obj;
(void)argc;
std::string name = argv[1] != 0 ? argv[1] : "";
char folder[MAX_PATH];
WIN32_FIND_DATAA found;
HANDLE handle;
std::string match;
std::string lowerName;
int prefixCount = 0;
while (!name.empty() && name[0] == ' ')
name.erase(0, 1);
while (!name.empty() && name[name.size() - 1] == ' ')
name.erase(name.size() - 1);
if (_strnicmp(name.c_str(), "saves/autosave/", 15) == 0)
name.erase(0, 15);
if (name.size() >= 4 && _stricmp(name.c_str() + name.size() - 4, ".bls") == 0)
name.erase(name.size() - 4);
if (name.empty() || name.find_first_of("/\\:*?\"<>|") != std::string::npos || name.find("..") != std::string::npos)
return "";
if (!gameFile(folder, MAX_PATH, "autosave\\*.bls"))
return "";
handle = FindFirstFileA(folder, &found);
if (handle == INVALID_HANDLE_VALUE)
return "";
do {
if (found.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
continue;
std::string file = found.cFileName;
if (file.size() < 4 || _stricmp(file.c_str() + file.size() - 4, ".bls") != 0)
continue;
std::string base = file.substr(0, file.size() - 4);
if (_stricmp(base.c_str(), name.c_str()) == 0) {
match = file;
prefixCount = 1;
break;
}
if (lowerName.empty()) {
lowerName = name;
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower);
}
std::transform(base.begin(), base.end(), base.begin(), ::tolower);
if (base.find(lowerName) != std::string::npos) {
if (prefixCount == 0)
match = file;
prefixCount++;
}
} while (FindNextFileA(handle, &found));
FindClose(handle);
if (prefixCount == 0)
return "";
if (prefixCount > 1) {
std::snprintf(gFoundPath, sizeof(gFoundPath), "*%d", prefixCount);
return gFoundPath;
}
std::snprintf(gFoundPath, sizeof(gFoundPath), "saves/autosave/%s", match.c_str());
return gFoundPath;
}
static char gNewestPath[MAX_PATH]; static char gNewestPath[MAX_PATH];
static const char *autoSaveNewest(ADDR obj, int argc, const char *argv[]) static const char *autoSaveNewest(ADDR obj, int argc, const char *argv[])
@@ -2213,6 +2389,7 @@ static void deferredInstall()
if (!gArmed) { if (!gArmed) {
gArmed = true; gArmed = true;
reschedule(); reschedule();
scheduleHostCheck();
} }
if (!gChat) if (!gChat)
installChat(); installChat();
@@ -2237,6 +2414,30 @@ static VOID CALLBACK installTimerProc(HWND hwnd, UINT message, UINT_PTR id, DWOR
} }
} }
/* destroyServer deactivates every package activated after the client
add-ons, AutoSaveWatch included, so it is installed again afterwards. */
static void autoSaveRewatch(ADDR obj, int argc, const char *argv[])
{
(void)obj;
(void)argc;
(void)argv;
gWatching = false;
tsf_Eval("$AutoSave::WatchInstalled = 0;");
if (gInstallTimer == 0)
gInstallTimer = SetTimer(0, 0, 500, installTimerProc);
}
/* A build that was just loaded from a file matches that file. */
static void autoSaveBaseline(ADDR obj, int argc, const char *argv[])
{
(void)obj;
(void)argc;
(void)argv;
gSavedChange = gChange;
gSavedCount = brickCount();
gHaveBaseline = true;
}
bool init() bool init()
{ {
if (!tsh_InitInternal()) if (!tsh_InitInternal())
@@ -2252,14 +2453,19 @@ bool init()
tsf_AddVar("AutoSave::Change", &gChange); tsf_AddVar("AutoSave::Change", &gChange);
tsf_AddVar("AutoSave::Loading", &gLoading); tsf_AddVar("AutoSave::Loading", &gLoading);
tsf_AddVar("AutoSave::Preview", &gPreview); tsf_AddVar("AutoSave::Preview", &gPreview);
tsf_AddVar("AutoSave::AutoLoad", &gAutoLoad);
installPreview(); installPreview();
tsf_AddConsoleFunc(0, 0, "autoSaveTick", autoSaveTick, "autoSaveTick() - timed brick save", 1, 1); tsf_AddConsoleFunc(0, 0, "autoSaveTick", autoSaveTick, "autoSaveTick() - timed brick save", 1, 1);
tsf_AddConsoleFunc(0, 0, "autoSaveChunk", autoSaveChunk, "autoSaveChunk() - continue a brick save", 1, 1); tsf_AddConsoleFunc(0, 0, "autoSaveChunk", autoSaveChunk, "autoSaveChunk() - continue a brick save", 1, 1);
tsf_AddConsoleFunc(0, 0, "autoSaveApply", autoSaveApply, "autoSaveApply() - clamp autosave prefs, write config/server/prefs.cs and reschedule", 1, 1); tsf_AddConsoleFunc(0, 0, "autoSaveApply", autoSaveApply, "autoSaveApply() - clamp autosave prefs, write config/server/prefs.cs and reschedule", 1, 1);
tsf_AddConsoleFunc(0, 0, "autoSave", autoSaveNow, "autoSave() - write a dated save in saves/autosave/", 1, 1); tsf_AddConsoleFunc(0, 0, "autoSave", autoSaveNow, "autoSave() - write a dated save in saves/autosave/", 1, 1);
tsf_AddConsoleFunc(0, 0, "autoSaveNewest", autoSaveNewest, "autoSaveNewest() - newest file in saves/autosave/", 1, 1); tsf_AddConsoleFunc(0, 0, "autoSaveNewest", autoSaveNewest, "autoSaveNewest() - newest file in saves/autosave/", 1, 1);
tsf_AddConsoleFunc(0, 0, "autoSaveFind", autoSaveFind, "autoSaveFind(name) - saves/autosave/ file by name or unique start", 2, 2);
tsf_AddConsoleFunc(0, 0, "autoSavePreviewPoll", autoSavePreviewPoll, "autoSavePreviewPoll() - report a finished save screenshot", 1, 1); tsf_AddConsoleFunc(0, 0, "autoSavePreviewPoll", autoSavePreviewPoll, "autoSavePreviewPoll() - report a finished save screenshot", 1, 1);
tsf_AddConsoleFunc(0, 0, "autoSaveTraceStep", autoSaveTraceStep, "autoSaveTraceStep() - trace part of a save screenshot", 1, 1); tsf_AddConsoleFunc(0, 0, "autoSaveTraceStep", autoSaveTraceStep, "autoSaveTraceStep() - trace part of a save screenshot", 1, 1);
tsf_AddConsoleFunc(0, 0, "autoSaveHostCheck", autoSaveHostCheck, "autoSaveHostCheck() - notice a new server", 1, 1);
tsf_AddConsoleFunc(0, 0, "autoSaveRewatch", autoSaveRewatch, "autoSaveRewatch() - install the change watcher again", 1, 1);
tsf_AddConsoleFunc(0, 0, "autoSaveBaseline", autoSaveBaseline, "autoSaveBaseline() - treat the current build as saved", 1, 1);
gInstallTimer = SetTimer(0, 0, 500, installTimerProc); gInstallTimer = SetTimer(0, 0, 500, installTimerProc);
if (gInstallTimer == 0) if (gInstallTimer == 0)
BlPrintf("%s: could not start the install timer", PROJECT_NAME); BlPrintf("%s: could not start the install timer", PROJECT_NAME);
@@ -2279,6 +2485,7 @@ bool deinit()
tsf_Eval("if (isEventPending($AutoSave::ChunkSchedule)) cancel($AutoSave::ChunkSchedule);"); tsf_Eval("if (isEventPending($AutoSave::ChunkSchedule)) cancel($AutoSave::ChunkSchedule);");
tsf_Eval("if (isEventPending($AutoSave::PreviewSchedule)) cancel($AutoSave::PreviewSchedule);"); tsf_Eval("if (isEventPending($AutoSave::PreviewSchedule)) cancel($AutoSave::PreviewSchedule);");
tsf_Eval("if (isEventPending($AutoSave::TraceSchedule)) cancel($AutoSave::TraceSchedule);"); tsf_Eval("if (isEventPending($AutoSave::TraceSchedule)) cancel($AutoSave::TraceSchedule);");
tsf_Eval("if (isEventPending($AutoSave::HostSchedule)) cancel($AutoSave::HostSchedule);");
if (gWatching) if (gWatching)
tsf_Eval("deactivatePackage(AutoSaveWatch);"); tsf_Eval("deactivatePackage(AutoSaveWatch);");
clearSave(); clearSave();