roblox-opencode 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -122
- package/commands/setup-game.md +108 -108
- package/commands/sync-check.md +53 -53
- package/core/roblox-core.md +93 -93
- package/dist/server.js +189 -167
- package/package.json +35 -35
- package/skills/roblox-analytics/SKILL.md +277 -277
- package/skills/roblox-analytics/references/event-batcher.luau +75 -75
- package/skills/roblox-animation-vfx/SKILL.md +1325 -1325
- package/skills/roblox-architecture/SKILL.md +863 -863
- package/skills/roblox-architecture/references/combat-systems.md +1381 -1381
- package/skills/roblox-code-review/SKILL.md +686 -686
- package/skills/roblox-data/SKILL.md +889 -889
- package/skills/roblox-data/references/inventory-systems.md +1729 -1729
- package/skills/roblox-debug/SKILL.md +98 -98
- package/skills/roblox-gui/SKILL.md +1103 -1103
- package/skills/roblox-gui-fusion/SKILL.md +150 -150
- package/skills/roblox-gui-fusion/references/inventory.luau +427 -427
- package/skills/roblox-gui-fusion/references/settings-menu.luau +579 -579
- package/skills/roblox-gui-fusion/references/shop.luau +411 -411
- package/skills/roblox-luau-mastery/SKILL.md +1519 -1519
- package/skills/roblox-monetization/SKILL.md +1084 -1084
- package/skills/roblox-monetization/references/process-receipt.luau +131 -131
- package/skills/roblox-networking/SKILL.md +669 -669
- package/skills/roblox-networking/references/remote-validator.luau +193 -193
- package/skills/roblox-publish-checklist/SKILL.md +127 -127
- package/skills/roblox-runtime/SKILL.md +753 -753
- package/skills/roblox-sharp-edges/SKILL.md +294 -294
- package/skills/roblox-sync/SKILL.md +126 -126
- package/skills/roblox-testing/SKILL.md +943 -943
- package/skills/roblox-tooling/SKILL.md +149 -149
- package/vendor/LICENSES/ProfileStore-LICENSE +201 -201
- package/vendor/LICENSES/RbxUtil-LICENSE +7 -7
- package/vendor/LICENSES/promise-LICENSE +20 -20
- package/vendor/LICENSES/t-LICENSE +21 -21
- package/vendor/LICENSES/testez-LICENSE +200 -200
- package/vendor/README.md +83 -83
- package/vendor/fusion/Animation/ExternalTime.luau +83 -83
- package/vendor/fusion/Animation/Spring.luau +321 -321
- package/vendor/fusion/Animation/Stopwatch.luau +127 -127
- package/vendor/fusion/Animation/Tween.luau +187 -187
- package/vendor/fusion/Animation/getTweenDuration.luau +27 -27
- package/vendor/fusion/Animation/getTweenRatio.luau +47 -47
- package/vendor/fusion/Animation/lerpType.luau +163 -163
- package/vendor/fusion/Animation/packType.luau +99 -99
- package/vendor/fusion/Animation/springCoefficients.luau +80 -80
- package/vendor/fusion/Animation/unpackType.luau +102 -102
- package/vendor/fusion/Colour/Oklab.luau +70 -70
- package/vendor/fusion/Colour/sRGB.luau +54 -54
- package/vendor/fusion/External.luau +167 -167
- package/vendor/fusion/ExternalDebug.luau +69 -69
- package/vendor/fusion/Graph/Observer.luau +113 -113
- package/vendor/fusion/Graph/castToGraph.luau +28 -28
- package/vendor/fusion/Graph/change.luau +80 -80
- package/vendor/fusion/Graph/depend.luau +32 -32
- package/vendor/fusion/Graph/evaluate.luau +55 -55
- package/vendor/fusion/Instances/Attribute.luau +57 -57
- package/vendor/fusion/Instances/AttributeChange.luau +46 -46
- package/vendor/fusion/Instances/AttributeOut.luau +63 -63
- package/vendor/fusion/Instances/Child.luau +21 -21
- package/vendor/fusion/Instances/Children.luau +147 -147
- package/vendor/fusion/Instances/Hydrate.luau +32 -32
- package/vendor/fusion/Instances/New.luau +52 -52
- package/vendor/fusion/Instances/OnChange.luau +49 -49
- package/vendor/fusion/Instances/OnEvent.luau +53 -53
- package/vendor/fusion/Instances/Out.luau +69 -69
- package/vendor/fusion/Instances/applyInstanceProps.luau +148 -148
- package/vendor/fusion/Instances/defaultProps.luau +194 -194
- package/vendor/fusion/LICENSE +21 -21
- package/vendor/fusion/Logging/formatError.luau +48 -48
- package/vendor/fusion/Logging/messages.luau +51 -51
- package/vendor/fusion/Logging/parseError.luau +24 -24
- package/vendor/fusion/Memory/checkLifetime.luau +133 -133
- package/vendor/fusion/Memory/deriveScope.luau +23 -23
- package/vendor/fusion/Memory/deriveScopeImpl.luau +44 -44
- package/vendor/fusion/Memory/doCleanup.luau +78 -78
- package/vendor/fusion/Memory/innerScope.luau +33 -33
- package/vendor/fusion/Memory/legacyCleanup.luau +17 -17
- package/vendor/fusion/Memory/needsDestruction.luau +16 -16
- package/vendor/fusion/Memory/poisonScope.luau +33 -33
- package/vendor/fusion/Memory/scopePool.luau +54 -54
- package/vendor/fusion/Memory/scoped.luau +26 -26
- package/vendor/fusion/Memory/whichLivesLonger.luau +74 -74
- package/vendor/fusion/RobloxExternal.luau +97 -97
- package/vendor/fusion/State/Computed.luau +138 -138
- package/vendor/fusion/State/For/Disassembly.luau +210 -210
- package/vendor/fusion/State/For/ForTypes.luau +30 -30
- package/vendor/fusion/State/For/init.luau +109 -109
- package/vendor/fusion/State/ForKeys.luau +93 -93
- package/vendor/fusion/State/ForPairs.luau +96 -96
- package/vendor/fusion/State/ForValues.luau +93 -93
- package/vendor/fusion/State/Value.luau +87 -87
- package/vendor/fusion/State/castToState.luau +25 -25
- package/vendor/fusion/State/peek.luau +30 -30
- package/vendor/fusion/Types.luau +314 -314
- package/vendor/fusion/Utility/Contextual.luau +90 -90
- package/vendor/fusion/Utility/Safe.luau +22 -22
- package/vendor/fusion/Utility/isSimilar.luau +29 -29
- package/vendor/fusion/Utility/merge.luau +35 -35
- package/vendor/fusion/Utility/nameOf.luau +34 -34
- package/vendor/fusion/Utility/never.luau +13 -13
- package/vendor/fusion/Utility/nicknames.luau +10 -10
- package/vendor/fusion/Utility/xtypeof.luau +26 -26
- package/vendor/fusion/init.luau +82 -82
- package/vendor/profilestore/init.luau +2242 -2242
- package/vendor/promise/init.luau +1982 -1982
- package/vendor/rbxutil/buffer-util/Buffer.test.luau +25 -25
- package/vendor/rbxutil/buffer-util/BufferReader.luau +228 -228
- package/vendor/rbxutil/buffer-util/BufferWriter.luau +269 -269
- package/vendor/rbxutil/buffer-util/DataTypeBuffer.luau +223 -223
- package/vendor/rbxutil/buffer-util/Types.luau +60 -60
- package/vendor/rbxutil/buffer-util/index.d.ts +153 -153
- package/vendor/rbxutil/buffer-util/init.luau +41 -41
- package/vendor/rbxutil/buffer-util/package.json +16 -16
- package/vendor/rbxutil/buffer-util/wally.toml +9 -9
- package/vendor/rbxutil/comm/Client/ClientComm.luau +232 -232
- package/vendor/rbxutil/comm/Client/ClientRemoteProperty.luau +156 -156
- package/vendor/rbxutil/comm/Client/ClientRemoteSignal.luau +109 -109
- package/vendor/rbxutil/comm/Client/init.luau +135 -135
- package/vendor/rbxutil/comm/Server/RemoteProperty.luau +295 -295
- package/vendor/rbxutil/comm/Server/RemoteSignal.luau +211 -211
- package/vendor/rbxutil/comm/Server/ServerComm.luau +211 -211
- package/vendor/rbxutil/comm/Server/init.luau +140 -140
- package/vendor/rbxutil/comm/Types.luau +18 -18
- package/vendor/rbxutil/comm/Util.luau +27 -27
- package/vendor/rbxutil/comm/init.luau +35 -35
- package/vendor/rbxutil/comm/wally.toml +13 -13
- package/vendor/rbxutil/component/init.luau +759 -759
- package/vendor/rbxutil/component/init.test.luau +311 -311
- package/vendor/rbxutil/component/wally.toml +14 -14
- package/vendor/rbxutil/concur/init.luau +542 -542
- package/vendor/rbxutil/concur/init.test.luau +364 -364
- package/vendor/rbxutil/concur/wally.toml +8 -8
- package/vendor/rbxutil/enum-list/init.luau +101 -101
- package/vendor/rbxutil/enum-list/init.test.luau +91 -91
- package/vendor/rbxutil/enum-list/wally.toml +8 -8
- package/vendor/rbxutil/find/index.d.ts +20 -20
- package/vendor/rbxutil/find/init.luau +44 -44
- package/vendor/rbxutil/find/package.json +17 -17
- package/vendor/rbxutil/find/wally.toml +8 -8
- package/vendor/rbxutil/input/Gamepad.luau +559 -559
- package/vendor/rbxutil/input/Keyboard.luau +124 -124
- package/vendor/rbxutil/input/Mouse.luau +278 -278
- package/vendor/rbxutil/input/PreferredInput.luau +91 -91
- package/vendor/rbxutil/input/Touch.luau +120 -120
- package/vendor/rbxutil/input/init.luau +33 -33
- package/vendor/rbxutil/input/wally.toml +12 -12
- package/vendor/rbxutil/loader/index.d.ts +15 -15
- package/vendor/rbxutil/loader/init.luau +137 -137
- package/vendor/rbxutil/loader/wally.toml +8 -8
- package/vendor/rbxutil/log/index.d.ts +38 -38
- package/vendor/rbxutil/log/init.luau +746 -746
- package/vendor/rbxutil/log/wally.toml +8 -8
- package/vendor/rbxutil/net/init.luau +190 -190
- package/vendor/rbxutil/net/wally.toml +8 -8
- package/vendor/rbxutil/option/index.d.ts +44 -44
- package/vendor/rbxutil/option/init.luau +489 -489
- package/vendor/rbxutil/option/init.test.luau +342 -342
- package/vendor/rbxutil/option/wally.toml +8 -8
- package/vendor/rbxutil/pid/index.d.ts +53 -53
- package/vendor/rbxutil/pid/init.luau +195 -195
- package/vendor/rbxutil/pid/package.json +16 -16
- package/vendor/rbxutil/pid/wally.toml +9 -9
- package/vendor/rbxutil/quaternion/index.d.ts +117 -117
- package/vendor/rbxutil/quaternion/init.luau +570 -570
- package/vendor/rbxutil/quaternion/package.json +16 -16
- package/vendor/rbxutil/quaternion/wally.toml +9 -9
- package/vendor/rbxutil/query/index.d.ts +43 -43
- package/vendor/rbxutil/query/init.luau +117 -117
- package/vendor/rbxutil/query/package.json +18 -18
- package/vendor/rbxutil/query/wally.toml +9 -9
- package/vendor/rbxutil/sequent/index.d.ts +28 -28
- package/vendor/rbxutil/sequent/init.luau +340 -340
- package/vendor/rbxutil/sequent/package.json +16 -16
- package/vendor/rbxutil/sequent/wally.toml +9 -9
- package/vendor/rbxutil/ser/init.luau +175 -175
- package/vendor/rbxutil/ser/init.test.luau +50 -50
- package/vendor/rbxutil/ser/wally.toml +11 -11
- package/vendor/rbxutil/shake/index.d.ts +36 -36
- package/vendor/rbxutil/shake/init.luau +532 -532
- package/vendor/rbxutil/shake/init.test.luau +267 -267
- package/vendor/rbxutil/shake/package.json +16 -16
- package/vendor/rbxutil/shake/wally.toml +9 -9
- package/vendor/rbxutil/signal/index.d.ts +100 -100
- package/vendor/rbxutil/signal/init.luau +432 -432
- package/vendor/rbxutil/signal/init.test.luau +190 -190
- package/vendor/rbxutil/signal/package.json +17 -17
- package/vendor/rbxutil/signal/wally.toml +9 -9
- package/vendor/rbxutil/silo/TableWatcher.luau +65 -65
- package/vendor/rbxutil/silo/Util.luau +55 -55
- package/vendor/rbxutil/silo/init.luau +338 -338
- package/vendor/rbxutil/silo/init.test.luau +215 -215
- package/vendor/rbxutil/silo/wally.toml +8 -8
- package/vendor/rbxutil/spring/index.d.ts +40 -40
- package/vendor/rbxutil/spring/init.luau +97 -97
- package/vendor/rbxutil/spring/package.json +17 -17
- package/vendor/rbxutil/spring/wally.toml +8 -8
- package/vendor/rbxutil/stream/index.d.ts +88 -88
- package/vendor/rbxutil/stream/init.luau +597 -597
- package/vendor/rbxutil/stream/package.json +18 -18
- package/vendor/rbxutil/stream/wally.toml +9 -9
- package/vendor/rbxutil/streamable/Streamable.luau +202 -202
- package/vendor/rbxutil/streamable/StreamableUtil.luau +80 -80
- package/vendor/rbxutil/streamable/init.luau +8 -8
- package/vendor/rbxutil/streamable/wally.toml +12 -12
- package/vendor/rbxutil/symbol/init.luau +56 -56
- package/vendor/rbxutil/symbol/init.test.luau +37 -37
- package/vendor/rbxutil/symbol/wally.toml +8 -8
- package/vendor/rbxutil/table-util/init.luau +938 -938
- package/vendor/rbxutil/table-util/init.test.luau +439 -439
- package/vendor/rbxutil/task-queue/index.d.ts +27 -27
- package/vendor/rbxutil/task-queue/init.luau +97 -97
- package/vendor/rbxutil/task-queue/wally.toml +8 -8
- package/vendor/rbxutil/timer/index.d.ts +81 -81
- package/vendor/rbxutil/timer/init.luau +249 -249
- package/vendor/rbxutil/timer/init.test.luau +73 -73
- package/vendor/rbxutil/timer/wally.toml +11 -11
- package/vendor/rbxutil/tree/index.d.ts +15 -15
- package/vendor/rbxutil/tree/init.luau +137 -137
- package/vendor/rbxutil/tree/wally.toml +8 -8
- package/vendor/rbxutil/trove/index.d.ts +46 -46
- package/vendor/rbxutil/trove/init.luau +787 -787
- package/vendor/rbxutil/trove/init.test.luau +203 -203
- package/vendor/rbxutil/trove/wally.toml +8 -8
- package/vendor/rbxutil/typed-remote/init.luau +196 -196
- package/vendor/rbxutil/typed-remote/wally.toml +8 -8
- package/vendor/rbxutil/wait-for/index.d.ts +17 -17
- package/vendor/rbxutil/wait-for/init.luau +257 -257
- package/vendor/rbxutil/wait-for/init.test.luau +182 -182
- package/vendor/rbxutil/wait-for/wally.toml +11 -11
- package/vendor/t/t.lua +1350 -1350
- package/vendor/testez/Context.lua +26 -26
- package/vendor/testez/Expectation.lua +311 -311
- package/vendor/testez/ExpectationContext.lua +38 -38
- package/vendor/testez/LifecycleHooks.lua +89 -89
- package/vendor/testez/Reporters/TeamCityReporter.lua +101 -101
- package/vendor/testez/Reporters/TextReporter.lua +105 -105
- package/vendor/testez/Reporters/TextReporterQuiet.lua +96 -96
- package/vendor/testez/TestBootstrap.lua +146 -146
- package/vendor/testez/TestEnum.lua +27 -27
- package/vendor/testez/TestPlan.lua +304 -304
- package/vendor/testez/TestPlanner.lua +39 -39
- package/vendor/testez/TestResults.lua +111 -111
- package/vendor/testez/TestRunner.lua +188 -188
- package/vendor/testez/TestSession.lua +243 -243
- package/vendor/testez/init.lua +39 -39
|
@@ -1,687 +1,687 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: roblox-code-review
|
|
3
|
-
description: "Code review with security, performance, and monetization lenses for Roblox projects"
|
|
4
|
-
tags: [roblox, code-review, security, performance, monetization, networking, data-persistence]
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# /code-review - Code Quality Review
|
|
8
|
-
|
|
9
|
-
You are performing a code quality review on a Roblox project. Follow these 8 steps. Apply the relevant lens based on what changed. Don't apply all lenses every time.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Quick Reference
|
|
14
|
-
|
|
15
|
-
**Load lenses below only when code matching that domain changed. Don't apply all lenses every time.**
|
|
16
|
-
|
|
17
|
-
Key rules:
|
|
18
|
-
- **Remote types:** RemoteEvent (fire-and-forget), UnreliableRemoteEvent (loss-tolerant VFX/position), RemoteFunction (request-response, use sparingly), BindableEvent/BindableFunction (intra-client/server internal, never cross-boundary).
|
|
19
|
-
- **Data persistence:** ALWAYS ProfileStore for player state. Never raw DataStore. Schema template with defaults, DataVersion, migration functions, session locking, BindToClose.
|
|
20
|
-
- **Security:** Validate every remote parameter server-side. Rate-limit all remotes per-player.
|
|
21
|
-
- **Performance:** Consolidate Heartbeat loops. Cache services. Disconnect unused events.
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Step 1: Project Scan
|
|
26
|
-
|
|
27
|
-
Read the project directory structure. Survey script count, naming patterns, and organization.
|
|
28
|
-
|
|
29
|
-
Record:
|
|
30
|
-
- Total script count
|
|
31
|
-
- Folder organization
|
|
32
|
-
- Module naming patterns
|
|
33
|
-
- Whether the project uses Rojo/Wally or in-Studio editing
|
|
34
|
-
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
## Step 2: Organization Review
|
|
38
|
-
|
|
39
|
-
Check:
|
|
40
|
-
- Scripts in correct locations (ServerScriptService, StarterPlayerScripts, etc.)
|
|
41
|
-
- Proper use of services vs standalone scripts
|
|
42
|
-
- Clean folder structure (no orphaned scripts, no nesting > 3 levels deep)
|
|
43
|
-
- Module naming conventions consistent (PascalCase for ModuleScripts, camelCase for functions)
|
|
44
|
-
- No scripts with duplicate or overlapping responsibilities
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## Step 3: Code Quality Scan
|
|
49
|
-
|
|
50
|
-
Search for anti-patterns:
|
|
51
|
-
|
|
52
|
-
```
|
|
53
|
-
Deprecated APIs:
|
|
54
|
-
- wait( → replace with task.wait()
|
|
55
|
-
- spawn( → replace with task.spawn()
|
|
56
|
-
- delay( → replace with task.delay()
|
|
57
|
-
|
|
58
|
-
Code smells:
|
|
59
|
-
- Global variables (should be module-scoped)
|
|
60
|
-
- Missing type annotations on public functions
|
|
61
|
-
- Instance.new() in client scripts (should be server-created)
|
|
62
|
-
- while true without task.wait() (unbounded loops)
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Check for:
|
|
66
|
-
- Deprecated APIs (`wait()`, `spawn()`, `delay()`)
|
|
67
|
-
- Global variable usage (should be module-scoped)
|
|
68
|
-
- Missing type annotations on public functions
|
|
69
|
-
- Inconsistent naming conventions
|
|
70
|
-
- Dead code / unreachable code
|
|
71
|
-
- Duplicate code across scripts
|
|
72
|
-
- Overly long functions (>100 lines, should be refactored)
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
## Step 4: Architecture Review
|
|
77
|
-
|
|
78
|
-
Check:
|
|
79
|
-
- Module boundaries - Does each module have a single responsibility?
|
|
80
|
-
- Dependency direction - Do modules depend on abstractions, not concrete implementations?
|
|
81
|
-
- Circular requires - Any modules that depend on each other?
|
|
82
|
-
- Separation of concerns - Server vs Client logic properly separated
|
|
83
|
-
- Framework usage - If using a framework, is it used consistently?
|
|
84
|
-
- Configuration - Hardcoded values should be in config modules
|
|
85
|
-
|
|
86
|
-
---
|
|
87
|
-
|
|
88
|
-
## Step 5: Security Quick-Check
|
|
89
|
-
|
|
90
|
-
Quick scan for:
|
|
91
|
-
- Unvalidated RemoteEvent handlers (server-side)
|
|
92
|
-
- Client-trusted logic (currency, inventory, damage, position)
|
|
93
|
-
- Sensitive data in ReplicatedStorage or StarterPlayer
|
|
94
|
-
- Missing rate limiting on remotes
|
|
95
|
-
- ProcessReceipt implementation correctness
|
|
96
|
-
|
|
97
|
-
> **For a deep security review, run the Security Lens below.**
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## Step 6: Performance Quick-Check
|
|
102
|
-
|
|
103
|
-
Quick scan for:
|
|
104
|
-
- `wait()` or `spawn()` in tight loops
|
|
105
|
-
- Multiple `RunService.Heartbeat:Connect()` in same script
|
|
106
|
-
- Large tables without cleanup
|
|
107
|
-
- Undisconnected events (memory leaks)
|
|
108
|
-
- Unanchored parts without collision groups
|
|
109
|
-
- Excessive RemoteEvent usage
|
|
110
|
-
|
|
111
|
-
> **For a deep performance review, run the Performance Lens below.**
|
|
112
|
-
|
|
113
|
-
---
|
|
114
|
-
|
|
115
|
-
## Step 7: Quality Report
|
|
116
|
-
|
|
117
|
-
Rate overall quality:
|
|
118
|
-
|
|
119
|
-
- **A** - Production-ready. Clean, organized, secure, performant
|
|
120
|
-
- **B** - Solid with minor issues. Safe to ship with minor cleanup
|
|
121
|
-
- **C** - Functional but needs work. Ship with caveats
|
|
122
|
-
- **D** - Significant issues. Needs refactoring before ship
|
|
123
|
-
- **F** - Critical problems. Do not ship in current state
|
|
124
|
-
|
|
125
|
-
List findings by severity:
|
|
126
|
-
- **Critical** - Security vulnerabilities, data loss risk, crashes
|
|
127
|
-
- **High** - Memory leaks, performance bottlenecks, broken features
|
|
128
|
-
- **Medium** - Code smells, deprecated APIs, poor organization
|
|
129
|
-
- **Low** - Style inconsistencies, missing documentation
|
|
130
|
-
|
|
131
|
-
For each finding, provide:
|
|
132
|
-
1. File and line (or function name)
|
|
133
|
-
2. What's wrong
|
|
134
|
-
3. The specific fix (code)
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
## Step 8: Refactoring Suggestions
|
|
139
|
-
|
|
140
|
-
If significant issues found, suggest refactoring priorities:
|
|
141
|
-
|
|
142
|
-
1. **Immediate** - Must fix before next publish
|
|
143
|
-
2. **Short-term** - Fix in the next development cycle
|
|
144
|
-
3. **Long-term** - Plan for when the project grows
|
|
145
|
-
|
|
146
|
-
For each suggestion:
|
|
147
|
-
- What to change
|
|
148
|
-
- Why it matters
|
|
149
|
-
- Estimated effort (small/medium/large)
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
# Security Lens
|
|
154
|
-
|
|
155
|
-
*Apply this lens when security-relevant code changed: remotes, data persistence, monetization, player input handling, or server authority.*
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
## Security Step 1: Remote Surface Scan
|
|
160
|
-
|
|
161
|
-
Search for all `RemoteEvent` and `RemoteFunction` instances. Search for `:FireServer`, `:FireClient`, `:InvokeServer`, `:InvokeClient` to map the full remote surface.
|
|
162
|
-
|
|
163
|
-
Record every remote found with:
|
|
164
|
-
- Name
|
|
165
|
-
- Location (ReplicatedStorage path)
|
|
166
|
-
- Direction (Client→Server, Server→Client, Client↔Server)
|
|
167
|
-
- What it appears to do
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
|
-
## Security Step 2: Validation Check
|
|
172
|
-
|
|
173
|
-
For each remote, verify:
|
|
174
|
-
- **Argument type checking** - Server validates `typeof(arg)` for every parameter
|
|
175
|
-
- **Range validation** - Numeric inputs checked against min/max bounds
|
|
176
|
-
- **Cooldown enforcement** - Rate limiting prevents spam/exploitation
|
|
177
|
-
- **Authorization check** - Server verifies the requesting player owns the action
|
|
178
|
-
- **Sanitization** - String inputs cleaned, no injection vectors
|
|
179
|
-
|
|
180
|
-
Flag any remote that accepts input without full server-side validation.
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
## Security Step 3: Client Trust Audit
|
|
185
|
-
|
|
186
|
-
Search for client-side logic that should be server-side:
|
|
187
|
-
- Currency operations (giving/removing coins, cash, gems)
|
|
188
|
-
- Inventory changes (adding/removing items)
|
|
189
|
-
- Damage calculation (should be server-authoritative)
|
|
190
|
-
- Position setting (teleporting, movement authority)
|
|
191
|
-
- Leaderboard/stat modification
|
|
192
|
-
- Game state changes (win/lose conditions)
|
|
193
|
-
|
|
194
|
-
**Rule:** If it affects game state or other players, it must be server-validated.
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
## Security Step 4: Data Exposure Check
|
|
199
|
-
|
|
200
|
-
Verify:
|
|
201
|
-
- No sensitive data in `ReplicatedStorage` (secrets, configs with admin keys)
|
|
202
|
-
- No server-only logic in `StarterPlayerScripts` (game state, anti-cheat)
|
|
203
|
-
- No player data exposed to other players via remotes (unless intentional)
|
|
204
|
-
- RemoteEvent payloads don't include excess data (send only what's needed)
|
|
205
|
-
- No `require()` paths to server-only modules from client scripts
|
|
206
|
-
|
|
207
|
-
---
|
|
208
|
-
|
|
209
|
-
## Security Step 5: Rate Limiting Check
|
|
210
|
-
|
|
211
|
-
Verify all remotes have per-player rate limiting:
|
|
212
|
-
- Track last-fire timestamps server-side
|
|
213
|
-
- Reject requests faster than expected human input
|
|
214
|
-
- Different limits for different actions (chat: 1/sec, purchase: 1/5sec, movement: 30/sec)
|
|
215
|
-
- Log rate limit violations for monitoring
|
|
216
|
-
|
|
217
|
-
---
|
|
218
|
-
|
|
219
|
-
## Security Step 6: Vulnerability Report
|
|
220
|
-
|
|
221
|
-
Categorize findings:
|
|
222
|
-
|
|
223
|
-
- **Critical** - Exploitable for direct advantage (free currency, item duplication, account takeover)
|
|
224
|
-
- **High** - Data exposure or corruption possible
|
|
225
|
-
- **Medium** - Potential for abuse with moderate effort
|
|
226
|
-
- **Low** - Best practice violation, hard to exploit
|
|
227
|
-
|
|
228
|
-
For each vulnerability:
|
|
229
|
-
1. Remote/method affected
|
|
230
|
-
2. Exploit scenario (how an attacker would abuse it)
|
|
231
|
-
3. Impact (what they gain)
|
|
232
|
-
4. Fix (specific hardened code)
|
|
233
|
-
|
|
234
|
-
---
|
|
235
|
-
|
|
236
|
-
## Security Step 7: Hardening
|
|
237
|
-
|
|
238
|
-
Apply hardened code for each vulnerable remote. Include before/after for clarity.
|
|
239
|
-
|
|
240
|
-
Hardening patterns to apply:
|
|
241
|
-
- Server-side validation wrapper for each remote
|
|
242
|
-
- Rate limiting middleware
|
|
243
|
-
- Data sanitization functions
|
|
244
|
-
- Ownership verification for state changes
|
|
245
|
-
|
|
246
|
-
---
|
|
247
|
-
|
|
248
|
-
## Security Step 8: Re-verify
|
|
249
|
-
|
|
250
|
-
Confirm all vulnerabilities addressed:
|
|
251
|
-
- Each Critical/High finding has a corresponding fix applied
|
|
252
|
-
- Legitimate functionality still works after hardening
|
|
253
|
-
- No new issues introduced by the fixes
|
|
254
|
-
- Output a before/after comparison of the security posture
|
|
255
|
-
|
|
256
|
-
---
|
|
257
|
-
|
|
258
|
-
# Performance Lens
|
|
259
|
-
|
|
260
|
-
*Apply this lens when performance-sensitive code changed: large loops, data structures, rendering, network-heavy features, or when the user reports lag.*
|
|
261
|
-
|
|
262
|
-
---
|
|
263
|
-
|
|
264
|
-
## Performance Step 1: Project Scan
|
|
265
|
-
|
|
266
|
-
Search for known anti-patterns:
|
|
267
|
-
- `wait()` and `spawn()` (yield-based, blocks thread)
|
|
268
|
-
- `RunService` loops (Heartbeat, Stepped, RenderStepped frequency)
|
|
269
|
-
- `GetDescendants()` and `GetChildren()` in loops (expensive at scale)
|
|
270
|
-
|
|
271
|
-
---
|
|
272
|
-
|
|
273
|
-
## Performance Step 2: Part Audit
|
|
274
|
-
|
|
275
|
-
Check for:
|
|
276
|
-
- Total part count (target: <50,000 for mobile, <200,000 for PC)
|
|
277
|
-
- Unanchored parts without assembly (physics chaos)
|
|
278
|
-
- Parts without collision groups (unnecessary collision detection)
|
|
279
|
-
- MeshParts vs Unions vs parts (MeshParts are most efficient)
|
|
280
|
-
- Transparent/reflective parts (rendering cost)
|
|
281
|
-
|
|
282
|
-
---
|
|
283
|
-
|
|
284
|
-
## Performance Step 3: Script Audit
|
|
285
|
-
|
|
286
|
-
Check for:
|
|
287
|
-
- Multiple `Heartbeat:Connect()` in the same script (consolidate into one)
|
|
288
|
-
- Excessive RemoteEvent usage (batch updates, reduce frequency)
|
|
289
|
-
- Tight loops without `task.wait()` (thread starvation)
|
|
290
|
-
- Unindexed table operations (linear search vs hash lookup)
|
|
291
|
-
- String concatenation in loops (use table.concat instead)
|
|
292
|
-
- `Instance.new()` in hot paths (cache and reuse)
|
|
293
|
-
- Deep `WaitForChild()` chains (cache references)
|
|
294
|
-
|
|
295
|
-
---
|
|
296
|
-
|
|
297
|
-
## Performance Step 4: Memory Audit
|
|
298
|
-
|
|
299
|
-
Check for:
|
|
300
|
-
- Undisconnected events (every `:Connect()` must have a matching `:Disconnect()`)
|
|
301
|
-
- Unreferenced instances (created but never parented or referenced)
|
|
302
|
-
- Large data tables held in memory (use lazy loading)
|
|
303
|
-
- String/internment issues (unnecessary duplicate strings)
|
|
304
|
-
- Module-level state that grows without cleanup
|
|
305
|
-
|
|
306
|
-
---
|
|
307
|
-
|
|
308
|
-
## Performance Step 5: Network Audit
|
|
309
|
-
|
|
310
|
-
Check:
|
|
311
|
-
- RemoteEvent frequency - Are updates sent every frame when 1/sec would suffice?
|
|
312
|
-
- Data size per event - Are payloads unnecessarily large?
|
|
313
|
-
- Unnecessary replication - Is data sent to all players when only some need it?
|
|
314
|
-
- `FireAllClients` vs `FireClient` - Target specific players when possible
|
|
315
|
-
- Debouncing - Are rapid-fire remotes properly debounced server-side?
|
|
316
|
-
|
|
317
|
-
---
|
|
318
|
-
|
|
319
|
-
## Performance Step 6: Priority Report
|
|
320
|
-
|
|
321
|
-
Generate prioritized list:
|
|
322
|
-
|
|
323
|
-
- **Critical** - Causes crashes or completely unplayable experience
|
|
324
|
-
- **High** - Noticeable lag, frame drops, or rubber-banding
|
|
325
|
-
- **Medium** - Suboptimal but functional, wasted resources
|
|
326
|
-
- **Low** - Minor optimization opportunity
|
|
327
|
-
|
|
328
|
-
For each item:
|
|
329
|
-
1. What's slow
|
|
330
|
-
2. Why it's slow (technical explanation)
|
|
331
|
-
3. The fix (specific code change)
|
|
332
|
-
4. Expected improvement
|
|
333
|
-
|
|
334
|
-
---
|
|
335
|
-
|
|
336
|
-
## Performance Step 7: Apply Fixes
|
|
337
|
-
|
|
338
|
-
Provide optimized code for each finding. Include before/after with expected impact.
|
|
339
|
-
|
|
340
|
-
Common fixes:
|
|
341
|
-
- Replace `wait()` with `task.wait()`
|
|
342
|
-
- Consolidate Heartbeat connections
|
|
343
|
-
- Add `task.wait()` to prevent thread starvation
|
|
344
|
-
- Cache `GetService()` calls
|
|
345
|
-
- Use spatial indexing for distance checks
|
|
346
|
-
- Batch RemoteEvent updates
|
|
347
|
-
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
## Performance Step 8: Before/After
|
|
351
|
-
|
|
352
|
-
Document improvements:
|
|
353
|
-
1. **Metric** - What was measured (frame time, memory, network traffic)
|
|
354
|
-
2. **Before** - Original value
|
|
355
|
-
3. **After** - Improved value
|
|
356
|
-
4. **Change** - Percentage improvement
|
|
357
|
-
5. **Remaining** - What's left to optimize
|
|
358
|
-
|
|
359
|
-
---
|
|
360
|
-
|
|
361
|
-
# Monetization Lens
|
|
362
|
-
|
|
363
|
-
*Apply this lens when monetization code changed: GamePasses, DevProducts, Premium integration, shop UI, or when reviewing revenue strategy.*
|
|
364
|
-
|
|
365
|
-
---
|
|
366
|
-
|
|
367
|
-
## Monetization Step 1: Current State
|
|
368
|
-
|
|
369
|
-
Search for `MarketplaceService` usage. Find all GamePass and DevProduct references. List all existing monetization.
|
|
370
|
-
|
|
371
|
-
Record:
|
|
372
|
-
- All GamePasses (ID, name, price, what it grants)
|
|
373
|
-
- All DevProducts (ID, name, price, what it consumes)
|
|
374
|
-
- Premium benefits (if any)
|
|
375
|
-
- Where monetization is presented in-game
|
|
376
|
-
|
|
377
|
-
---
|
|
378
|
-
|
|
379
|
-
## Monetization Step 2: GamePass Review
|
|
380
|
-
|
|
381
|
-
Evaluate each GamePass:
|
|
382
|
-
- Is the value clear to the player?
|
|
383
|
-
- Is the price appropriate for what it grants?
|
|
384
|
-
- Is it discoverable in-game (shown at relevant moments)?
|
|
385
|
-
- Does it persist correctly (works after rejoin)?
|
|
386
|
-
- Is it idempotent (purchasing twice doesn't break anything)?
|
|
387
|
-
- Does it provide lasting value vs one-time use?
|
|
388
|
-
|
|
389
|
-
---
|
|
390
|
-
|
|
391
|
-
## Monetization Step 3: DevProduct Review
|
|
392
|
-
|
|
393
|
-
Evaluate each consumable:
|
|
394
|
-
- Is it compelling enough for repeat purchase?
|
|
395
|
-
- Is it priced for impulse buy (<100 Robux) or considered purchase?
|
|
396
|
-
- Does ProcessReceipt handle correctly (grant then confirm)?
|
|
397
|
-
- Are there diminishing returns or purchase limits?
|
|
398
|
-
- Is the value clear before purchasing?
|
|
399
|
-
- Does it complement (not replace) gameplay?
|
|
400
|
-
|
|
401
|
-
---
|
|
402
|
-
|
|
403
|
-
## Monetization Step 4: Missing Opportunities
|
|
404
|
-
|
|
405
|
-
Based on genre best practices, suggest monetization the game is missing.
|
|
406
|
-
|
|
407
|
-
**Simulator:** VIP GamePass (2x currency, exclusive pets), Auto-farm DevProduct, Lucky egg/spin DevProducts, Season pass.
|
|
408
|
-
|
|
409
|
-
**Tycoon:** VIP GamePass (faster income, exclusive materials), Extra plot slots, Cosmetic upgrades.
|
|
410
|
-
|
|
411
|
-
**RPG:** Inventory expansion, Experience boosts, Cosmetic gear, Character slots.
|
|
412
|
-
|
|
413
|
-
**Horror:** Skip/co-op passes, Cosmetic items (flashlight skins, outfits), Hint system DevProduct.
|
|
414
|
-
|
|
415
|
-
**Battle Royale:** Battle pass (seasonal), Cosmetic-only items, Victory animations.
|
|
416
|
-
|
|
417
|
-
---
|
|
418
|
-
|
|
419
|
-
## Monetization Step 5: Pricing Analysis
|
|
420
|
-
|
|
421
|
-
Compare prices against Roblox norms:
|
|
422
|
-
- **Entry point** - 25-49 Robux for impulse buys
|
|
423
|
-
- **Mid-tier** - 99-199 Robux for meaningful upgrades
|
|
424
|
-
- **Premium** - 499-999 Robux for VIP/lifetime benefits
|
|
425
|
-
- **Consumables** - 10-50 Robux for repeat purchases
|
|
426
|
-
|
|
427
|
-
Evaluate Robux-to-value ratio, price anchoring, and bundle discounts.
|
|
428
|
-
|
|
429
|
-
---
|
|
430
|
-
|
|
431
|
-
## Monetization Step 6: Premium Integration
|
|
432
|
-
|
|
433
|
-
Check:
|
|
434
|
-
- Premium payouts configured and optimized
|
|
435
|
-
- Premium-exclusive benefits (10-15% bonus, exclusive items, priority queue)
|
|
436
|
-
- Premium benefits clearly communicated to non-Premium players
|
|
437
|
-
- Premium doesn't create unfair advantages in competitive games
|
|
438
|
-
|
|
439
|
-
---
|
|
440
|
-
|
|
441
|
-
## Monetization Step 7: Ad Integration
|
|
442
|
-
|
|
443
|
-
Evaluate if Rewarded Video Ads would fit:
|
|
444
|
-
- Natural placement points (extra life, bonus currency, skip wait timer)
|
|
445
|
-
- Player opt-in only (never forced)
|
|
446
|
-
- Doesn't interrupt core gameplay loop
|
|
447
|
-
- Frequency capped (1 ad per X minutes)
|
|
448
|
-
- Value exchange is fair (ad view = meaningful reward)
|
|
449
|
-
|
|
450
|
-
---
|
|
451
|
-
|
|
452
|
-
## Monetization Step 8: Ethical Review
|
|
453
|
-
|
|
454
|
-
Flag potentially predatory patterns:
|
|
455
|
-
- Loot boxes / gambling mechanics - Are odds disclosed?
|
|
456
|
-
- FOMO pressure - Limited-time offers that pressure quick decisions?
|
|
457
|
-
- Pay-to-win - Can paying players dominate free players unfairly?
|
|
458
|
-
- Dark patterns - Confusing UI that leads to accidental purchases?
|
|
459
|
-
|
|
460
|
-
---
|
|
461
|
-
|
|
462
|
-
## Monetization Step 9: Recommendations Report
|
|
463
|
-
|
|
464
|
-
Prioritized list of monetization improvements:
|
|
465
|
-
|
|
466
|
-
- **Must-have** - Items that directly increase revenue
|
|
467
|
-
- **Should-have** - Items that improve conversion or retention
|
|
468
|
-
- **Nice-to-have** - Items that optimize existing revenue
|
|
469
|
-
|
|
470
|
-
For each recommendation:
|
|
471
|
-
1. What to implement
|
|
472
|
-
2. Why it will increase revenue
|
|
473
|
-
3. Implementation effort (small/medium/large)
|
|
474
|
-
4. Where in the game to present it
|
|
475
|
-
5. Pricing suggestion
|
|
476
|
-
|
|
477
|
-
---
|
|
478
|
-
|
|
479
|
-
# Networking / Remote Architecture Lens
|
|
480
|
-
|
|
481
|
-
*Apply this lens when networking code changed: new remotes, Bindable refactoring, effect syncing, or when auditing remote surface for scaling.*
|
|
482
|
-
|
|
483
|
-
---
|
|
484
|
-
|
|
485
|
-
## Networking Step 1: Remote Surface Map
|
|
486
|
-
|
|
487
|
-
Search for all `RemoteEvent`, `RemoteFunction`, and `UnreliableRemoteEvent` instances. Search for `:FireServer`, `:FireClient`, `:FireAllClients`, `:InvokeServer`, `:InvokeClient`, and `:Invoke` to map the full remote surface.
|
|
488
|
-
|
|
489
|
-
Record every remote found with:
|
|
490
|
-
- Name
|
|
491
|
-
- Location (ReplicatedStorage path)
|
|
492
|
-
- Type (RemoteEvent, RemoteFunction, UnreliableRemoteEvent)
|
|
493
|
-
- Direction (Client→Server, Server→Client, Client↔Server)
|
|
494
|
-
- What it does and whether it carries critical or cosmetic data
|
|
495
|
-
|
|
496
|
-
---
|
|
497
|
-
|
|
498
|
-
## Networking Step 2: Type Selection Audit
|
|
499
|
-
|
|
500
|
-
Verify each remote uses the correct type:
|
|
501
|
-
|
|
502
|
-
| Type | Best For | Notes |
|
|
503
|
-
|------|----------|-------|
|
|
504
|
-
| `RemoteEvent` | Fire-and-forget messages | Most common. No return value. |
|
|
505
|
-
| `RemoteFunction` | Request-response patterns | Blocking. Avoid in hot paths. Prefer RemoteEvent + callback pattern. |
|
|
506
|
-
| `UnreliableRemoteEvent` | Loss-tolerant data (effects, cosmetics, position updates) | Can drop packets under load. NEVER for currency, inventory, damage, game state. |
|
|
507
|
-
| `BindableEvent` | Intra-client or intra-server pub/sub | LocalScript→LocalScript or Script→Script only. Never cross Server↔Client boundary. |
|
|
508
|
-
| `BindableFunction` | Intra-client or intra-server request-response | Same boundary rule as BindableEvent. |
|
|
509
|
-
|
|
510
|
-
Flag any remote using the wrong type:
|
|
511
|
-
- `RemoteFunction` where `RemoteEvent` + response pattern would suffice
|
|
512
|
-
- `RemoteEvent` used as BindableEvent (firing a remote only to reach other scripts on the same client)
|
|
513
|
-
- `RemoteEvent` carrying critical data that should use reliable delivery
|
|
514
|
-
- `UnreliableRemoteEvent` carrying game-critical state
|
|
515
|
-
|
|
516
|
-
---
|
|
517
|
-
|
|
518
|
-
## Networking Step 3: RemoteFunction Check
|
|
519
|
-
|
|
520
|
-
`RemoteFunction` is a blocking pattern - the caller yields until the receiver returns. This can cause lag if overused.
|
|
521
|
-
|
|
522
|
-
Verify:
|
|
523
|
-
- `:InvokeServer` is used sparingly (not per-frame or in tight loops)
|
|
524
|
-
- `:InvokeClient` is only used when the server genuinely needs an answer from a specific client (rare)
|
|
525
|
-
- Consider replacing with `RemoteEvent` + response event pattern for non-critical request-response flows
|
|
526
|
-
- `:InvokeClient` has a timeout safeguard (client may not respond)
|
|
527
|
-
|
|
528
|
-
---
|
|
529
|
-
|
|
530
|
-
## Networking Step 4: Bindable Audit
|
|
531
|
-
|
|
532
|
-
`BindableEvent` and `BindableFunction` are strictly intra-boundary (Server→Server or Client→Client).
|
|
533
|
-
|
|
534
|
-
Check:
|
|
535
|
-
- No `BindableEvent` / `BindableFunction` in ReplicatedStorage that is fired by server and listened to by client (use `RemoteEvent` instead - Bindables don't replicate)
|
|
536
|
-
- Client-side `BindableEvent`s are used for decoupling LocalScripts (cleaner than direct module references)
|
|
537
|
-
- Server-side `BindableEvent`s are used for service-to-service pub/sub within the same script context
|
|
538
|
-
- No orphaned or unused Bindable instances left in the hierarchy
|
|
539
|
-
|
|
540
|
-
---
|
|
541
|
-
|
|
542
|
-
## Networking Step 5: UnreliableRemoteEvent Audit
|
|
543
|
-
|
|
544
|
-
`UnreliableRemoteEvent` trades reliability for speed. Data may arrive out of order, arrive duplicates, or not arrive at all.
|
|
545
|
-
|
|
546
|
-
Verify:
|
|
547
|
-
- Used only for loss-tolerant data: cosmetic effects, non-critical position interpolation, particle triggers, sound events
|
|
548
|
-
- Never used for: currency transactions, inventory changes, damage application, game state transitions, unlock/upgrade actions
|
|
549
|
-
- Client-side has fallback logic (if a cosmetic event is dropped, the game still functions)
|
|
550
|
-
- Server-side does not rely on unreliable delivery for authoritative state
|
|
551
|
-
|
|
552
|
-
Recommended pattern - separate critical and cosmetic remotes:
|
|
553
|
-
```luau
|
|
554
|
-
-- Critical: reliable RemoteEvent
|
|
555
|
-
local PurchaseRequest = ReplicatedStorage.Remotes.PurchaseRequest -- RemoteEvent
|
|
556
|
-
-- Cosmetic: unreliable
|
|
557
|
-
local HitEffectSync = ReplicatedStorage.Remotes.HitEffectSync -- UnreliableRemoteEvent
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
---
|
|
561
|
-
|
|
562
|
-
## Networking Step 6: Organization & Naming
|
|
563
|
-
|
|
564
|
-
Check:
|
|
565
|
-
- All remotes organized under a consistent folder structure (e.g., `ReplicatedStorage > Remotes > {Category}`)
|
|
566
|
-
- Naming convention is consistent: `PascalCase` for remote names, `VerbNoun` pattern (`PlayerRequestPurchase`, `ServerSyncInventory`)
|
|
567
|
-
- No unused / orphaned remotes in ReplicatedStorage
|
|
568
|
-
- RemoteFunction and its response type are clearly paired by naming
|
|
569
|
-
- UnreliableRemoteEvents are clearly distinguishable by name or naming suffix (e.g., `Sync_` prefix)
|
|
570
|
-
|
|
571
|
-
---
|
|
572
|
-
|
|
573
|
-
## Networking Step 7: Anti-Patterns
|
|
574
|
-
|
|
575
|
-
Flag these common networking mistakes:
|
|
576
|
-
|
|
577
|
-
- **Using RemoteFunction in a hot path** - blocks the caller. Prefer RemoteEvent.
|
|
578
|
-
- **Using RemoteEvent when BindableEvent would suffice** - a remote that only reaches scripts on the same player should be a BindableEvent, avoiding network overhead.
|
|
579
|
-
- **Using RemoteEvent when UnreliableRemoteEvent would be better** - for high-frequency cosmetic updates (position, VFX), unreliable saves bandwidth and CPU.
|
|
580
|
-
- **Using UnreliableRemoteEvent for critical state** - dropped packets will corrupt game state.
|
|
581
|
-
- **No remote surface map** - undocumented remotes make auditing and debugging harder.
|
|
582
|
-
- **Inconsistent naming** - `fireRemote`, `HandleRequest`, `r1` mixed in the same project.
|
|
583
|
-
- **Remotes scattered across ReplicatedStorage** - all remotes should live under a single `Remotes` folder.
|
|
584
|
-
- **BindableEvent in ReplicatedStorage used cross-boundary** - Bindables don't replicate; this will silently fail on the client.
|
|
585
|
-
|
|
586
|
-
---
|
|
587
|
-
|
|
588
|
-
# Data Persistence Lens
|
|
589
|
-
|
|
590
|
-
*Apply this lens when data persistence code changed: player data loading/saving, ProfileStore setup, data migration, or when auditing data integrity for production readiness.*
|
|
591
|
-
|
|
592
|
-
---
|
|
593
|
-
|
|
594
|
-
## Data Step 1: Data Library Detection
|
|
595
|
-
|
|
596
|
-
Search for how player data is stored:
|
|
597
|
-
|
|
598
|
-
- **ProfileStore found (recommended):** Continue to Step 2.
|
|
599
|
-
- **Raw DataStoreService** used directly: Flag as **High** priority. ProfileStore handles session locking, auto-save, BindToClose, retry logic, and schema reconciliation automatically. Recommend migration. Cross-reference: `roblox-data` §4.
|
|
600
|
-
- **No data persistence found:** Flag if the game needs it.
|
|
601
|
-
|
|
602
|
-
Verify ProfileStore installation:
|
|
603
|
-
- Wally dependency: `ProfileStore = "madstudioroblox/profileservice@1.4.0"`
|
|
604
|
-
- Or manual ModuleScript in ServerScriptService / ReplicatedStorage
|
|
605
|
-
- ProfileStore is required from the correct path
|
|
606
|
-
|
|
607
|
-
---
|
|
608
|
-
|
|
609
|
-
## Data Step 2: Profile Structure Review
|
|
610
|
-
|
|
611
|
-
Verify the `PROFILE_TEMPLATE` (or equivalent schema):
|
|
612
|
-
|
|
613
|
-
- Exists and is a table
|
|
614
|
-
- Contains a `DataVersion` field (number, incremented on schema changes)
|
|
615
|
-
- Uses nested structure for non-trivial games (separate `Currency`, `Progression`, `Inventory`, `Settings` sections)
|
|
616
|
-
- Every field has a sensible default value (ProfileStore's `:Reconcile()` fills missing fields from the template)
|
|
617
|
-
- No Instance references in the template (only JSON-compatible types: number, string, boolean, table)
|
|
618
|
-
- Cross-reference: `roblox-data` §6
|
|
619
|
-
|
|
620
|
-
---
|
|
621
|
-
|
|
622
|
-
## Data Step 3: Session Locking
|
|
623
|
-
|
|
624
|
-
Confirm session locking is properly configured:
|
|
625
|
-
|
|
626
|
-
- `profile:AddUserId(player.UserId)` is called after loading (GDPR compliance)
|
|
627
|
-
- `profile:ListenToRelease()` is connected, kicks the player if lock is stolen
|
|
628
|
-
- `"ForceLoad"` strategy is used (waits for lock, does not fail silently)
|
|
629
|
-
- `profile:Reconcile()` is called after loading to fill missing fields from template
|
|
630
|
-
- Cross-reference: `roblox-data` §5
|
|
631
|
-
|
|
632
|
-
---
|
|
633
|
-
|
|
634
|
-
## Data Step 4: Lifecycle Review
|
|
635
|
-
|
|
636
|
-
Verify the full player data lifecycle:
|
|
637
|
-
|
|
638
|
-
| Event | Must Do | Cross-Ref |
|
|
639
|
-
|-------|---------|-----------|
|
|
640
|
-
| `PlayerAdded` | Load profile via `LoadProfileAsync`, reconcile, store in cache | `roblox-data` §4 |
|
|
641
|
-
| Mid-game | Mutate `profile.Data.*` directly, leaderstats sync back | `roblox-data` §4 |
|
|
642
|
-
| `PlayerRemoving` | Sync leaderstats to profile, call `profile:Release()` | `roblox-data` §4 |
|
|
643
|
-
| `BindToClose` | If using raw DataStore: parallel saves with 30s timeout | `roblox-data` §10.3 |
|
|
644
|
-
| Auto-save | ProfileStore handles internally. If raw: 5-min interval, exponential retry | `roblox-data` §10.1 |
|
|
645
|
-
|
|
646
|
-
Flag missing lifecycle handlers, especially `PlayerRemoving` release and `BindToClose`.
|
|
647
|
-
|
|
648
|
-
---
|
|
649
|
-
|
|
650
|
-
## Data Step 5: Data Access Patterns
|
|
651
|
-
|
|
652
|
-
Check how data is read and written:
|
|
653
|
-
|
|
654
|
-
- Data is mutated via `profile.Data.fieldName`, not by calling DataStore methods directly
|
|
655
|
-
- Leaderstats `IntValue`/`StringValue` are synced back to `profile.Data` before `:Release()`
|
|
656
|
-
- Other scripts access player data via a getter module (`getProfile(player)`), not by directly importing ProfileStore
|
|
657
|
-
- Data writes happen immediately in memory; saves are handled by ProfileStore's auto-save interval
|
|
658
|
-
- Cross-reference: `roblox-data` §4
|
|
659
|
-
|
|
660
|
-
---
|
|
661
|
-
|
|
662
|
-
## Data Step 6: Migration Strategy
|
|
663
|
-
|
|
664
|
-
If the game has evolved its data schema:
|
|
665
|
-
|
|
666
|
-
- `DataVersion` field exists in the template
|
|
667
|
-
- Migration functions exist for each version bump (v1→v2, v2→v3, etc.)
|
|
668
|
-
- `DataMigrations.migrate(data)` is called after `LoadProfileAsync` and before `:Reconcile()`
|
|
669
|
-
- Migrations are sequential, pure functions (no yields, no side effects)
|
|
670
|
-
- Migration for old data that lacks `DataVersion` defaults to version 1
|
|
671
|
-
- Cross-reference: `roblox-data` §7
|
|
672
|
-
|
|
673
|
-
---
|
|
674
|
-
|
|
675
|
-
## Data Step 7: Anti-Patterns
|
|
676
|
-
|
|
677
|
-
Flag these common data persistence mistakes:
|
|
678
|
-
|
|
679
|
-
- **Raw DataStore for player state** instead of ProfileStore - missing session locking, auto-save, retry, BindToClose. Cross-ref: `roblox-data` §4.
|
|
680
|
-
- **Saving too frequently** - every coin pickup triggers a DataStore write. Rate limits will be hit. Cross-ref: `roblox-data` §11.
|
|
681
|
-
- **No `pcall` around DataStore calls** - unhandled errors crash the script. Cross-ref: `roblox-data` §11.
|
|
682
|
-
- **Storing Instance references in DataStore** - Instances are not serializable. Store IDs instead. Cross-ref: `roblox-data` §11.
|
|
683
|
-
- **No data validation before save** - NaN values or corrupt data can silently break the save. Cross-ref: `roblox-data` §10.5.
|
|
684
|
-
- **Missing `DataVersion`** - no way to detect or migrate old schema formats. Cross-ref: `roblox-data` §6.
|
|
685
|
-
- **Session locking bypassed** - using raw DataStore without manual lock implementation risks data loss. Cross-ref: `roblox-data` §5.
|
|
686
|
-
- **No `BindToClose` handler** - server shutdown loses unsaved player data. Cross-ref: `roblox-data` §10.3.
|
|
1
|
+
---
|
|
2
|
+
name: roblox-code-review
|
|
3
|
+
description: "Code review with security, performance, and monetization lenses for Roblox projects"
|
|
4
|
+
tags: [roblox, code-review, security, performance, monetization, networking, data-persistence]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /code-review - Code Quality Review
|
|
8
|
+
|
|
9
|
+
You are performing a code quality review on a Roblox project. Follow these 8 steps. Apply the relevant lens based on what changed. Don't apply all lenses every time.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Quick Reference
|
|
14
|
+
|
|
15
|
+
**Load lenses below only when code matching that domain changed. Don't apply all lenses every time.**
|
|
16
|
+
|
|
17
|
+
Key rules:
|
|
18
|
+
- **Remote types:** RemoteEvent (fire-and-forget), UnreliableRemoteEvent (loss-tolerant VFX/position), RemoteFunction (request-response, use sparingly), BindableEvent/BindableFunction (intra-client/server internal, never cross-boundary).
|
|
19
|
+
- **Data persistence:** ALWAYS ProfileStore for player state. Never raw DataStore. Schema template with defaults, DataVersion, migration functions, session locking, BindToClose.
|
|
20
|
+
- **Security:** Validate every remote parameter server-side. Rate-limit all remotes per-player.
|
|
21
|
+
- **Performance:** Consolidate Heartbeat loops. Cache services. Disconnect unused events.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Step 1: Project Scan
|
|
26
|
+
|
|
27
|
+
Read the project directory structure. Survey script count, naming patterns, and organization.
|
|
28
|
+
|
|
29
|
+
Record:
|
|
30
|
+
- Total script count
|
|
31
|
+
- Folder organization
|
|
32
|
+
- Module naming patterns
|
|
33
|
+
- Whether the project uses Rojo/Wally or in-Studio editing
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Step 2: Organization Review
|
|
38
|
+
|
|
39
|
+
Check:
|
|
40
|
+
- Scripts in correct locations (ServerScriptService, StarterPlayerScripts, etc.)
|
|
41
|
+
- Proper use of services vs standalone scripts
|
|
42
|
+
- Clean folder structure (no orphaned scripts, no nesting > 3 levels deep)
|
|
43
|
+
- Module naming conventions consistent (PascalCase for ModuleScripts, camelCase for functions)
|
|
44
|
+
- No scripts with duplicate or overlapping responsibilities
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Step 3: Code Quality Scan
|
|
49
|
+
|
|
50
|
+
Search for anti-patterns:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
Deprecated APIs:
|
|
54
|
+
- wait( → replace with task.wait()
|
|
55
|
+
- spawn( → replace with task.spawn()
|
|
56
|
+
- delay( → replace with task.delay()
|
|
57
|
+
|
|
58
|
+
Code smells:
|
|
59
|
+
- Global variables (should be module-scoped)
|
|
60
|
+
- Missing type annotations on public functions
|
|
61
|
+
- Instance.new() in client scripts (should be server-created)
|
|
62
|
+
- while true without task.wait() (unbounded loops)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Check for:
|
|
66
|
+
- Deprecated APIs (`wait()`, `spawn()`, `delay()`)
|
|
67
|
+
- Global variable usage (should be module-scoped)
|
|
68
|
+
- Missing type annotations on public functions
|
|
69
|
+
- Inconsistent naming conventions
|
|
70
|
+
- Dead code / unreachable code
|
|
71
|
+
- Duplicate code across scripts
|
|
72
|
+
- Overly long functions (>100 lines, should be refactored)
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Step 4: Architecture Review
|
|
77
|
+
|
|
78
|
+
Check:
|
|
79
|
+
- Module boundaries - Does each module have a single responsibility?
|
|
80
|
+
- Dependency direction - Do modules depend on abstractions, not concrete implementations?
|
|
81
|
+
- Circular requires - Any modules that depend on each other?
|
|
82
|
+
- Separation of concerns - Server vs Client logic properly separated
|
|
83
|
+
- Framework usage - If using a framework, is it used consistently?
|
|
84
|
+
- Configuration - Hardcoded values should be in config modules
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Step 5: Security Quick-Check
|
|
89
|
+
|
|
90
|
+
Quick scan for:
|
|
91
|
+
- Unvalidated RemoteEvent handlers (server-side)
|
|
92
|
+
- Client-trusted logic (currency, inventory, damage, position)
|
|
93
|
+
- Sensitive data in ReplicatedStorage or StarterPlayer
|
|
94
|
+
- Missing rate limiting on remotes
|
|
95
|
+
- ProcessReceipt implementation correctness
|
|
96
|
+
|
|
97
|
+
> **For a deep security review, run the Security Lens below.**
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Step 6: Performance Quick-Check
|
|
102
|
+
|
|
103
|
+
Quick scan for:
|
|
104
|
+
- `wait()` or `spawn()` in tight loops
|
|
105
|
+
- Multiple `RunService.Heartbeat:Connect()` in same script
|
|
106
|
+
- Large tables without cleanup
|
|
107
|
+
- Undisconnected events (memory leaks)
|
|
108
|
+
- Unanchored parts without collision groups
|
|
109
|
+
- Excessive RemoteEvent usage
|
|
110
|
+
|
|
111
|
+
> **For a deep performance review, run the Performance Lens below.**
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Step 7: Quality Report
|
|
116
|
+
|
|
117
|
+
Rate overall quality:
|
|
118
|
+
|
|
119
|
+
- **A** - Production-ready. Clean, organized, secure, performant
|
|
120
|
+
- **B** - Solid with minor issues. Safe to ship with minor cleanup
|
|
121
|
+
- **C** - Functional but needs work. Ship with caveats
|
|
122
|
+
- **D** - Significant issues. Needs refactoring before ship
|
|
123
|
+
- **F** - Critical problems. Do not ship in current state
|
|
124
|
+
|
|
125
|
+
List findings by severity:
|
|
126
|
+
- **Critical** - Security vulnerabilities, data loss risk, crashes
|
|
127
|
+
- **High** - Memory leaks, performance bottlenecks, broken features
|
|
128
|
+
- **Medium** - Code smells, deprecated APIs, poor organization
|
|
129
|
+
- **Low** - Style inconsistencies, missing documentation
|
|
130
|
+
|
|
131
|
+
For each finding, provide:
|
|
132
|
+
1. File and line (or function name)
|
|
133
|
+
2. What's wrong
|
|
134
|
+
3. The specific fix (code)
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Step 8: Refactoring Suggestions
|
|
139
|
+
|
|
140
|
+
If significant issues found, suggest refactoring priorities:
|
|
141
|
+
|
|
142
|
+
1. **Immediate** - Must fix before next publish
|
|
143
|
+
2. **Short-term** - Fix in the next development cycle
|
|
144
|
+
3. **Long-term** - Plan for when the project grows
|
|
145
|
+
|
|
146
|
+
For each suggestion:
|
|
147
|
+
- What to change
|
|
148
|
+
- Why it matters
|
|
149
|
+
- Estimated effort (small/medium/large)
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
# Security Lens
|
|
154
|
+
|
|
155
|
+
*Apply this lens when security-relevant code changed: remotes, data persistence, monetization, player input handling, or server authority.*
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Security Step 1: Remote Surface Scan
|
|
160
|
+
|
|
161
|
+
Search for all `RemoteEvent` and `RemoteFunction` instances. Search for `:FireServer`, `:FireClient`, `:InvokeServer`, `:InvokeClient` to map the full remote surface.
|
|
162
|
+
|
|
163
|
+
Record every remote found with:
|
|
164
|
+
- Name
|
|
165
|
+
- Location (ReplicatedStorage path)
|
|
166
|
+
- Direction (Client→Server, Server→Client, Client↔Server)
|
|
167
|
+
- What it appears to do
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Security Step 2: Validation Check
|
|
172
|
+
|
|
173
|
+
For each remote, verify:
|
|
174
|
+
- **Argument type checking** - Server validates `typeof(arg)` for every parameter
|
|
175
|
+
- **Range validation** - Numeric inputs checked against min/max bounds
|
|
176
|
+
- **Cooldown enforcement** - Rate limiting prevents spam/exploitation
|
|
177
|
+
- **Authorization check** - Server verifies the requesting player owns the action
|
|
178
|
+
- **Sanitization** - String inputs cleaned, no injection vectors
|
|
179
|
+
|
|
180
|
+
Flag any remote that accepts input without full server-side validation.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Security Step 3: Client Trust Audit
|
|
185
|
+
|
|
186
|
+
Search for client-side logic that should be server-side:
|
|
187
|
+
- Currency operations (giving/removing coins, cash, gems)
|
|
188
|
+
- Inventory changes (adding/removing items)
|
|
189
|
+
- Damage calculation (should be server-authoritative)
|
|
190
|
+
- Position setting (teleporting, movement authority)
|
|
191
|
+
- Leaderboard/stat modification
|
|
192
|
+
- Game state changes (win/lose conditions)
|
|
193
|
+
|
|
194
|
+
**Rule:** If it affects game state or other players, it must be server-validated.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Security Step 4: Data Exposure Check
|
|
199
|
+
|
|
200
|
+
Verify:
|
|
201
|
+
- No sensitive data in `ReplicatedStorage` (secrets, configs with admin keys)
|
|
202
|
+
- No server-only logic in `StarterPlayerScripts` (game state, anti-cheat)
|
|
203
|
+
- No player data exposed to other players via remotes (unless intentional)
|
|
204
|
+
- RemoteEvent payloads don't include excess data (send only what's needed)
|
|
205
|
+
- No `require()` paths to server-only modules from client scripts
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Security Step 5: Rate Limiting Check
|
|
210
|
+
|
|
211
|
+
Verify all remotes have per-player rate limiting:
|
|
212
|
+
- Track last-fire timestamps server-side
|
|
213
|
+
- Reject requests faster than expected human input
|
|
214
|
+
- Different limits for different actions (chat: 1/sec, purchase: 1/5sec, movement: 30/sec)
|
|
215
|
+
- Log rate limit violations for monitoring
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Security Step 6: Vulnerability Report
|
|
220
|
+
|
|
221
|
+
Categorize findings:
|
|
222
|
+
|
|
223
|
+
- **Critical** - Exploitable for direct advantage (free currency, item duplication, account takeover)
|
|
224
|
+
- **High** - Data exposure or corruption possible
|
|
225
|
+
- **Medium** - Potential for abuse with moderate effort
|
|
226
|
+
- **Low** - Best practice violation, hard to exploit
|
|
227
|
+
|
|
228
|
+
For each vulnerability:
|
|
229
|
+
1. Remote/method affected
|
|
230
|
+
2. Exploit scenario (how an attacker would abuse it)
|
|
231
|
+
3. Impact (what they gain)
|
|
232
|
+
4. Fix (specific hardened code)
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Security Step 7: Hardening
|
|
237
|
+
|
|
238
|
+
Apply hardened code for each vulnerable remote. Include before/after for clarity.
|
|
239
|
+
|
|
240
|
+
Hardening patterns to apply:
|
|
241
|
+
- Server-side validation wrapper for each remote
|
|
242
|
+
- Rate limiting middleware
|
|
243
|
+
- Data sanitization functions
|
|
244
|
+
- Ownership verification for state changes
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Security Step 8: Re-verify
|
|
249
|
+
|
|
250
|
+
Confirm all vulnerabilities addressed:
|
|
251
|
+
- Each Critical/High finding has a corresponding fix applied
|
|
252
|
+
- Legitimate functionality still works after hardening
|
|
253
|
+
- No new issues introduced by the fixes
|
|
254
|
+
- Output a before/after comparison of the security posture
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
# Performance Lens
|
|
259
|
+
|
|
260
|
+
*Apply this lens when performance-sensitive code changed: large loops, data structures, rendering, network-heavy features, or when the user reports lag.*
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Performance Step 1: Project Scan
|
|
265
|
+
|
|
266
|
+
Search for known anti-patterns:
|
|
267
|
+
- `wait()` and `spawn()` (yield-based, blocks thread)
|
|
268
|
+
- `RunService` loops (Heartbeat, Stepped, RenderStepped frequency)
|
|
269
|
+
- `GetDescendants()` and `GetChildren()` in loops (expensive at scale)
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Performance Step 2: Part Audit
|
|
274
|
+
|
|
275
|
+
Check for:
|
|
276
|
+
- Total part count (target: <50,000 for mobile, <200,000 for PC)
|
|
277
|
+
- Unanchored parts without assembly (physics chaos)
|
|
278
|
+
- Parts without collision groups (unnecessary collision detection)
|
|
279
|
+
- MeshParts vs Unions vs parts (MeshParts are most efficient)
|
|
280
|
+
- Transparent/reflective parts (rendering cost)
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Performance Step 3: Script Audit
|
|
285
|
+
|
|
286
|
+
Check for:
|
|
287
|
+
- Multiple `Heartbeat:Connect()` in the same script (consolidate into one)
|
|
288
|
+
- Excessive RemoteEvent usage (batch updates, reduce frequency)
|
|
289
|
+
- Tight loops without `task.wait()` (thread starvation)
|
|
290
|
+
- Unindexed table operations (linear search vs hash lookup)
|
|
291
|
+
- String concatenation in loops (use table.concat instead)
|
|
292
|
+
- `Instance.new()` in hot paths (cache and reuse)
|
|
293
|
+
- Deep `WaitForChild()` chains (cache references)
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Performance Step 4: Memory Audit
|
|
298
|
+
|
|
299
|
+
Check for:
|
|
300
|
+
- Undisconnected events (every `:Connect()` must have a matching `:Disconnect()`)
|
|
301
|
+
- Unreferenced instances (created but never parented or referenced)
|
|
302
|
+
- Large data tables held in memory (use lazy loading)
|
|
303
|
+
- String/internment issues (unnecessary duplicate strings)
|
|
304
|
+
- Module-level state that grows without cleanup
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Performance Step 5: Network Audit
|
|
309
|
+
|
|
310
|
+
Check:
|
|
311
|
+
- RemoteEvent frequency - Are updates sent every frame when 1/sec would suffice?
|
|
312
|
+
- Data size per event - Are payloads unnecessarily large?
|
|
313
|
+
- Unnecessary replication - Is data sent to all players when only some need it?
|
|
314
|
+
- `FireAllClients` vs `FireClient` - Target specific players when possible
|
|
315
|
+
- Debouncing - Are rapid-fire remotes properly debounced server-side?
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Performance Step 6: Priority Report
|
|
320
|
+
|
|
321
|
+
Generate prioritized list:
|
|
322
|
+
|
|
323
|
+
- **Critical** - Causes crashes or completely unplayable experience
|
|
324
|
+
- **High** - Noticeable lag, frame drops, or rubber-banding
|
|
325
|
+
- **Medium** - Suboptimal but functional, wasted resources
|
|
326
|
+
- **Low** - Minor optimization opportunity
|
|
327
|
+
|
|
328
|
+
For each item:
|
|
329
|
+
1. What's slow
|
|
330
|
+
2. Why it's slow (technical explanation)
|
|
331
|
+
3. The fix (specific code change)
|
|
332
|
+
4. Expected improvement
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Performance Step 7: Apply Fixes
|
|
337
|
+
|
|
338
|
+
Provide optimized code for each finding. Include before/after with expected impact.
|
|
339
|
+
|
|
340
|
+
Common fixes:
|
|
341
|
+
- Replace `wait()` with `task.wait()`
|
|
342
|
+
- Consolidate Heartbeat connections
|
|
343
|
+
- Add `task.wait()` to prevent thread starvation
|
|
344
|
+
- Cache `GetService()` calls
|
|
345
|
+
- Use spatial indexing for distance checks
|
|
346
|
+
- Batch RemoteEvent updates
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Performance Step 8: Before/After
|
|
351
|
+
|
|
352
|
+
Document improvements:
|
|
353
|
+
1. **Metric** - What was measured (frame time, memory, network traffic)
|
|
354
|
+
2. **Before** - Original value
|
|
355
|
+
3. **After** - Improved value
|
|
356
|
+
4. **Change** - Percentage improvement
|
|
357
|
+
5. **Remaining** - What's left to optimize
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
# Monetization Lens
|
|
362
|
+
|
|
363
|
+
*Apply this lens when monetization code changed: GamePasses, DevProducts, Premium integration, shop UI, or when reviewing revenue strategy.*
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Monetization Step 1: Current State
|
|
368
|
+
|
|
369
|
+
Search for `MarketplaceService` usage. Find all GamePass and DevProduct references. List all existing monetization.
|
|
370
|
+
|
|
371
|
+
Record:
|
|
372
|
+
- All GamePasses (ID, name, price, what it grants)
|
|
373
|
+
- All DevProducts (ID, name, price, what it consumes)
|
|
374
|
+
- Premium benefits (if any)
|
|
375
|
+
- Where monetization is presented in-game
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Monetization Step 2: GamePass Review
|
|
380
|
+
|
|
381
|
+
Evaluate each GamePass:
|
|
382
|
+
- Is the value clear to the player?
|
|
383
|
+
- Is the price appropriate for what it grants?
|
|
384
|
+
- Is it discoverable in-game (shown at relevant moments)?
|
|
385
|
+
- Does it persist correctly (works after rejoin)?
|
|
386
|
+
- Is it idempotent (purchasing twice doesn't break anything)?
|
|
387
|
+
- Does it provide lasting value vs one-time use?
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Monetization Step 3: DevProduct Review
|
|
392
|
+
|
|
393
|
+
Evaluate each consumable:
|
|
394
|
+
- Is it compelling enough for repeat purchase?
|
|
395
|
+
- Is it priced for impulse buy (<100 Robux) or considered purchase?
|
|
396
|
+
- Does ProcessReceipt handle correctly (grant then confirm)?
|
|
397
|
+
- Are there diminishing returns or purchase limits?
|
|
398
|
+
- Is the value clear before purchasing?
|
|
399
|
+
- Does it complement (not replace) gameplay?
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Monetization Step 4: Missing Opportunities
|
|
404
|
+
|
|
405
|
+
Based on genre best practices, suggest monetization the game is missing.
|
|
406
|
+
|
|
407
|
+
**Simulator:** VIP GamePass (2x currency, exclusive pets), Auto-farm DevProduct, Lucky egg/spin DevProducts, Season pass.
|
|
408
|
+
|
|
409
|
+
**Tycoon:** VIP GamePass (faster income, exclusive materials), Extra plot slots, Cosmetic upgrades.
|
|
410
|
+
|
|
411
|
+
**RPG:** Inventory expansion, Experience boosts, Cosmetic gear, Character slots.
|
|
412
|
+
|
|
413
|
+
**Horror:** Skip/co-op passes, Cosmetic items (flashlight skins, outfits), Hint system DevProduct.
|
|
414
|
+
|
|
415
|
+
**Battle Royale:** Battle pass (seasonal), Cosmetic-only items, Victory animations.
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Monetization Step 5: Pricing Analysis
|
|
420
|
+
|
|
421
|
+
Compare prices against Roblox norms:
|
|
422
|
+
- **Entry point** - 25-49 Robux for impulse buys
|
|
423
|
+
- **Mid-tier** - 99-199 Robux for meaningful upgrades
|
|
424
|
+
- **Premium** - 499-999 Robux for VIP/lifetime benefits
|
|
425
|
+
- **Consumables** - 10-50 Robux for repeat purchases
|
|
426
|
+
|
|
427
|
+
Evaluate Robux-to-value ratio, price anchoring, and bundle discounts.
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## Monetization Step 6: Premium Integration
|
|
432
|
+
|
|
433
|
+
Check:
|
|
434
|
+
- Premium payouts configured and optimized
|
|
435
|
+
- Premium-exclusive benefits (10-15% bonus, exclusive items, priority queue)
|
|
436
|
+
- Premium benefits clearly communicated to non-Premium players
|
|
437
|
+
- Premium doesn't create unfair advantages in competitive games
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## Monetization Step 7: Ad Integration
|
|
442
|
+
|
|
443
|
+
Evaluate if Rewarded Video Ads would fit:
|
|
444
|
+
- Natural placement points (extra life, bonus currency, skip wait timer)
|
|
445
|
+
- Player opt-in only (never forced)
|
|
446
|
+
- Doesn't interrupt core gameplay loop
|
|
447
|
+
- Frequency capped (1 ad per X minutes)
|
|
448
|
+
- Value exchange is fair (ad view = meaningful reward)
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## Monetization Step 8: Ethical Review
|
|
453
|
+
|
|
454
|
+
Flag potentially predatory patterns:
|
|
455
|
+
- Loot boxes / gambling mechanics - Are odds disclosed?
|
|
456
|
+
- FOMO pressure - Limited-time offers that pressure quick decisions?
|
|
457
|
+
- Pay-to-win - Can paying players dominate free players unfairly?
|
|
458
|
+
- Dark patterns - Confusing UI that leads to accidental purchases?
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## Monetization Step 9: Recommendations Report
|
|
463
|
+
|
|
464
|
+
Prioritized list of monetization improvements:
|
|
465
|
+
|
|
466
|
+
- **Must-have** - Items that directly increase revenue
|
|
467
|
+
- **Should-have** - Items that improve conversion or retention
|
|
468
|
+
- **Nice-to-have** - Items that optimize existing revenue
|
|
469
|
+
|
|
470
|
+
For each recommendation:
|
|
471
|
+
1. What to implement
|
|
472
|
+
2. Why it will increase revenue
|
|
473
|
+
3. Implementation effort (small/medium/large)
|
|
474
|
+
4. Where in the game to present it
|
|
475
|
+
5. Pricing suggestion
|
|
476
|
+
|
|
477
|
+
---
|
|
478
|
+
|
|
479
|
+
# Networking / Remote Architecture Lens
|
|
480
|
+
|
|
481
|
+
*Apply this lens when networking code changed: new remotes, Bindable refactoring, effect syncing, or when auditing remote surface for scaling.*
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## Networking Step 1: Remote Surface Map
|
|
486
|
+
|
|
487
|
+
Search for all `RemoteEvent`, `RemoteFunction`, and `UnreliableRemoteEvent` instances. Search for `:FireServer`, `:FireClient`, `:FireAllClients`, `:InvokeServer`, `:InvokeClient`, and `:Invoke` to map the full remote surface.
|
|
488
|
+
|
|
489
|
+
Record every remote found with:
|
|
490
|
+
- Name
|
|
491
|
+
- Location (ReplicatedStorage path)
|
|
492
|
+
- Type (RemoteEvent, RemoteFunction, UnreliableRemoteEvent)
|
|
493
|
+
- Direction (Client→Server, Server→Client, Client↔Server)
|
|
494
|
+
- What it does and whether it carries critical or cosmetic data
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## Networking Step 2: Type Selection Audit
|
|
499
|
+
|
|
500
|
+
Verify each remote uses the correct type:
|
|
501
|
+
|
|
502
|
+
| Type | Best For | Notes |
|
|
503
|
+
|------|----------|-------|
|
|
504
|
+
| `RemoteEvent` | Fire-and-forget messages | Most common. No return value. |
|
|
505
|
+
| `RemoteFunction` | Request-response patterns | Blocking. Avoid in hot paths. Prefer RemoteEvent + callback pattern. |
|
|
506
|
+
| `UnreliableRemoteEvent` | Loss-tolerant data (effects, cosmetics, position updates) | Can drop packets under load. NEVER for currency, inventory, damage, game state. |
|
|
507
|
+
| `BindableEvent` | Intra-client or intra-server pub/sub | LocalScript→LocalScript or Script→Script only. Never cross Server↔Client boundary. |
|
|
508
|
+
| `BindableFunction` | Intra-client or intra-server request-response | Same boundary rule as BindableEvent. |
|
|
509
|
+
|
|
510
|
+
Flag any remote using the wrong type:
|
|
511
|
+
- `RemoteFunction` where `RemoteEvent` + response pattern would suffice
|
|
512
|
+
- `RemoteEvent` used as BindableEvent (firing a remote only to reach other scripts on the same client)
|
|
513
|
+
- `RemoteEvent` carrying critical data that should use reliable delivery
|
|
514
|
+
- `UnreliableRemoteEvent` carrying game-critical state
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## Networking Step 3: RemoteFunction Check
|
|
519
|
+
|
|
520
|
+
`RemoteFunction` is a blocking pattern - the caller yields until the receiver returns. This can cause lag if overused.
|
|
521
|
+
|
|
522
|
+
Verify:
|
|
523
|
+
- `:InvokeServer` is used sparingly (not per-frame or in tight loops)
|
|
524
|
+
- `:InvokeClient` is only used when the server genuinely needs an answer from a specific client (rare)
|
|
525
|
+
- Consider replacing with `RemoteEvent` + response event pattern for non-critical request-response flows
|
|
526
|
+
- `:InvokeClient` has a timeout safeguard (client may not respond)
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
## Networking Step 4: Bindable Audit
|
|
531
|
+
|
|
532
|
+
`BindableEvent` and `BindableFunction` are strictly intra-boundary (Server→Server or Client→Client).
|
|
533
|
+
|
|
534
|
+
Check:
|
|
535
|
+
- No `BindableEvent` / `BindableFunction` in ReplicatedStorage that is fired by server and listened to by client (use `RemoteEvent` instead - Bindables don't replicate)
|
|
536
|
+
- Client-side `BindableEvent`s are used for decoupling LocalScripts (cleaner than direct module references)
|
|
537
|
+
- Server-side `BindableEvent`s are used for service-to-service pub/sub within the same script context
|
|
538
|
+
- No orphaned or unused Bindable instances left in the hierarchy
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
## Networking Step 5: UnreliableRemoteEvent Audit
|
|
543
|
+
|
|
544
|
+
`UnreliableRemoteEvent` trades reliability for speed. Data may arrive out of order, arrive duplicates, or not arrive at all.
|
|
545
|
+
|
|
546
|
+
Verify:
|
|
547
|
+
- Used only for loss-tolerant data: cosmetic effects, non-critical position interpolation, particle triggers, sound events
|
|
548
|
+
- Never used for: currency transactions, inventory changes, damage application, game state transitions, unlock/upgrade actions
|
|
549
|
+
- Client-side has fallback logic (if a cosmetic event is dropped, the game still functions)
|
|
550
|
+
- Server-side does not rely on unreliable delivery for authoritative state
|
|
551
|
+
|
|
552
|
+
Recommended pattern - separate critical and cosmetic remotes:
|
|
553
|
+
```luau
|
|
554
|
+
-- Critical: reliable RemoteEvent
|
|
555
|
+
local PurchaseRequest = ReplicatedStorage.Remotes.PurchaseRequest -- RemoteEvent
|
|
556
|
+
-- Cosmetic: unreliable
|
|
557
|
+
local HitEffectSync = ReplicatedStorage.Remotes.HitEffectSync -- UnreliableRemoteEvent
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## Networking Step 6: Organization & Naming
|
|
563
|
+
|
|
564
|
+
Check:
|
|
565
|
+
- All remotes organized under a consistent folder structure (e.g., `ReplicatedStorage > Remotes > {Category}`)
|
|
566
|
+
- Naming convention is consistent: `PascalCase` for remote names, `VerbNoun` pattern (`PlayerRequestPurchase`, `ServerSyncInventory`)
|
|
567
|
+
- No unused / orphaned remotes in ReplicatedStorage
|
|
568
|
+
- RemoteFunction and its response type are clearly paired by naming
|
|
569
|
+
- UnreliableRemoteEvents are clearly distinguishable by name or naming suffix (e.g., `Sync_` prefix)
|
|
570
|
+
|
|
571
|
+
---
|
|
572
|
+
|
|
573
|
+
## Networking Step 7: Anti-Patterns
|
|
574
|
+
|
|
575
|
+
Flag these common networking mistakes:
|
|
576
|
+
|
|
577
|
+
- **Using RemoteFunction in a hot path** - blocks the caller. Prefer RemoteEvent.
|
|
578
|
+
- **Using RemoteEvent when BindableEvent would suffice** - a remote that only reaches scripts on the same player should be a BindableEvent, avoiding network overhead.
|
|
579
|
+
- **Using RemoteEvent when UnreliableRemoteEvent would be better** - for high-frequency cosmetic updates (position, VFX), unreliable saves bandwidth and CPU.
|
|
580
|
+
- **Using UnreliableRemoteEvent for critical state** - dropped packets will corrupt game state.
|
|
581
|
+
- **No remote surface map** - undocumented remotes make auditing and debugging harder.
|
|
582
|
+
- **Inconsistent naming** - `fireRemote`, `HandleRequest`, `r1` mixed in the same project.
|
|
583
|
+
- **Remotes scattered across ReplicatedStorage** - all remotes should live under a single `Remotes` folder.
|
|
584
|
+
- **BindableEvent in ReplicatedStorage used cross-boundary** - Bindables don't replicate; this will silently fail on the client.
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
# Data Persistence Lens
|
|
589
|
+
|
|
590
|
+
*Apply this lens when data persistence code changed: player data loading/saving, ProfileStore setup, data migration, or when auditing data integrity for production readiness.*
|
|
591
|
+
|
|
592
|
+
---
|
|
593
|
+
|
|
594
|
+
## Data Step 1: Data Library Detection
|
|
595
|
+
|
|
596
|
+
Search for how player data is stored:
|
|
597
|
+
|
|
598
|
+
- **ProfileStore found (recommended):** Continue to Step 2.
|
|
599
|
+
- **Raw DataStoreService** used directly: Flag as **High** priority. ProfileStore handles session locking, auto-save, BindToClose, retry logic, and schema reconciliation automatically. Recommend migration. Cross-reference: `roblox-data` §4.
|
|
600
|
+
- **No data persistence found:** Flag if the game needs it.
|
|
601
|
+
|
|
602
|
+
Verify ProfileStore installation:
|
|
603
|
+
- Wally dependency: `ProfileStore = "madstudioroblox/profileservice@1.4.0"`
|
|
604
|
+
- Or manual ModuleScript in ServerScriptService / ReplicatedStorage
|
|
605
|
+
- ProfileStore is required from the correct path
|
|
606
|
+
|
|
607
|
+
---
|
|
608
|
+
|
|
609
|
+
## Data Step 2: Profile Structure Review
|
|
610
|
+
|
|
611
|
+
Verify the `PROFILE_TEMPLATE` (or equivalent schema):
|
|
612
|
+
|
|
613
|
+
- Exists and is a table
|
|
614
|
+
- Contains a `DataVersion` field (number, incremented on schema changes)
|
|
615
|
+
- Uses nested structure for non-trivial games (separate `Currency`, `Progression`, `Inventory`, `Settings` sections)
|
|
616
|
+
- Every field has a sensible default value (ProfileStore's `:Reconcile()` fills missing fields from the template)
|
|
617
|
+
- No Instance references in the template (only JSON-compatible types: number, string, boolean, table)
|
|
618
|
+
- Cross-reference: `roblox-data` §6
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
## Data Step 3: Session Locking
|
|
623
|
+
|
|
624
|
+
Confirm session locking is properly configured:
|
|
625
|
+
|
|
626
|
+
- `profile:AddUserId(player.UserId)` is called after loading (GDPR compliance)
|
|
627
|
+
- `profile:ListenToRelease()` is connected, kicks the player if lock is stolen
|
|
628
|
+
- `"ForceLoad"` strategy is used (waits for lock, does not fail silently)
|
|
629
|
+
- `profile:Reconcile()` is called after loading to fill missing fields from template
|
|
630
|
+
- Cross-reference: `roblox-data` §5
|
|
631
|
+
|
|
632
|
+
---
|
|
633
|
+
|
|
634
|
+
## Data Step 4: Lifecycle Review
|
|
635
|
+
|
|
636
|
+
Verify the full player data lifecycle:
|
|
637
|
+
|
|
638
|
+
| Event | Must Do | Cross-Ref |
|
|
639
|
+
|-------|---------|-----------|
|
|
640
|
+
| `PlayerAdded` | Load profile via `LoadProfileAsync`, reconcile, store in cache | `roblox-data` §4 |
|
|
641
|
+
| Mid-game | Mutate `profile.Data.*` directly, leaderstats sync back | `roblox-data` §4 |
|
|
642
|
+
| `PlayerRemoving` | Sync leaderstats to profile, call `profile:Release()` | `roblox-data` §4 |
|
|
643
|
+
| `BindToClose` | If using raw DataStore: parallel saves with 30s timeout | `roblox-data` §10.3 |
|
|
644
|
+
| Auto-save | ProfileStore handles internally. If raw: 5-min interval, exponential retry | `roblox-data` §10.1 |
|
|
645
|
+
|
|
646
|
+
Flag missing lifecycle handlers, especially `PlayerRemoving` release and `BindToClose`.
|
|
647
|
+
|
|
648
|
+
---
|
|
649
|
+
|
|
650
|
+
## Data Step 5: Data Access Patterns
|
|
651
|
+
|
|
652
|
+
Check how data is read and written:
|
|
653
|
+
|
|
654
|
+
- Data is mutated via `profile.Data.fieldName`, not by calling DataStore methods directly
|
|
655
|
+
- Leaderstats `IntValue`/`StringValue` are synced back to `profile.Data` before `:Release()`
|
|
656
|
+
- Other scripts access player data via a getter module (`getProfile(player)`), not by directly importing ProfileStore
|
|
657
|
+
- Data writes happen immediately in memory; saves are handled by ProfileStore's auto-save interval
|
|
658
|
+
- Cross-reference: `roblox-data` §4
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
## Data Step 6: Migration Strategy
|
|
663
|
+
|
|
664
|
+
If the game has evolved its data schema:
|
|
665
|
+
|
|
666
|
+
- `DataVersion` field exists in the template
|
|
667
|
+
- Migration functions exist for each version bump (v1→v2, v2→v3, etc.)
|
|
668
|
+
- `DataMigrations.migrate(data)` is called after `LoadProfileAsync` and before `:Reconcile()`
|
|
669
|
+
- Migrations are sequential, pure functions (no yields, no side effects)
|
|
670
|
+
- Migration for old data that lacks `DataVersion` defaults to version 1
|
|
671
|
+
- Cross-reference: `roblox-data` §7
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
675
|
+
## Data Step 7: Anti-Patterns
|
|
676
|
+
|
|
677
|
+
Flag these common data persistence mistakes:
|
|
678
|
+
|
|
679
|
+
- **Raw DataStore for player state** instead of ProfileStore - missing session locking, auto-save, retry, BindToClose. Cross-ref: `roblox-data` §4.
|
|
680
|
+
- **Saving too frequently** - every coin pickup triggers a DataStore write. Rate limits will be hit. Cross-ref: `roblox-data` §11.
|
|
681
|
+
- **No `pcall` around DataStore calls** - unhandled errors crash the script. Cross-ref: `roblox-data` §11.
|
|
682
|
+
- **Storing Instance references in DataStore** - Instances are not serializable. Store IDs instead. Cross-ref: `roblox-data` §11.
|
|
683
|
+
- **No data validation before save** - NaN values or corrupt data can silently break the save. Cross-ref: `roblox-data` §10.5.
|
|
684
|
+
- **Missing `DataVersion`** - no way to detect or migrate old schema formats. Cross-ref: `roblox-data` §6.
|
|
685
|
+
- **Session locking bypassed** - using raw DataStore without manual lock implementation risks data loss. Cross-ref: `roblox-data` §5.
|
|
686
|
+
- **No `BindToClose` handler** - server shutdown loses unsaved player data. Cross-ref: `roblox-data` §10.3.
|
|
687
687
|
- **Sequential saves in `BindToClose`** - with many players, 30s timeout may be exceeded. Must use `task.spawn` for parallel saves. Cross-ref: `roblox-data` §12.
|