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,193 +1,193 @@
|
|
|
1
|
-
--[[
|
|
2
|
-
RemoteValidator — Production-ready server-side validation module
|
|
3
|
-
Source: roblox-opencode/skills/roblox-networking (authored for this harness)
|
|
4
|
-
|
|
5
|
-
Drop this into ServerScriptService/Modules/. Require it in every remote handler.
|
|
6
|
-
Covers: type checking, range validation, cooldowns, existence checks, distance,
|
|
7
|
-
authorization, and cleanup.
|
|
8
|
-
|
|
9
|
-
ADAPT THIS: Add game-specific checks (e.g. team validation, game state checks).
|
|
10
|
-
The core patterns (validateArgs, checkCooldown, withinRange) are universal.
|
|
11
|
-
]]
|
|
12
|
-
|
|
13
|
-
local Players = game:GetService("Players")
|
|
14
|
-
|
|
15
|
-
local RemoteValidator = {}
|
|
16
|
-
|
|
17
|
-
-------------------------------------------------------------------------------
|
|
18
|
-
-- Type Checking
|
|
19
|
-
-- Validates that arguments match expected types.
|
|
20
|
-
-------------------------------------------------------------------------------
|
|
21
|
-
|
|
22
|
-
type TypeSpec = string | (value: any) -> boolean
|
|
23
|
-
|
|
24
|
-
function RemoteValidator.checkType(value: any, expected: TypeSpec): boolean
|
|
25
|
-
if typeof(expected) == "function" then
|
|
26
|
-
return expected(value)
|
|
27
|
-
end
|
|
28
|
-
return typeof(value) == expected
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
function RemoteValidator.validateArgs(
|
|
32
|
-
args: { any },
|
|
33
|
-
schema: { { name: string, type: TypeSpec, optional: boolean? } }
|
|
34
|
-
): (boolean, string?)
|
|
35
|
-
for i, spec in schema do
|
|
36
|
-
local value = args[i]
|
|
37
|
-
|
|
38
|
-
if value == nil then
|
|
39
|
-
if not spec.optional then
|
|
40
|
-
return false, `Missing required argument: {spec.name}`
|
|
41
|
-
end
|
|
42
|
-
continue
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
if not RemoteValidator.checkType(value, spec.type) then
|
|
46
|
-
return false, `Invalid type for {spec.name}: expected {tostring(spec.type)}, got {typeof(value)}`
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
-- Reject extra arguments not declared in schema
|
|
51
|
-
if #args > #schema then
|
|
52
|
-
return false, `Too many arguments: expected {#schema}, got {#args}`
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
return true, nil
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
-------------------------------------------------------------------------------
|
|
59
|
-
-- Range Checking
|
|
60
|
-
-- Validates that numeric values fall within acceptable bounds.
|
|
61
|
-
-------------------------------------------------------------------------------
|
|
62
|
-
|
|
63
|
-
function RemoteValidator.checkRange(value: number, min: number, max: number): boolean
|
|
64
|
-
return typeof(value) == "number"
|
|
65
|
-
and value == value -- NaN check
|
|
66
|
-
and value >= min
|
|
67
|
-
and value <= max
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
function RemoteValidator.checkIntegerRange(value: number, min: number, max: number): boolean
|
|
71
|
-
return RemoteValidator.checkRange(value, min, max)
|
|
72
|
-
and math.floor(value) == value
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
-------------------------------------------------------------------------------
|
|
76
|
-
-- Cooldown Tracking
|
|
77
|
-
-- Per-player, per-action cooldown enforcement.
|
|
78
|
-
-------------------------------------------------------------------------------
|
|
79
|
-
|
|
80
|
-
local cooldowns: { [Player]: { [string]: number } } = {}
|
|
81
|
-
|
|
82
|
-
function RemoteValidator.checkCooldown(player: Player, action: string, cooldownSeconds: number): boolean
|
|
83
|
-
local now = os.clock()
|
|
84
|
-
local playerCooldowns = cooldowns[player]
|
|
85
|
-
|
|
86
|
-
if not playerCooldowns then
|
|
87
|
-
playerCooldowns = {}
|
|
88
|
-
cooldowns[player] = playerCooldowns
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
local lastUsed = playerCooldowns[action]
|
|
92
|
-
if lastUsed and (now - lastUsed) < cooldownSeconds then
|
|
93
|
-
return false
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
playerCooldowns[action] = now
|
|
97
|
-
return true
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
function RemoteValidator.clearPlayerCooldowns(player: Player)
|
|
101
|
-
cooldowns[player] = nil
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
-------------------------------------------------------------------------------
|
|
105
|
-
-- Existence Checks
|
|
106
|
-
-- Validates that targets, objects, and instances actually exist.
|
|
107
|
-
-------------------------------------------------------------------------------
|
|
108
|
-
|
|
109
|
-
function RemoteValidator.playerExists(playerName: string): Player?
|
|
110
|
-
return Players:FindFirstChild(playerName) :: Player?
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
function RemoteValidator.characterAlive(player: Player): boolean
|
|
114
|
-
local character = player.Character
|
|
115
|
-
if not character then
|
|
116
|
-
return false
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
local humanoid = character:FindFirstChildOfClass("Humanoid")
|
|
120
|
-
if not humanoid then
|
|
121
|
-
return false
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
return humanoid.Health > 0
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
function RemoteValidator.instanceExists(parent: Instance, name: string, className: string?): Instance?
|
|
128
|
-
local child = parent:FindFirstChild(name)
|
|
129
|
-
if not child then
|
|
130
|
-
return nil
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
if className and not child:IsA(className) then
|
|
134
|
-
return nil
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
return child
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
-------------------------------------------------------------------------------
|
|
141
|
-
-- Authorization
|
|
142
|
-
-- Checks if a player is allowed to perform an action.
|
|
143
|
-
-------------------------------------------------------------------------------
|
|
144
|
-
|
|
145
|
-
function RemoteValidator.playerOwnsItem(player: Player, itemId: string, inventoryFolder: Folder?): boolean
|
|
146
|
-
local folder = inventoryFolder or player:FindFirstChild("Inventory") :: Folder?
|
|
147
|
-
if not folder then
|
|
148
|
-
return false
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
return folder:FindFirstChild(itemId) ~= nil
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
function RemoteValidator.playerHasAttribute(player: Player, attribute: string, expectedValue: any?): boolean
|
|
155
|
-
local value = player:GetAttribute(attribute)
|
|
156
|
-
if expectedValue ~= nil then
|
|
157
|
-
return value == expectedValue
|
|
158
|
-
end
|
|
159
|
-
return value ~= nil
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
-------------------------------------------------------------------------------
|
|
163
|
-
-- Distance Check
|
|
164
|
-
-- Validates that two positions are within an acceptable range.
|
|
165
|
-
-------------------------------------------------------------------------------
|
|
166
|
-
|
|
167
|
-
function RemoteValidator.withinRange(posA: Vector3, posB: Vector3, maxDistance: number): boolean
|
|
168
|
-
return (posA - posB).Magnitude <= maxDistance
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
function RemoteValidator.playerWithinRange(player: Player, targetPos: Vector3, maxDistance: number): boolean
|
|
172
|
-
local character = player.Character
|
|
173
|
-
if not character then
|
|
174
|
-
return false
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
local root = character:FindFirstChild("HumanoidRootPart")
|
|
178
|
-
if not root then
|
|
179
|
-
return false
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
return RemoteValidator.withinRange(root.Position, targetPos, maxDistance)
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
-------------------------------------------------------------------------------
|
|
186
|
-
-- Cleanup — prevent memory leaks from departed players
|
|
187
|
-
-------------------------------------------------------------------------------
|
|
188
|
-
|
|
189
|
-
Players.PlayerRemoving:Connect(function(player)
|
|
190
|
-
RemoteValidator.clearPlayerCooldowns(player)
|
|
191
|
-
end)
|
|
192
|
-
|
|
193
|
-
return RemoteValidator
|
|
1
|
+
--[[
|
|
2
|
+
RemoteValidator — Production-ready server-side validation module
|
|
3
|
+
Source: roblox-opencode/skills/roblox-networking (authored for this harness)
|
|
4
|
+
|
|
5
|
+
Drop this into ServerScriptService/Modules/. Require it in every remote handler.
|
|
6
|
+
Covers: type checking, range validation, cooldowns, existence checks, distance,
|
|
7
|
+
authorization, and cleanup.
|
|
8
|
+
|
|
9
|
+
ADAPT THIS: Add game-specific checks (e.g. team validation, game state checks).
|
|
10
|
+
The core patterns (validateArgs, checkCooldown, withinRange) are universal.
|
|
11
|
+
]]
|
|
12
|
+
|
|
13
|
+
local Players = game:GetService("Players")
|
|
14
|
+
|
|
15
|
+
local RemoteValidator = {}
|
|
16
|
+
|
|
17
|
+
-------------------------------------------------------------------------------
|
|
18
|
+
-- Type Checking
|
|
19
|
+
-- Validates that arguments match expected types.
|
|
20
|
+
-------------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
type TypeSpec = string | (value: any) -> boolean
|
|
23
|
+
|
|
24
|
+
function RemoteValidator.checkType(value: any, expected: TypeSpec): boolean
|
|
25
|
+
if typeof(expected) == "function" then
|
|
26
|
+
return expected(value)
|
|
27
|
+
end
|
|
28
|
+
return typeof(value) == expected
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
function RemoteValidator.validateArgs(
|
|
32
|
+
args: { any },
|
|
33
|
+
schema: { { name: string, type: TypeSpec, optional: boolean? } }
|
|
34
|
+
): (boolean, string?)
|
|
35
|
+
for i, spec in schema do
|
|
36
|
+
local value = args[i]
|
|
37
|
+
|
|
38
|
+
if value == nil then
|
|
39
|
+
if not spec.optional then
|
|
40
|
+
return false, `Missing required argument: {spec.name}`
|
|
41
|
+
end
|
|
42
|
+
continue
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if not RemoteValidator.checkType(value, spec.type) then
|
|
46
|
+
return false, `Invalid type for {spec.name}: expected {tostring(spec.type)}, got {typeof(value)}`
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
-- Reject extra arguments not declared in schema
|
|
51
|
+
if #args > #schema then
|
|
52
|
+
return false, `Too many arguments: expected {#schema}, got {#args}`
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
return true, nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
-------------------------------------------------------------------------------
|
|
59
|
+
-- Range Checking
|
|
60
|
+
-- Validates that numeric values fall within acceptable bounds.
|
|
61
|
+
-------------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
function RemoteValidator.checkRange(value: number, min: number, max: number): boolean
|
|
64
|
+
return typeof(value) == "number"
|
|
65
|
+
and value == value -- NaN check
|
|
66
|
+
and value >= min
|
|
67
|
+
and value <= max
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
function RemoteValidator.checkIntegerRange(value: number, min: number, max: number): boolean
|
|
71
|
+
return RemoteValidator.checkRange(value, min, max)
|
|
72
|
+
and math.floor(value) == value
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
-------------------------------------------------------------------------------
|
|
76
|
+
-- Cooldown Tracking
|
|
77
|
+
-- Per-player, per-action cooldown enforcement.
|
|
78
|
+
-------------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
local cooldowns: { [Player]: { [string]: number } } = {}
|
|
81
|
+
|
|
82
|
+
function RemoteValidator.checkCooldown(player: Player, action: string, cooldownSeconds: number): boolean
|
|
83
|
+
local now = os.clock()
|
|
84
|
+
local playerCooldowns = cooldowns[player]
|
|
85
|
+
|
|
86
|
+
if not playerCooldowns then
|
|
87
|
+
playerCooldowns = {}
|
|
88
|
+
cooldowns[player] = playerCooldowns
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
local lastUsed = playerCooldowns[action]
|
|
92
|
+
if lastUsed and (now - lastUsed) < cooldownSeconds then
|
|
93
|
+
return false
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
playerCooldowns[action] = now
|
|
97
|
+
return true
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
function RemoteValidator.clearPlayerCooldowns(player: Player)
|
|
101
|
+
cooldowns[player] = nil
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
-------------------------------------------------------------------------------
|
|
105
|
+
-- Existence Checks
|
|
106
|
+
-- Validates that targets, objects, and instances actually exist.
|
|
107
|
+
-------------------------------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
function RemoteValidator.playerExists(playerName: string): Player?
|
|
110
|
+
return Players:FindFirstChild(playerName) :: Player?
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
function RemoteValidator.characterAlive(player: Player): boolean
|
|
114
|
+
local character = player.Character
|
|
115
|
+
if not character then
|
|
116
|
+
return false
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
local humanoid = character:FindFirstChildOfClass("Humanoid")
|
|
120
|
+
if not humanoid then
|
|
121
|
+
return false
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
return humanoid.Health > 0
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
function RemoteValidator.instanceExists(parent: Instance, name: string, className: string?): Instance?
|
|
128
|
+
local child = parent:FindFirstChild(name)
|
|
129
|
+
if not child then
|
|
130
|
+
return nil
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
if className and not child:IsA(className) then
|
|
134
|
+
return nil
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
return child
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
-------------------------------------------------------------------------------
|
|
141
|
+
-- Authorization
|
|
142
|
+
-- Checks if a player is allowed to perform an action.
|
|
143
|
+
-------------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
function RemoteValidator.playerOwnsItem(player: Player, itemId: string, inventoryFolder: Folder?): boolean
|
|
146
|
+
local folder = inventoryFolder or player:FindFirstChild("Inventory") :: Folder?
|
|
147
|
+
if not folder then
|
|
148
|
+
return false
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
return folder:FindFirstChild(itemId) ~= nil
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
function RemoteValidator.playerHasAttribute(player: Player, attribute: string, expectedValue: any?): boolean
|
|
155
|
+
local value = player:GetAttribute(attribute)
|
|
156
|
+
if expectedValue ~= nil then
|
|
157
|
+
return value == expectedValue
|
|
158
|
+
end
|
|
159
|
+
return value ~= nil
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
-------------------------------------------------------------------------------
|
|
163
|
+
-- Distance Check
|
|
164
|
+
-- Validates that two positions are within an acceptable range.
|
|
165
|
+
-------------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
function RemoteValidator.withinRange(posA: Vector3, posB: Vector3, maxDistance: number): boolean
|
|
168
|
+
return (posA - posB).Magnitude <= maxDistance
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
function RemoteValidator.playerWithinRange(player: Player, targetPos: Vector3, maxDistance: number): boolean
|
|
172
|
+
local character = player.Character
|
|
173
|
+
if not character then
|
|
174
|
+
return false
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
local root = character:FindFirstChild("HumanoidRootPart")
|
|
178
|
+
if not root then
|
|
179
|
+
return false
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
return RemoteValidator.withinRange(root.Position, targetPos, maxDistance)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
-------------------------------------------------------------------------------
|
|
186
|
+
-- Cleanup — prevent memory leaks from departed players
|
|
187
|
+
-------------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
Players.PlayerRemoving:Connect(function(player)
|
|
190
|
+
RemoteValidator.clearPlayerCooldowns(player)
|
|
191
|
+
end)
|
|
192
|
+
|
|
193
|
+
return RemoteValidator
|
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: roblox-publish-checklist
|
|
3
|
-
description: "Pre-publish verification gauntlet for Roblox games"
|
|
4
|
-
tags: [roblox, publish, checklist, verification]
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# /publish-checklist - Pre-Publish Verification
|
|
8
|
-
|
|
9
|
-
You are verifying a Roblox game is ready to publish. Work through every category below. For each item, check it and note PASS/FAIL/SKIP with a brief explanation.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## 1. Data & Persistence
|
|
14
|
-
|
|
15
|
-
- [ ] **DataStore save/load tested** - Player data saves on leave and loads on rejoin correctly
|
|
16
|
-
- [ ] **Session locking verified** - Using ProfileStore or equivalent; no concurrent data corruption
|
|
17
|
-
- [ ] **BindToClose implemented** - Server saves data before shutdown (game close, server hop)
|
|
18
|
-
- [ ] **Data migration plan** - If updating an existing game, schema migration handles old data formats
|
|
19
|
-
- [ ] **Edge case: disconnect during save** - Data not lost if player disconnects mid-save
|
|
20
|
-
- [ ] **Edge case: multiple DataStore calls** - No race conditions from parallel saves
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## 2. Security
|
|
25
|
-
|
|
26
|
-
- [ ] **All remotes validated server-side** - Every RemoteEvent/RemoteFunction checks argument types, ranges, and ownership
|
|
27
|
-
- [ ] **No sensitive data exposed** - ReplicatedStorage, StarterPlayer have no server-only logic or secrets
|
|
28
|
-
- [ ] **Rate limiting on all remotes** - Per-player throttling prevents flooding/exploitation
|
|
29
|
-
- [ ] **No client-trusted game logic** - Currency, inventory, damage, positions calculated server-side only
|
|
30
|
-
- [ ] **ProcessReceipt handled correctly** - Grant item THEN return PurchaseGranted; NotProcessedYet on failure
|
|
31
|
-
- [ ] **Anti-cheat basics** - Speed checks, teleport detection, inventory validation
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## 3. Performance
|
|
36
|
-
|
|
37
|
-
- [ ] **Mobile tested** - Game runs on mobile devices without crashes or severe lag
|
|
38
|
-
- [ ] **Part count within limits** - Workspace part count reasonable for target devices
|
|
39
|
-
- [ ] **No memory leaks** - Events disconnected on cleanup, no orphaned instances accumulating
|
|
40
|
-
- [ ] **MicroProfiler reviewed** - No scripts consistently over budget (>1ms per frame)
|
|
41
|
-
- [ ] **StreamingEnabled considered** - If large map, StreamingEnabled is enabled and tested
|
|
42
|
-
- [ ] **Signal cleanup** - No undisconnected BindableEvent/RemoteEvent connections leaking
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## 4. Monetization
|
|
47
|
-
|
|
48
|
-
- [ ] **GamePasses work correctly** - Purchasing grants the correct benefit, idempotent on rejoin
|
|
49
|
-
- [ ] **DevProducts grant properly** - Consumables delivered, ProcessReceipt handles edge cases
|
|
50
|
-
- [ ] **Premium benefits functional** - Premium payout optimized, exclusive perks work
|
|
51
|
-
- [ ] **Prices reviewed** - Competitive with similar games, clear value proposition
|
|
52
|
-
- [ ] **No pay-to-win concerns** - Free players have reasonable experience
|
|
53
|
-
- [ ] **Premium Payouts enabled** - Game is eligible and configured for Premium Payouts
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
## 5. Mobile Compatibility
|
|
58
|
-
|
|
59
|
-
- [ ] **Touch controls work** - All interactions accessible via tap
|
|
60
|
-
- [ ] **UI scales properly** - Using Scale not Offset for UI elements; tested on small screens
|
|
61
|
-
- [ ] **ContextActionService for input** - Game actions bound properly for mobile
|
|
62
|
-
- [ ] **Small screen tested** - UI doesn't overlap, buttons are tappable, text is readable
|
|
63
|
-
- [ ] **Landscape and portrait** - Orientation handled if applicable
|
|
64
|
-
- [ ] **Performance on low-end devices** - Tested on minimum spec mobile device
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## 6. Gameplay
|
|
69
|
-
|
|
70
|
-
- [ ] **Core loop tested end-to-end** - Play for 10+ minutes, full loop works
|
|
71
|
-
- [ ] **Edge cases handled:**
|
|
72
|
-
- [ ] Disconnect during trade/transaction
|
|
73
|
-
- [ ] Death during cutscene
|
|
74
|
-
- [ ] Player leaves during multiplayer event
|
|
75
|
-
- [ ] Rapid button pressing
|
|
76
|
-
- [ ] Backfill/rejoin during active game
|
|
77
|
-
- [ ] **Tutorial/FTUE works** - New player can learn the game without confusion
|
|
78
|
-
- [ ] **Difficulty curve** - Early game engaging, progression feels rewarding
|
|
79
|
-
- [ ] **Fun check** - Core loop is actually enjoyable to play repeatedly
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
## 7. Metadata
|
|
84
|
-
|
|
85
|
-
- [ ] **Game icon set** - 512x512, clear and representative
|
|
86
|
-
- [ ] **Thumbnails uploaded** - At least 3 images showing gameplay
|
|
87
|
-
- [ ] **Description written** - Clear, compelling, includes key features
|
|
88
|
-
- [ ] **Genre selected** - Correct category for discovery
|
|
89
|
-
- [ ] **Max players configured** - Appropriate for game type
|
|
90
|
-
- [ ] **Game badges** - Achievement badges set up for milestones
|
|
91
|
-
- [ ] **Game rating** - Age-appropriate settings configured
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## 8. Social
|
|
96
|
-
|
|
97
|
-
- [ ] **Private servers configured** - Available and priced if applicable
|
|
98
|
-
- [ ] **Social features tested** - Party system, chat, friending all work
|
|
99
|
-
- [ ] **Report/block doesn't break game** - Reporting a player doesn't crash or corrupt game state
|
|
100
|
-
- [ ] **Server browser** - Game appears in search with correct tags
|
|
101
|
-
- [ ] **Team play** - If multiplayer, team assignment and switching work
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## 9. Analytics
|
|
106
|
-
|
|
107
|
-
- [ ] **Key events instrumented:**
|
|
108
|
-
- [ ] Player joins / leaves
|
|
109
|
-
- [ ] Purchases (GamePass and DevProduct)
|
|
110
|
-
- [ ] Level/zone completions
|
|
111
|
-
- [ ] Session length tracking
|
|
112
|
-
- [ ] Error/crash reporting
|
|
113
|
-
- [ ] **Basic funnel tracking** - New player → tutorial complete → first purchase → retention
|
|
114
|
-
- [ ] **Dashboard configured** - Analytics visible in Roblox Creator Dashboard
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
## Summary
|
|
119
|
-
|
|
120
|
-
After checking all items, output:
|
|
121
|
-
|
|
122
|
-
1. **Overall status:** READY / NOT READY
|
|
123
|
-
2. **Critical blockers** (must fix before publish)
|
|
124
|
-
3. **Warnings** (should fix, not blocking)
|
|
125
|
-
4. **Passed items** - Count and percentage
|
|
126
|
-
5. **Failed items** - List each with the fix needed
|
|
127
|
-
|
|
1
|
+
---
|
|
2
|
+
name: roblox-publish-checklist
|
|
3
|
+
description: "Pre-publish verification gauntlet for Roblox games"
|
|
4
|
+
tags: [roblox, publish, checklist, verification]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /publish-checklist - Pre-Publish Verification
|
|
8
|
+
|
|
9
|
+
You are verifying a Roblox game is ready to publish. Work through every category below. For each item, check it and note PASS/FAIL/SKIP with a brief explanation.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Data & Persistence
|
|
14
|
+
|
|
15
|
+
- [ ] **DataStore save/load tested** - Player data saves on leave and loads on rejoin correctly
|
|
16
|
+
- [ ] **Session locking verified** - Using ProfileStore or equivalent; no concurrent data corruption
|
|
17
|
+
- [ ] **BindToClose implemented** - Server saves data before shutdown (game close, server hop)
|
|
18
|
+
- [ ] **Data migration plan** - If updating an existing game, schema migration handles old data formats
|
|
19
|
+
- [ ] **Edge case: disconnect during save** - Data not lost if player disconnects mid-save
|
|
20
|
+
- [ ] **Edge case: multiple DataStore calls** - No race conditions from parallel saves
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 2. Security
|
|
25
|
+
|
|
26
|
+
- [ ] **All remotes validated server-side** - Every RemoteEvent/RemoteFunction checks argument types, ranges, and ownership
|
|
27
|
+
- [ ] **No sensitive data exposed** - ReplicatedStorage, StarterPlayer have no server-only logic or secrets
|
|
28
|
+
- [ ] **Rate limiting on all remotes** - Per-player throttling prevents flooding/exploitation
|
|
29
|
+
- [ ] **No client-trusted game logic** - Currency, inventory, damage, positions calculated server-side only
|
|
30
|
+
- [ ] **ProcessReceipt handled correctly** - Grant item THEN return PurchaseGranted; NotProcessedYet on failure
|
|
31
|
+
- [ ] **Anti-cheat basics** - Speed checks, teleport detection, inventory validation
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 3. Performance
|
|
36
|
+
|
|
37
|
+
- [ ] **Mobile tested** - Game runs on mobile devices without crashes or severe lag
|
|
38
|
+
- [ ] **Part count within limits** - Workspace part count reasonable for target devices
|
|
39
|
+
- [ ] **No memory leaks** - Events disconnected on cleanup, no orphaned instances accumulating
|
|
40
|
+
- [ ] **MicroProfiler reviewed** - No scripts consistently over budget (>1ms per frame)
|
|
41
|
+
- [ ] **StreamingEnabled considered** - If large map, StreamingEnabled is enabled and tested
|
|
42
|
+
- [ ] **Signal cleanup** - No undisconnected BindableEvent/RemoteEvent connections leaking
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 4. Monetization
|
|
47
|
+
|
|
48
|
+
- [ ] **GamePasses work correctly** - Purchasing grants the correct benefit, idempotent on rejoin
|
|
49
|
+
- [ ] **DevProducts grant properly** - Consumables delivered, ProcessReceipt handles edge cases
|
|
50
|
+
- [ ] **Premium benefits functional** - Premium payout optimized, exclusive perks work
|
|
51
|
+
- [ ] **Prices reviewed** - Competitive with similar games, clear value proposition
|
|
52
|
+
- [ ] **No pay-to-win concerns** - Free players have reasonable experience
|
|
53
|
+
- [ ] **Premium Payouts enabled** - Game is eligible and configured for Premium Payouts
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 5. Mobile Compatibility
|
|
58
|
+
|
|
59
|
+
- [ ] **Touch controls work** - All interactions accessible via tap
|
|
60
|
+
- [ ] **UI scales properly** - Using Scale not Offset for UI elements; tested on small screens
|
|
61
|
+
- [ ] **ContextActionService for input** - Game actions bound properly for mobile
|
|
62
|
+
- [ ] **Small screen tested** - UI doesn't overlap, buttons are tappable, text is readable
|
|
63
|
+
- [ ] **Landscape and portrait** - Orientation handled if applicable
|
|
64
|
+
- [ ] **Performance on low-end devices** - Tested on minimum spec mobile device
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 6. Gameplay
|
|
69
|
+
|
|
70
|
+
- [ ] **Core loop tested end-to-end** - Play for 10+ minutes, full loop works
|
|
71
|
+
- [ ] **Edge cases handled:**
|
|
72
|
+
- [ ] Disconnect during trade/transaction
|
|
73
|
+
- [ ] Death during cutscene
|
|
74
|
+
- [ ] Player leaves during multiplayer event
|
|
75
|
+
- [ ] Rapid button pressing
|
|
76
|
+
- [ ] Backfill/rejoin during active game
|
|
77
|
+
- [ ] **Tutorial/FTUE works** - New player can learn the game without confusion
|
|
78
|
+
- [ ] **Difficulty curve** - Early game engaging, progression feels rewarding
|
|
79
|
+
- [ ] **Fun check** - Core loop is actually enjoyable to play repeatedly
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 7. Metadata
|
|
84
|
+
|
|
85
|
+
- [ ] **Game icon set** - 512x512, clear and representative
|
|
86
|
+
- [ ] **Thumbnails uploaded** - At least 3 images showing gameplay
|
|
87
|
+
- [ ] **Description written** - Clear, compelling, includes key features
|
|
88
|
+
- [ ] **Genre selected** - Correct category for discovery
|
|
89
|
+
- [ ] **Max players configured** - Appropriate for game type
|
|
90
|
+
- [ ] **Game badges** - Achievement badges set up for milestones
|
|
91
|
+
- [ ] **Game rating** - Age-appropriate settings configured
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 8. Social
|
|
96
|
+
|
|
97
|
+
- [ ] **Private servers configured** - Available and priced if applicable
|
|
98
|
+
- [ ] **Social features tested** - Party system, chat, friending all work
|
|
99
|
+
- [ ] **Report/block doesn't break game** - Reporting a player doesn't crash or corrupt game state
|
|
100
|
+
- [ ] **Server browser** - Game appears in search with correct tags
|
|
101
|
+
- [ ] **Team play** - If multiplayer, team assignment and switching work
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 9. Analytics
|
|
106
|
+
|
|
107
|
+
- [ ] **Key events instrumented:**
|
|
108
|
+
- [ ] Player joins / leaves
|
|
109
|
+
- [ ] Purchases (GamePass and DevProduct)
|
|
110
|
+
- [ ] Level/zone completions
|
|
111
|
+
- [ ] Session length tracking
|
|
112
|
+
- [ ] Error/crash reporting
|
|
113
|
+
- [ ] **Basic funnel tracking** - New player → tutorial complete → first purchase → retention
|
|
114
|
+
- [ ] **Dashboard configured** - Analytics visible in Roblox Creator Dashboard
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Summary
|
|
119
|
+
|
|
120
|
+
After checking all items, output:
|
|
121
|
+
|
|
122
|
+
1. **Overall status:** READY / NOT READY
|
|
123
|
+
2. **Critical blockers** (must fix before publish)
|
|
124
|
+
3. **Warnings** (should fix, not blocking)
|
|
125
|
+
4. **Passed items** - Count and percentage
|
|
126
|
+
5. **Failed items** - List each with the fix needed
|
|
127
|
+
|
|
128
128
|
If NOT READY, provide specific fixes for every failed item before the user publishes.
|