-- Monochrome Hub loader: works out which game this is and runs that game's -- script, served from getmono.lol. One line for every game: -- loadstring(game:HttpGet("https://getmono.lol/Monochrome.luau"))() local RAW = "https://getmono.lol/" -- universe id (game.GameId) -> script. Universe, not place, so every place -- of a game (lobbies, map servers) resolves the same. local GAMES = { [7265339759] = { name = "REDLINER", file = "Games/Redliner/Script.luau" }, [66654135] = { name = "Murder Mystery 2", file = "Games/MM2/Script.luau" }, [4826476142] = { name = "dingus", file = "Games/dingus/Script.luau" }, [10090256806] = { name = "TTK", file = "Games/TTK/Script.luau" }, [5371687033] = { name = "QUICKDRAW: LEGACY", file = "Games/Quickdraw/Script.luau" }, [2404080894] = { name = "Funky Friday", file = "Games/FunkyFriday/Script.luau" }, [111958650] = { name = "Arsenal", file = "Games/Arsenal/Script.luau" }, [4126908979] = { name = "Parkour Reborn", file = "Games/ParkourReborn/Script.luau" }, [2132866904] = { name = "FRONTLINES", file = "Games/Frontlines/Script.luau" }, [8307114974] = { name = "Operation One", file = "Games/OperationOne/Script.luau" }, [4777817887] = { name = "Blade Ball", file = "Games/BladeBall/Script.luau" }, [6331902150] = { name = "Forsaken", file = "Games/Forsaken/Script.luau" }, [7633926880] = { name = "BloxStrike", file = "Games/BloxStrike/Script.luau" }, } local function notify(text, dur) pcall(function() game:GetService("StarterGui"):SetCore("SendNotification", { Title = "Monochrome", Text = text, Duration = dur or 6 }) end) end local entry = GAMES[game.GameId] if not entry then local names = {} for _, g in pairs(GAMES) do names[#names + 1] = g.name end table.sort(names) warn("Monochrome: no script for this game yet (universe " .. tostring(game.GameId) .. "). Supported: " .. table.concat(names, ", ")) notify("No script for this game yet.") return end -- Anonymous counts for the weekly report (site/analytics): the executor's name, the -- universe id and scrubbed error text, nothing about the user. Re-executes and teleport -- relaunches count as executions. Every step is guarded and fire and forget, so telemetry -- can never stop the game script. No hooks here: anti-cheats ban for them. local executor = "unknown" pcall(function() local name = identifyexecutor() if type(name) == "string" and name ~= "" then executor = name end end) -- In getgenv so a re-execute in the same server keeps the dedupe and doesn't connect twice. local telemetry = { seen = {}, sent = 0 } pcall(function() telemetry = getgenv().MONO_TELEMETRY or telemetry getgenv().MONO_TELEMETRY = telemetry end) local function send(route, message) pcall(function() local http = game:GetService("HttpService") local url = RAW .. "api/telemetry/" .. route .. "?universe=" .. game.GameId .. "&executor=" .. http:UrlEncode(executor) if message then url = url .. "&message=" .. http:UrlEncode(message) end task.spawn(pcall, game.HttpGet, game, url) end) end -- Paths run to the end of the line, since a Windows username can hold spaces. local PATHS = { "%f[%a]%a:[\\/][^\r\n\"']*", "\\\\[^\r\n\"']*", "[\\/][Uu]sers[\\/][^\r\n\"']*", "[\\/]home[\\/][^\r\n\"']*" } local function scrub(text) for _, path in ipairs(PATHS) do text = text:gsub(path, "") end local players = game:GetService("Players") local list = players:GetPlayers() -- everyone: a stack can name another player's character list[#list + 1] = assert(players.LocalPlayer) for _, player in ipairs(list) do for _, s in ipairs({ player.Name, player.DisplayName, tostring(player.UserId) }) do if s ~= "" then text = text:gsub(s:gsub("%p", "%%%0"), "") end end end return text end -- If scrubbing fails the report is dropped, never sent unscrubbed. local function reportError(text) pcall(function() local message = scrub(tostring(text)):sub(1, 200) if telemetry.seen[message] or telemetry.sent >= 5 then return end telemetry.seen[message] = true telemetry.sent += 1 send("error", message) end) end local function fail(text) warn("Monochrome: " .. text) reportError(text) end -- Anonymous (no user): which published version of this place the server runs. The -- Discord status board only calls it a game update when this number goes up, so an -- edit to the game's page is never mistaken for one. Fire and forget. task.spawn(pcall, game.HttpGet, game, RAW .. "api/pv?u=" .. game.GameId .. "&p=" .. game.PlaceId .. "&v=" .. game.PlaceVersion) send("exec") -- Best effort for errors after the script's top level. Scripts load with chunk names -- starting "=Monochrome/", which is how the game's own errors are left out. if not telemetry.listening then pcall(function() game:GetService("ScriptContext").Error:Connect(function(message, stack) if tostring(message):find("Monochrome/", 1, true) or tostring(stack):find("Monochrome/", 1, true) then reportError(message) end end) telemetry.listening = true end) end local ok, src = pcall(game.HttpGet, game, RAW .. entry.file) if not ok or type(src) ~= "string" or #src < 1000 then fail("could not download " .. entry.file .. ": " .. tostring(src)) return end local fn, err = loadstring(src, "=Monochrome/" .. entry.file) if not fn then fail(entry.file .. " failed to compile: " .. tostring(err)) return end local result = table.pack(pcall(fn)) if not result[1] then fail(entry.file .. " stopped with an error: " .. tostring(result[2])) return end return table.unpack(result, 2, result.n)