Index: binaries/data/mods/public/globalscripts/utility.js =================================================================== --- binaries/data/mods/public/globalscripts/utility.js +++ binaries/data/mods/public/globalscripts/utility.js @@ -52,6 +52,38 @@ } /** + * Check if the data is contained in its stringification. + */ +function isEqualToStringified(data) +{ + return deepIncludes(data, JSON.parse(JSON.stringify(data))); +} + +/** + * Recursively check if the base is contained in the conatiner. + */ +function deepIncludes(base, container) +{ + if (typeof base == "object") + { + for (const key in base) + if (!deepIncludes(base[key], container[key])) + return false; + return true; + } + + if (typeof base == "array") + { + for (let i = 0; i < base.length; ++i) + if (!deepIncludes(base[i], container[i])) + return false; + return true; + } + + return base === container; +} + +/** * Removes prefixing path from a path or filename, leaving just the file's name (with extension) * * ie. a/b/c/file.ext -> file.ext Index: binaries/data/mods/public/gui/session/AutoFormation.js =================================================================== --- binaries/data/mods/public/gui/session/AutoFormation.js +++ binaries/data/mods/public/gui/session/AutoFormation.js @@ -56,7 +56,7 @@ */ getDefault() { - return this.defaultFormation == NULL_FORMATION ? undefined : this.defaultFormation; + return this.defaultFormation == NULL_FORMATION ? null : this.defaultFormation; } /** @@ -65,6 +65,6 @@ getNull() { let walkOnly = Engine.ConfigDB_GetValue("user", "gui.session.formationwalkonly") === "true"; - return walkOnly ? NULL_FORMATION : undefined; + return walkOnly ? NULL_FORMATION : null; } } Index: binaries/data/mods/public/simulation/helpers/Commands.js =================================================================== --- binaries/data/mods/public/simulation/helpers/Commands.js +++ binaries/data/mods/public/simulation/helpers/Commands.js @@ -4,6 +4,12 @@ function ProcessCommand(player, cmd) { + // Commands can only contain stringifiable data. If things cannot be + // stringified, this can yield an OOS on replay, since the replay uses + // stringified commands. + if (!isEqualToStringified(cmd)) + error("Found order containing non-stringifiable data: " + JSON.stringify(cmd)); + let cmpPlayer = QueryPlayerIDInterface(player); if (!cmpPlayer) return;