{"id":"moxxum/round-manager","name":"round-manager","scope":"moxxum","platform":"roblox","description":"Match/round state machine: Lobby/Intermission/Active/Results, ready/spectate, map+mode voting","version":"0.1.0","latest":"0.1.0","versions":["0.1.0"],"license":"MIT","licenseRating":"safe","licenseCaveats":["The package archive does not include its license text; the license is declared in its manifest metadata."],"licenseVerified":false,"dependencies":{},"integrity":"5675afc7d7ce68cae2935bd19262692fb22bdb22ea82b743e1abc83636af1324","likes":0,"downloads":0,"install":"forest install moxxum/round-manager","url":"https://forest.dev/p/roblox/moxxum/round-manager","files":"https://api.forest.dev/ai/package/roblox/moxxum/round-manager/files","readme":"# round-manager\n\nMatch/round state machine — the RobloxWare backbone. States cycle\n`Lobby → Intermission → Active → Results → Lobby` with configurable durations,\nplayer ready/spectate handling, map/mode voting during Intermission, and a\n`StateChanged` signal per transition. Pure server-side state: replicate to clients\nthrough netkit however the game likes.\n\nServer realm: `RoundManager = \"moxxum/round-manager@0.1.0\"` under\n`[server-dependencies]`.\n\n## API\n\n```lua\nlocal rm = RoundManager.new(config?)\nrm.StateChanged:Connect(function(newState, oldState, data) end)\nrm:Start() / rm:Stop() / rm:Destroy()\nrm:GetState()            -- \"Lobby\" | \"Intermission\" | \"Active\" | \"Results\"\nrm:GetTimeRemaining()    -- seconds left in a timed state, nil in Lobby\nrm:GetParticipants()     -- snapshot taken when the round went Active\nrm:SetReady(player, bool) / rm:IsReady(player)          -- used when RequireReady\nrm:SetSpectating(player, bool) / rm:IsSpectating(player)\nrm:VoteMap(player, name) / rm:VoteMode(player, name)    -- Intermission only → boolean\nrm:GetMapVotes() / rm:GetModeVotes()                    -- { [option] = count }\nrm:EndRoundEarly(results?)                              -- Active → Results now\n```\n\nConfig (defaults): `IntermissionSeconds = 15`, `ActiveSeconds = 120`,\n`ResultsSeconds = 10`, `MinPlayers = 2`, `RequireReady = false`, `Maps`/`Modes`\n(vote options; omit to skip voting).\n\n`StateChanged` data: `{ Map, Mode, Duration, Participants, Results }` (fields present\nwhere they make sense; `Results` is whatever you passed to `EndRoundEarly`).\n\nRules: below `MinPlayers` the machine sits in Lobby (Intermission falls back to\nLobby if players leave). A round ends early when every participant leaves. Ready\nflags clear on each return to Lobby; ties and no-vote rounds pick randomly.\n\n## Example\n\n```lua\nlocal rm = RoundManager.new({\n\tIntermissionSeconds = GameConfig.Round.IntermissionSeconds,\n\tActiveSeconds = GameConfig.Round.ActiveSeconds,\n\tMinPlayers = 2,\n\tMaps = { \"Desert\", \"City\" },\n})\n\nrm.StateChanged:Connect(function(newState, _old, data)\n\tRemotes.RoundState:FireAll(newState, data.Map, data.Duration)\n\tif newState == \"Active\" then\n\t\tfor _, player in data.Participants do\n\t\t\tspawnIntoArena(player, data.Map)\n\t\tend\n\tend\nend)\n\nrm:Start()\n```\n\n## Test\n\n`rojo build test.project.json -o test.rbxl`, open in Studio, Play Solo, expect\n`[round-manager-test] ALL PASS` within ~15s (full cycle, voting, early end, spectate).\n","readmeTruncated":false}