-- ChloeOps LiveOps adapter v1 -- Install ONE copy as a Script in ServerScriptService. -- Your server-side gameplay reads OrbitXPMultiplier and OrbitEventEnabled. -- Messages are best effort; newly started servers do not inherit active events. local HttpService = game:GetService("HttpService") local MessagingService = game:GetService("MessagingService") -- These names are part of the existing game/website protocol. local PROTOCOL_VERSION = 1 local MESSAGE_TOPIC = "OrbitLiveOps_v1" local LAST_COMMAND_ATTRIBUTE = "OrbitLastCommandId" local MAX_MESSAGE_BYTES = 1024 local MAX_COMMAND_ID_LENGTH = 80 local MAX_LIFETIME_SECONDS = 24 * 60 * 60 local CLOCK_SKEW_SECONDS = 30 local MAX_SUBSCRIBE_ATTEMPTS = 8 local MAX_RETRY_SECONDS = 60 local CONTROLS = { xp_multiplier = { attribute = "OrbitXPMultiplier", defaultValue = 1, accepts = function(value) return value >= 1 and value <= 5 end, toAttribute = function(value) return value end, }, event_toggle = { attribute = "OrbitEventEnabled", defaultValue = false, accepts = function(value) return value == 0 or value == 1 end, toAttribute = function(value) return value == 1 end, }, } -- Keep the last accepted command after expiry to reject delayed/replayed messages. local latestByAttribute = {} local function isFiniteNumber(value) return type(value) == "number" and value == value and value > -math.huge and value < math.huge end local function decodeCommand(data, now) if type(data) ~= "string" or #data > MAX_MESSAGE_BYTES then return nil end local decoded, command = pcall(HttpService.JSONDecode, HttpService, data) if not decoded or type(command) ~= "table" or command.version ~= PROTOCOL_VERSION then return nil end if type(command.id) ~= "string" or #command.id == 0 or #command.id > MAX_COMMAND_ID_LENGTH then return nil end if not isFiniteNumber(command.issuedAt) or not isFiniteNumber(command.expiresAt) then return nil end local lifetime = command.expiresAt - command.issuedAt if command.issuedAt > now + CLOCK_SKEW_SECONDS or command.expiresAt <= now or lifetime <= 0 or lifetime > MAX_LIFETIME_SECONDS then return nil end if type(command.kind) ~= "string" or not isFiniteNumber(command.value) then return nil end local control = CONTROLS[command.kind] if not control or not control.accepts(command.value) then return nil end return command, control end local function applyCommand(command, control, now) local previous = latestByAttribute[control.attribute] if previous and (previous.id == command.id or previous.issuedAt >= command.issuedAt) then return end local accepted = { id = command.id, issuedAt = command.issuedAt, } latestByAttribute[control.attribute] = accepted workspace:SetAttribute(control.attribute, control.toAttribute(command.value)) workspace:SetAttribute(LAST_COMMAND_ATTRIBUTE, command.id) task.delay(command.expiresAt - now, function() -- Compare this acceptance, not just the ID: older timers cannot undo newer state. if latestByAttribute[control.attribute] == accepted then workspace:SetAttribute(control.attribute, control.defaultValue) end end) end local function receiveMessage(packet) if type(packet) ~= "table" then return end local now = os.time() local command, control = decodeCommand(packet.Data, now) if command then applyCommand(command, control, now) end end local function subscribe() for attempt = 1, MAX_SUBSCRIBE_ATTEMPTS do local subscribed = pcall(function() MessagingService:SubscribeAsync(MESSAGE_TOPIC, receiveMessage) end) if subscribed then return end if attempt < MAX_SUBSCRIBE_ATTEMPTS then task.wait(math.min(2 ^ attempt, MAX_RETRY_SECONDS)) end end warn("ChloeOps: subscription unavailable. No live operations will be applied on this server.") end for _, control in pairs(CONTROLS) do workspace:SetAttribute(control.attribute, control.defaultValue) end task.spawn(subscribe)