Build an Anime RNG Loop in Roblox
Add a secure anime RNG loop to an existing Roblox map with server-side logic.
Introduction
30 Second Summary
Your existing game map may represent hours of careful work. A new gameplay loop should add something exciting without disturbing the world you already built.
In this project, you will add a free Anime RNG roll loop to your existing map in Roblox Studio. The finished loop uses a server-authoritative design to control weighted random selection, cooldowns, and session ownership.
What You'll Build
You'll press ROLL over your existing map, see an anime-inspired archetype appear in its rarity color, and watch its ownership count rise without changing the map itself.
By the end of this project, you'll have:
- A map-matched roll panel where pressing ROLL reveals one of four anime-inspired archetypes with its rarity.
- A server-controlled roll path that selects weighted outcomes. Rapid requests grant nothing. Every connected player keeps a separate session count.
- A Team Test run where the existing map remains intact while rarity colors and ownership feedback update after each accepted roll.
- Secret Mission: A global roll feed that shows every connected tester the same successful roll.
Are there any prerequisites?
You'll need a Roblox account, an existing map open in Roblox Studio on Windows, Visual Studio Code with GitHub Copilot Free, the Studio MCP server enabled, and collaboration mode ready for Team Test. A second collaborator is only needed for the optional two-player isolation check and Secret Mission demonstration.
Before We Start
Before any hands-on work, lock in the boundaries that protect your existing Roblox Studio map. Its geometry stays unchanged while you add a free Anime RNG loop with session-only inventory through manually started Team Test, without standard Test, F5, MCP playtest, or start_stop_play.
Inspect the Existing Game Safely
The existing map is the asset this feature must protect. A careless edit could replace models, scripts, or interfaces that already make the place work.
The Roblox Studio MCP server from earlier can map the current data model before it writes anything. You will use that baseline to approve three isolated branches.
In this step, get ready to:
- Create a read-only inventory of the existing game structure.
- Report naming conflicts before any edits begin.
- Add an isolated RNG scaffold without changing the map.
Audit the existing data model
A read-only inventory creates a before picture of the place. This makes every later change easy to identify.
- Ask the connected MCP agent to record every existing child of Workspace with a read-only tree search.
- Ask the agent to inventory ReplicatedStorage with a read-only tree search.
- Ask the agent to inventory ServerScriptService with a read-only tree search.
- Ask the agent to inventory StarterGui with a read-only tree search.
- Ask the agent to search existing scripts under those four services for AnimeRNG, RequestRoll, or AnimeRNGGui.
- Require the agent to report every naming conflict before it edits the place.
- Require the agent to preserve the four tree reports as the baseline for the final comparison.
Good. The baseline now gives every approved edit a clear boundary.
Did the Agent Find a Conflict?
- Keep the existing place unchanged while you investigate the conflict.
- Ask the agent to inspect the conflicting instance without modifying it.
- Confirm whether the instance belongs to an existing system before approving the scaffold.
Use the conflict report to decide how to preserve the existing object. Help me resolve an Anime RNG naming conflict safely.
Create the isolated service branches
A RemoteFunction creates a request path between the future roll button and the server. The RequestRoll object provides that path without placing game logic in the map.
This is the first point where the agent can write to your place. The approved paths keep its changes boxed away from Workspace.
- Limit the agent's write permission to the three approved branches in this step.
- Ask the agent to create a Folder named AnimeRNG under ReplicatedStorage.
- Ask the agent to create a RemoteFunction named RequestRoll inside ReplicatedStorage.AnimeRNG.
- Expand ReplicatedStorage.AnimeRNG in the Explorer panel to confirm that RequestRoll is its only new child.
- Ask the agent to create a Folder named AnimeRNG under ServerScriptService.
- Expand ServerScriptService.AnimeRNG in the Explorer panel to confirm that the folder is empty.
- Require the agent to report whether it changed any pre-existing instance.
You should now see the network entry point under ReplicatedStorage. You should also see an empty server folder ready for the logic added later.
Add the GUI shell and verify the delta
A ScreenGui overlays interface elements without changing map geometry. This shell reserves space for the roll controls while leaving the world intact.
- Ask the agent to create a ScreenGui named AnimeRNGGui under StarterGui.
- Ask the agent to create a Frame named Main inside StarterGui.AnimeRNGGui.
- Expand StarterGui.AnimeRNGGui in the Explorer panel to confirm that Main appears beneath it.
- Ask the agent to add RollButton, ResultLabel, OddsLabel, and InventoryLabel under Main.
- Review the panel in the Studio viewport to confirm that it overlays the map.
- Ask the agent to style only StarterGui.AnimeRNGGui using the current viewport as its visual reference.
- Ask the agent to capture the viewport with the new panel beside the existing map.
- Ask the agent to produce an after tree report for the four audited services.
Before you compare the reports, do you expect any pre-existing Workspace child to be missing?
- Compare the before report with the after report for Workspace.
- Confirm that every pre-existing Workspace child still appears.
- Confirm that the three approved branches appear at their exact paths.
- Confirm that the conflict report contains no unresolved conflict involving AnimeRNG, RequestRoll, or AnimeRNGGui.
- Review the viewport capture to confirm that the panel matches the map's visual language without moving its geometry.
You should see every pre-existing Workspace child unchanged. The reports should show new objects only inside the three approved branches.
That safety boundary is now in place. Your RNG shell has a home without taking ownership of the map.
✔️ Awesome, I've got everything!
Great. Your map remains intact while the isolated storage, server, and interface branches are ready for the roll loop.
ⓧ I'd like to double check the full code
- Confirm that ReplicatedStorage.AnimeRNG is a Folder.
- Confirm that ReplicatedStorage.AnimeRNG contains a RemoteFunction named RequestRoll.
- Confirm that ServerScriptService.AnimeRNG is an empty Folder.
- Confirm that StarterGui.AnimeRNGGui is a ScreenGui containing a Frame named Main.
- Confirm that Main contains a TextButton named RollButton.
- Confirm that Main contains TextLabel objects named ResultLabel, OddsLabel, and InventoryLabel.
- Confirm that every pre-existing Workspace child remains unchanged.
- Confirm that the panel is positioned over the existing map without changing its geometry.
Did the Scaffold Touch the Map?
- Revert the agent's latest write before continuing.
- Restore the pre-existing Workspace tree from the baseline report.
- Ask the agent to recreate only the three approved branches.
Share both tree reports when asking for help. Help me isolate an unexpected Roblox Studio MCP edit.
Your existing map is protected by a verified integration boundary. Next, you will turn the empty panel into a clickable roll prototype.
Build the Placeholder Roll Panel
Your isolated roll GUI now sits over the existing map in Roblox Studio. Its button still has no response.
A temporary LocalScript gives the button one display-only behavior. The placeholder exposes the missing server response without choosing a reward on the client.
In this step, get ready to:
- Add a temporary display-only response to the roll button.
- Audit the script for client-side reward logic.
- Run the placeholder through Team Test.
Create the temporary client response
The Roblox Studio MCP server can add this script without touching the map. This edit stays inside the isolated GUI branch.
- Switch back to the MCP agent from the previous step.
- Ask the agent to create a LocalScript named RNGClient directly under StarterGui.AnimeRNGGui.
- Require the source of RNGClient to match this code exactly:
local gui = script.Parent
local main = gui:WaitForChild("Main")
local rollButton = main:WaitForChild("RollButton") :: TextButton
local resultLabel = main:WaitForChild("ResultLabel") :: TextLabel
rollButton.Activated:Connect(function()
resultLabel.Text = "Waiting for server..."
end)
What Does This Code Do?
- The gui variable refers to the existing AnimeRNGGui.
- The main variable locates the panel that already contains the interface controls.
- The rollButton variable provides a typed reference to RollButton.
- The resultLabel variable provides a typed reference to ResultLabel.
- The Activated connection changes one label when the player presses the button.
- Approve only the creation of StarterGui.AnimeRNGGui.RNGClient if your AI client requests permission.
- Confirm that RNGClient appears directly under AnimeRNGGui in the Studio Explorer.
- Select RNGClient in the Explorer to confirm its source matches the reference.
Does the Script Look Different?
Check that RNGClient is directly under AnimeRNGGui. A script placed under Main gives script.Parent the wrong starting point.
Check the capitalization of Main, RollButton, and ResultLabel. Each name must match the existing GUI object exactly.
Help me check the temporary RNGClient script.
✔️ Awesome, I've got everything!
Great. Your isolated GUI now contains the temporary RNGClient script.
ⓧ I'd like to double check the full code
Compare the complete temporary script with StarterGui.AnimeRNGGui.RNGClient in Studio.
local gui = script.Parent
local main = gui:WaitForChild("Main")
local rollButton = main:WaitForChild("RollButton") :: TextButton
local resultLabel = main:WaitForChild("ResultLabel") :: TextLabel
rollButton.Activated:Connect(function()
resultLabel.Text = "Waiting for server..."
end)
Audit the client boundary
A secure client-server boundary keeps reward decisions away from player-controlled code. This temporary client only changes visible text.
- Ask the MCP agent to use script_read on StarterGui.AnimeRNGGui.RNGClient.
- Require the agent to report whether the script contains a random-number generator.
- Require the agent to report whether the script contains a reward table.
- Require the agent to report whether the script mutates an inventory.
- Require the agent to report whether the script selects an outcome.
- Compare the agent's report with the temporary script.
What Does the Audit Prove?
The client knows which button was pressed. It also knows which label to update.
The client has no logic for deciding a reward. That missing decision creates the limitation you will observe in Team Test.
Prove the placeholder in Team Test
Team Test runs the existing map through the collaborative workflow required by this project. The temporary response gives you a visible result without pretending that a client-selected reward is secure.
Keep This Test Manual
Use Team Test for this check. Do not select the standard Test option.
Keep control of the session inside Studio. Do not ask the MCP agent to call start_stop_play.
Before you start the session, do you think ROLL can produce an archetype with this script alone?
- Select Team Test from Studio's testing dropdown.
- Press Play yourself to start the session.
- Wait for the existing map to finish loading.
- Press ROLL once.
You'll see ResultLabel change to Waiting for server.... The existing map remains unchanged.
This planned limitation proves that the button reaches the temporary client handler. It also exposes the missing trusted server result.
- Click End Session from the Team Test session.
That is your first playable slice complete. Next, you will give the button a server-authoritative result.
Add the Server-Authoritative Roll
The placeholder from the previous step proved that the roll button reaches its temporary client script. It also exposed the missing trusted response.
A server-authoritative boundary gives the server control over every reward decision. You will connect the existing button to that boundary through a RemoteFunction.
In this step, get ready to:
- Define weighted outcomes in server-only code.
- Build the server-owned roll service.
- Replace the placeholder client with a response renderer.
Build the server-owned roll path
A weighted random selection gives each outcome a section of one total range. Keeping the table under ServerScriptService prevents the client from choosing a reward.
- Switch back to the AI client connected to the Roblox Studio MCP server.
- Ask the agent to create a ModuleScript named RollDefinitions inside ServerScriptService.AnimeRNG.
- Require the agent to use the exact source shown in the full-code reference below.
- Require the agent to leave the existing map unchanged.
✔️ Awesome, I've got everything!
Great. Your server-only outcome table contains the four required archetypes.
ⓧ I'd like to double check the full code
return {
{
Name = "Rookie Hero",
Rarity = "Common",
Weight = 60,
Color = Color3.fromRGB(205, 205, 205),
},
{
Name = "Flame Swordsman",
Rarity = "Rare",
Weight = 25,
Color = Color3.fromRGB(80, 170, 255),
},
{
Name = "Shadow Monarch",
Rarity = "Epic",
Weight = 10,
Color = Color3.fromRGB(185, 90, 255),
},
{
Name = "Celestial Warrior",
Rarity = "Legendary",
Weight = 5,
Color = Color3.fromRGB(255, 195, 65),
},
}
How Does the Outcome Table Work?
- Each entry stores one outcome name.
- Each rarity has a corresponding display color.
- The four weights total 100.
- A larger weight gives an outcome a larger section of the selection range.
- Ask the agent to read game.ServerScriptService.AnimeRNG.RollDefinitions with script_read.
- Confirm the returned source contains all four outcome names.
- Confirm the returned weights total 100.
The four outcomes now exist in server-only code. The existing map remains unchanged.
Is the Outcome Table Incomplete?
Confirm that RollDefinitions is a ModuleScript inside ServerScriptService.AnimeRNG.
Compare every name plus weight with the full-code reference. A missing comma can stop the module from loading.
Help me check the outcome table.
The service converts the outcome table into a protected roll. It stores cooldown timestamps plus ownership tables under each player's UserId.
- Ask the agent to create a Script named RollService inside ServerScriptService.AnimeRNG.
- Require the agent to use the exact source shown in the full-code reference below.
- Require the agent to preserve ReplicatedStorage.AnimeRNG.RequestRoll.
✔️ Awesome, I've got everything!
Great. Your server now owns selection, cooldown enforcement, plus session ownership.
ⓧ I'd like to double check the full code
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remotes = ReplicatedStorage:WaitForChild("AnimeRNG")
local requestRoll = remotes:WaitForChild("RequestRoll") :: RemoteFunction
local RollDefinitions = require(script.Parent:WaitForChild("RollDefinitions") :: ModuleScript)
local random = Random.new()
local lastRollAt = {}
local inventories = {}
local COOLDOWN_SECONDS = 1.5
local function chooseOutcome()
local totalWeight = 0
for _, outcome in ipairs(RollDefinitions) do
totalWeight += outcome.Weight
end
local cursor = random:NextNumber(0, totalWeight)
for _, outcome in ipairs(RollDefinitions) do
cursor -= outcome.Weight
if cursor <= 0 then
return outcome
end
end
return RollDefinitions[#RollDefinitions]
end
local function onRequestRoll(player)
local userId = player.UserId
local now = time()
local previousRoll = lastRollAt[userId]
if previousRoll and now - previousRoll < COOLDOWN_SECONDS then
return {
Success = false,
Remaining = COOLDOWN_SECONDS - (now - previousRoll),
}
end
lastRollAt[userId] = now
local outcome = chooseOutcome()
local inventory = inventories[userId]
if not inventory then
inventory = {}
inventories[userId] = inventory
end
inventory[outcome.Name] = (inventory[outcome.Name] or 0) + 1
return {
Success = true,
Name = outcome.Name,
Rarity = outcome.Rarity,
Color = outcome.Color,
Count = inventory[outcome.Name],
}
end
requestRoll.OnServerInvoke = onRequestRoll
Players.PlayerRemoving:Connect(function(player)
lastRollAt[player.UserId] = nil
inventories[player.UserId] = nil
end)
How Does the Server Protect Each Roll?
- The chooseOutcome() function calculates the total available weight.
- The server uses Random:NextNumber() to select a point inside that range.
- The onRequestRoll() function rejects requests made inside the 1.5 second cooldown.
- The inventories table records copies owned during the current server session.
- The cleanup connection removes one player's temporary data when they leave.
- Ask the agent to read game.ServerScriptService.AnimeRNG.RollService with script_read.
- Confirm the returned source defines chooseOutcome().
- Confirm the returned source defines onRequestRoll().
- Confirm COOLDOWN_SECONDS is set to 1.5.
The server branch can now return one validated result for each accepted request. Cooldown requests return without adding an outcome.
Does the Service Reference a Missing Instance?
Confirm that RequestRoll remains inside ReplicatedStorage.AnimeRNG.
Confirm that RollDefinitions shares the ServerScriptService.AnimeRNG folder with RollService.
Help me diagnose the server paths.
Replace the placeholder client
The client needs to request a roll without supplying a desired outcome. Its response handler displays only the values returned by the server.
- Select StarterGui.AnimeRNGGui.RNGClient in the Studio Explorer window.
- Ask the agent to replace the temporary source with the exact full-code reference below.
- Require the agent to keep RNGClient directly under AnimeRNGGui.
✔️ Awesome, I've got everything!
Great. Your button now requests a server result without choosing the reward locally.
ⓧ I'd like to double check the full code
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remotes = ReplicatedStorage:WaitForChild("AnimeRNG")
local requestRoll = remotes:WaitForChild("RequestRoll") :: RemoteFunction
local gui = script.Parent
local main = gui:WaitForChild("Main")
local rollButton = main:WaitForChild("RollButton") :: TextButton
local resultLabel = main:WaitForChild("ResultLabel") :: TextLabel
local oddsLabel = main:WaitForChild("OddsLabel") :: TextLabel
local inventoryLabel = main:WaitForChild("InventoryLabel") :: TextLabel
local rolling = false
local defaultTextColor = Color3.fromRGB(255, 255, 255)
oddsLabel.Text = "Common 60% | Rare 25% | Epic 10% | Legendary 5%"
resultLabel.Text = "Press ROLL"
inventoryLabel.Text = "No session rolls yet"
local function onRollActivated()
if rolling then
return
end
rolling = true
rollButton.Text = "ROLLING..."
local success, response = pcall(requestRoll.InvokeServer, requestRoll)
if not success then
resultLabel.Text = "Server request failed"
resultLabel.TextColor3 = defaultTextColor
elseif response.Success then
resultLabel.Text = response.Name .. " [" .. response.Rarity .. "]"
resultLabel.TextColor3 = response.Color
inventoryLabel.Text = "Owned " .. response.Name .. ": " .. tostring(response.Count)
else
resultLabel.Text = "Wait before rolling again."
resultLabel.TextColor3 = defaultTextColor
end
rollButton.Text = "ROLL"
rolling = false
end
rollButton.Activated:Connect(onRollActivated)
What Does the Final Client Do?
- The client finds the existing RequestRoll remote.
- The rolling value blocks overlapping button requests.
- The pcall() wrapper catches a failed server request.
- A successful response supplies the name, rarity, color, plus ownership count.
- A rejected response displays Wait before rolling again..
- Ask the agent to read game.StarterGui.AnimeRNGGui.RNGClient with script_read.
- Confirm the source calls RequestRoll:InvokeServer() through pcall().
- Confirm the source contains no random number generator.
- Confirm the source contains no reward table.
- Confirm the source contains no inventory mutation.
The client now renders a trusted response from the server. It never tells the server which reward to grant.
Does the Client Reference the Wrong Parent?
Confirm that RNGClient remains directly under AnimeRNGGui. The script.Parent reference depends on that placement.
Confirm that RollButton plus all three labels remain inside Main.
Help me check the client hierarchy.
Verify the trusted roll in Team Test
Before you start the session, do you expect the first request to succeed or hit the cooldown?
- Leave the standard Test option unselected.
- Keep the MCP agent from calling start_stop_play.
- Return to the testing dropdown in Roblox Studio.
- Select Team Test.
- Press Play.
- Press ROLL once.
The panel shows one of the four archetypes with its rarity. The ownership label shows one copy of that outcome.
- Press ROLL again immediately.
You will see Wait before rolling again. in the result label. That rejection proves the server is enforcing the 1.5 second cooldown.
- Wait at least 1.5 seconds.
- Press ROLL again.
- Repeat the accepted-roll check several times.
- Confirm the existing map still loads unchanged.
You will receive exactly one archetype for each accepted request. Rapid requests will not grant another outcome.
- Click End Session.
You return to edit mode with the map unchanged. The Team Test session has ended cleanly.
Does the Roll Stay on Press ROLL?
Confirm that RollService is a Script inside ServerScriptService.AnimeRNG.
Confirm that RequestRoll is a RemoteFunction inside ReplicatedStorage.AnimeRNG.
Help me diagnose the roll request.
That is the trust boundary working: the server now chooses every outcome plus rejects rapid requests. Next, you will match the feedback to the visual language of the existing map.
Match the Interface to the Map
Your trusted server now returns weighted results to the existing roll panel. Each player's session inventory stays on the server.
The panel still needs to feel at home in the map. Its feedback also needs to remain readable during gameplay.
Readable odds expose the probabilities. Rarity color identifies the result's tier.
In this step, get ready to:
- Match the panel to the map's visual style.
- Confirm that each feedback label uses the server response.
- Test readability at busy plus dark viewpoints.
Capture the map's visual language
The map already has a visual language built from its lighting plus its environment. A focused capture gives the agent a reference without inviting changes to the world geometry.
The palette needs the map's dominant background color. It also needs the map's accent color.
The map's text color completes the palette.
Matching an existing game's colors can be fiddly because the same shade looks different under bright lighting or deep shadow.
The existing GUI object names stay unchanged. The edit boundary remains StarterGui.AnimeRNGGui.
- Switch back to Roblox Studio from earlier.
- Frame a representative section of the existing map in the viewport.
- Ask your trusted MCP agent to capture the current map with screen_capture.
- Ask the agent to report the map's three-color palette.
- Ask the agent to apply that palette only within StarterGui.AnimeRNGGui.
- Ask the agent to provide a before-and-after tree report for the styling pass.
The report should show property changes only under StarterGui.AnimeRNGGui. The map geometry stays unchanged.
That careful boundary paid off. The roll panel now belongs in the map without replacing any of its world content.
Does the Report Include Map Changes?
- Reject any edit outside StarterGui.AnimeRNGGui.
- Reframe the viewport if the capture does not show the map's dominant visual style.
Ask for help with keeping the styling pass isolated.
Confirm the feedback wiring
Styling changes how the panel looks. The existing RNGClient still controls what each label displays.
A successful server response already contains the rarity color plus the ownership count. The client only displays those returned values.
- Ask the MCP agent to read game.StarterGui.AnimeRNGGui.RNGClient with script_read.
- Ask the agent to confirm that oddsLabel.Text lists all four percentages.
- Ask the agent to confirm that the success branch assigns response.Color to resultLabel.TextColor3.
- Ask the agent to confirm that inventoryLabel.Text uses response.Count.
- Compare the agent's report with the full reference below.
✔️ Awesome, I've got everything!
That is the feedback path confirmed. The client keeps the fixed odds visible while using the server's color plus count for successful rolls.
ⓧ I'd like to double check the full code
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remotes = ReplicatedStorage:WaitForChild("AnimeRNG")
local requestRoll = remotes:WaitForChild("RequestRoll") :: RemoteFunction
local gui = script.Parent
local main = gui:WaitForChild("Main")
local rollButton = main:WaitForChild("RollButton") :: TextButton
local resultLabel = main:WaitForChild("ResultLabel") :: TextLabel
local oddsLabel = main:WaitForChild("OddsLabel") :: TextLabel
local inventoryLabel = main:WaitForChild("InventoryLabel") :: TextLabel
local rolling = false
local defaultTextColor = Color3.fromRGB(255, 255, 255)
oddsLabel.Text = "Common 60% | Rare 25% | Epic 10% | Legendary 5%"
resultLabel.Text = "Press ROLL"
inventoryLabel.Text = "No session rolls yet"
local function onRollActivated()
if rolling then
return
end
rolling = true
rollButton.Text = "ROLLING..."
local success, response = pcall(requestRoll.InvokeServer, requestRoll)
if not success then
resultLabel.Text = "Server request failed"
resultLabel.TextColor3 = defaultTextColor
elseif response.Success then
resultLabel.Text = response.Name .. " [" .. response.Rarity .. "]"
resultLabel.TextColor3 = response.Color
inventoryLabel.Text = "Owned " .. response.Name .. ": " .. tostring(response.Count)
else
resultLabel.Text = "Wait before rolling again."
resultLabel.TextColor3 = defaultTextColor
end
rollButton.Text = "ROLL"
rolling = false
end
rollButton.Activated:Connect(onRollActivated)
What Does This Script Confirm?
- The fixed oddsLabel.Text value shows the Common through Legendary percentages before the first roll.
- The successful response supplies response.Color for the rarity feedback.
- The server-supplied response.Count becomes the ownership count for the rolled outcome.
Test readability across the map
A good palette can still disappear against a crowded scene. Team Test lets you judge the interface inside the real map.
This check stays in Team Test. The MCP agent does not start the session.
- Leave the standard Test option unused.
- Manually select Team Test from Studio's testing dropdown.
- Press Play.
- Move your character to the map's busiest viewpoint.
- Press ROLL.
The result text takes on the color returned for its rarity.
The odds line shows all four percentages. The inventory line shows the ownership count.
- Move your character to the map's darkest viewpoint.
- Allow the 1.5-second cooldown to finish.
- Press ROLL.
The panel should remain readable against the darker background. Its placement should leave important gameplay visible.
- Click End Session.
Any correction remains limited to GUI size plus contrast plus placement. The existing map stays untouched.
- Ask the agent to adjust only GUI size or placement if the panel blocks gameplay.
- Ask the agent to adjust only GUI contrast if a label disappears against the map.
Before you run the final check, what do you expect the ownership count to do when the same outcome returns?
- Manually select Team Test from Studio's testing dropdown.
- Press Play.
- Move your character to the harder of the two viewpoints.
- Press ROLL once.
- Wait for the cooldown after each accepted roll.
- Press ROLL after each wait until one outcome repeats.
The OddsLabel still shows all four percentages. The ResultLabel uses the returned rarity color.
The InventoryLabel increases for the repeated outcome. That proves the panel displays the server's session count.
- Capture a screenshot of the complete panel at this viewpoint.
- Click End Session.
The interface now fits the map while preserving clear server-driven feedback. Your original world remains intact.
Is the Feedback Still Hard to Read?
- Confirm that the successful response still sets resultLabel.TextColor3 = response.Color if every result stays white.
- Confirm that inventoryLabel.Text still uses response.Count if a repeated outcome does not increase.
- Adjust only the Main GUI if the panel covers important gameplay.
Ask for help with diagnosing the roll panel feedback.
Your RNG interface now matches the existing game. Next, you will validate the complete loop through Team Test only.
Validate with Team Test
Your map-matched panel already displays server responses through readable odds. Rarity colors plus ownership feedback make each roll visible.
The RNG state is player-specific. You will prove that the server controls this state through Roblox Studio's collaborative Team Test workflow.
In this step, get ready to:
- Verify that the client sends no reward fields to the server.
- Complete five accepted rolls through Team Test.
- Confirm that the Team Test session closes without runtime errors.
Audit the client-server boundary
A secure RemoteFunction accepts a bare request to roll. Every outcome detail should travel from the server to the client in the response.
- Switch back to the MCP-capable AI client from earlier.
- Ask the MCP agent to read ServerScriptService.AnimeRNG.RollDefinitions.
- Ask the MCP agent to read ServerScriptService.AnimeRNG.RollService.
- Ask the MCP agent to read StarterGui.AnimeRNGGui.RNGClient.
- Ask the agent to verify that RNGClient sends no reward fields through RequestRoll.
- Require the report to cover rarity plus weight plus reward name plus ownership count.
- Require the report to identify the script responsible for weighted selection.
Keep This Audit Read-Only
This audit checks the trust boundary without changing the project state.
- Keep all three scripts unchanged.
- Keep the MCP playtest subagent unused.
- Keep start_stop_play unused.
The report should show that RNGClient invokes RequestRoll without reward data. RollService owns the result.
That trust boundary is confirmed. The client can ask for a roll without choosing what it receives.
✔️ Awesome, I've got everything!
Your audit confirms that weighted selection plus cooldown enforcement plus ownership updates stay on the server.
ⓧ I'd like to double check the full code
- Compare ServerScriptService/AnimeRNG/RollDefinitions with this unchanged reference:
return {
{
Name = "Rookie Hero",
Rarity = "Common",
Weight = 60,
Color = Color3.fromRGB(205, 205, 205),
},
{
Name = "Flame Swordsman",
Rarity = "Rare",
Weight = 25,
Color = Color3.fromRGB(80, 170, 255),
},
{
Name = "Shadow Monarch",
Rarity = "Epic",
Weight = 10,
Color = Color3.fromRGB(185, 90, 255),
},
{
Name = "Celestial Warrior",
Rarity = "Legendary",
Weight = 5,
Color = Color3.fromRGB(255, 195, 65),
},
}
What Should Match?
- The four weights total 100.
- Each outcome includes a server-owned name plus rarity plus weight plus color.
- Compare ServerScriptService/AnimeRNG/RollService with this unchanged reference:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remotes = ReplicatedStorage:WaitForChild("AnimeRNG")
local requestRoll = remotes:WaitForChild("RequestRoll") :: RemoteFunction
local RollDefinitions = require(script.Parent:WaitForChild("RollDefinitions") :: ModuleScript)
local random = Random.new()
local lastRollAt = {}
local inventories = {}
local COOLDOWN_SECONDS = 1.5
local function chooseOutcome()
local totalWeight = 0
for _, outcome in ipairs(RollDefinitions) do
totalWeight += outcome.Weight
end
local cursor = random:NextNumber(0, totalWeight)
for _, outcome in ipairs(RollDefinitions) do
cursor -= outcome.Weight
if cursor <= 0 then
return outcome
end
end
return RollDefinitions[#RollDefinitions]
end
local function onRequestRoll(player)
local userId = player.UserId
local now = time()
local previousRoll = lastRollAt[userId]
if previousRoll and now - previousRoll < COOLDOWN_SECONDS then
return {
Success = false,
Remaining = COOLDOWN_SECONDS - (now - previousRoll),
}
end
lastRollAt[userId] = now
local outcome = chooseOutcome()
local inventory = inventories[userId]
if not inventory then
inventory = {}
inventories[userId] = inventory
end
inventory[outcome.Name] = (inventory[outcome.Name] or 0) + 1
return {
Success = true,
Name = outcome.Name,
Rarity = outcome.Rarity,
Color = outcome.Color,
Count = inventory[outcome.Name],
}
end
requestRoll.OnServerInvoke = onRequestRoll
Players.PlayerRemoving:Connect(function(player)
lastRollAt[player.UserId] = nil
inventories[player.UserId] = nil
end)
What Should the Server Own?
- The chooseOutcome() function selects against cumulative server-side weights.
- The onRequestRoll() function enforces the 1.5-second cooldown.
- The inventories table keeps ownership separate by userId.
- Compare StarterGui/AnimeRNGGui/RNGClient with this unchanged reference:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remotes = ReplicatedStorage:WaitForChild("AnimeRNG")
local requestRoll = remotes:WaitForChild("RequestRoll") :: RemoteFunction
local gui = script.Parent
local main = gui:WaitForChild("Main")
local rollButton = main:WaitForChild("RollButton") :: TextButton
local resultLabel = main:WaitForChild("ResultLabel") :: TextLabel
local oddsLabel = main:WaitForChild("OddsLabel") :: TextLabel
local inventoryLabel = main:WaitForChild("InventoryLabel") :: TextLabel
local rolling = false
local defaultTextColor = Color3.fromRGB(255, 255, 255)
oddsLabel.Text = "Common 60% | Rare 25% | Epic 10% | Legendary 5%"
resultLabel.Text = "Press ROLL"
inventoryLabel.Text = "No session rolls yet"
local function onRollActivated()
if rolling then
return
end
rolling = true
rollButton.Text = "ROLLING..."
local success, response = pcall(requestRoll.InvokeServer, requestRoll)
if not success then
resultLabel.Text = "Server request failed"
resultLabel.TextColor3 = defaultTextColor
elseif response.Success then
resultLabel.Text = response.Name .. " [" .. response.Rarity .. "]"
resultLabel.TextColor3 = response.Color
inventoryLabel.Text = "Owned " .. response.Name .. ": " .. tostring(response.Count)
else
resultLabel.Text = "Wait before rolling again."
resultLabel.TextColor3 = defaultTextColor
end
rollButton.Text = "ROLL"
rolling = false
end
rollButton.Activated:Connect(onRollActivated)
What Should the Client Do?
- The client invokes RequestRoll inside pcall() without supplying reward data.
- The client displays only the name plus rarity plus color plus count returned by the server.
- The client never mutates the session inventory.
Complete five manual rolls
The final gameplay proof must use the same collaborative path that carries player-specific server state. A solo test would skip the workflow this step needs to validate.
Use Team Test Only
This verification uses Team Test from start to finish.
- Keep standard Test unused.
- Keep F5 unused.
Before you start, do you expect rapid clicks to create extra ownership?
- Select Team Test from Roblox Studio's testing dropdown.
- Click Play.
- Press ROLL once after the map loads.
You should see exactly one anime-inspired archetype plus its rarity. The result color should match the returned rarity.
- Press ROLL again immediately.
You should see Wait before rolling again. The ownership count should remain unchanged.
- Wait at least 1.5 seconds before the next accepted roll.
- Continue pressing ROLL until you have completed five accepted rolls.
You now have five accepted outcomes. Each cooldown click granted nothing.
Add the Optional Isolation Check
One owner can complete the required check. An available collaborator can also prove that ownership stays separate between players.
- Ask the collaborator to join the Team Test.
- Have them click Play in their Studio session.
- Ask them to press ROLL once.
- Press ROLL in your own session.
- Compare InventoryLabel on both clients.
Each client should update for its own player's outcome. This confirms that the userId inventories remain separate.
Inspect output and end the session
The Team Test Output windows expose runtime problems from the collaborative session. A clean shutdown completes the proof.
Before you inspect the logs, do you expect any runtime errors from the five-roll session?
- Switch to the Team Test Output windows.
- Inspect each Output window for runtime errors.
- Capture a screenshot showing a successful roll plus an Output window without runtime errors.
- Click End Session from a test session.
The Team Test session should close cleanly. Every accepted click returned exactly one outcome.
That's the core loop validated. Your existing map stayed unchanged while the server controlled every reward.
Does Team Test Show a Problem?
Confirm that Team Test remained selected before you clicked Play.
If ROLL does not respond, inspect the newest runtime error for a path involving RollService or RNGClient.
If no collaborator is available, complete the required five-roll check with the owner session.
Help me diagnose my Team Test result.
Secret mission
Secret Mission: Broadcast a Global Roll Feed
Turn every successful roll into a shared moment for the whole server. Add a server-controlled feed that shows the same player, outcome, and rarity to every connected Team Test player.
Clean Up Your Resources
Clean Up Your Resources
Choose whether to keep the Anime RNG branches, leave them paused with Team Test closed, or delete them from your existing place. Free rolls in Roblox Studio create no ongoing costs.
Resources you used:
- The ReplicatedStorage.AnimeRNG branch.
- The ServerScriptService.AnimeRNG branch.
- The StarterGui.AnimeRNGGui branch.
Your existing Workspace map remains separate from these isolated branches.
Keep everything running
No cleanup action is needed. Choose this if you plan to continue building the Anime RNG feature.
- Keep ReplicatedStorage.AnimeRNG in the existing place.
- Keep ServerScriptService.AnimeRNG in the existing place.
- Keep StarterGui.AnimeRNGGui in the existing place.
- Save the existing place in Roblox Studio.
Pause - I'll come back to this later
Your Team Test session is already ended. Choose this when you want the feature ready for a later session.
- Confirm the Team Test session remains ended.
- Save the existing place in Roblox Studio.
- Close Roblox Studio.
The scripts remain inside the place. The next Team Test session can use the same Anime RNG loop.
Delete - I don't want to use this again
Deleting these branches can feel serious. Each removal path is isolated from the existing Workspace map.
- Delete ReplicatedStorage.AnimeRNG from the Explorer panel.
- Delete ServerScriptService.AnimeRNG from the Explorer panel.
- Delete StarterGui.AnimeRNGGui from the Explorer panel.
- Save the existing place in Roblox Studio.
- Expand Workspace in the Explorer panel to confirm its existing children remain.
You should no longer see the three Anime RNG branches in Explorer. Your existing Workspace children should still be present.
Nice Work!
Nice Work!
You made it! Your existing map now runs a server-authoritative Anime RNG loop in Roblox Studio.
You've learned how to:
- Protect your existing map while adding an isolated roll panel that matches its visual style.
- Choose weighted outcomes on the server. Enforce a 1.5-second cooldown. Track session ownership separately for each player.
- Validate the complete loop through Team Test only. Confirm every accepted roll returns one outcome. Confirm cooldown clicks grant nothing. End the session without runtime errors.
- Secret Mission: Broadcast every successful roll to all connected testers through a global roll feed.
Ready to quiz yourself?