- GameConfig: RX7AllowedUserIds allowlist + isRX7Allowed/starterCarFor helpers - ProfileTemplate: no hard-coded starter; granted per-player by UserId - DataManager: grants RX-7 to allowlisted players, public car to everyone else - Economy.buyCar + CarManager.spawn: server-side refuse RX-7 for non-allowlisted - Cars: RX-7 flagged Restricted; client dealership hides it from non-owners - Gates: stylua clean, selene 0/0, rojo builds
This commit is contained in:
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 4.3 MiB |
@@ -231,6 +231,11 @@ local function rebuildGarage()
|
|||||||
local owned = ownedIds[car.Id]
|
local owned = ownedIds[car.Id]
|
||||||
local equipped = latest.EquippedCarId == car.Id
|
local equipped = latest.EquippedCarId == car.Id
|
||||||
|
|
||||||
|
-- Hide restricted (allowlist-only) cars from anyone who doesn't own them.
|
||||||
|
if car.Restricted and not owned then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
local row = make("Frame", {
|
local row = make("Frame", {
|
||||||
Size = UDim2.new(1, 0, 0, 72),
|
Size = UDim2.new(1, 0, 0, 72),
|
||||||
BackgroundColor3 = Color3.fromRGB(26, 26, 38),
|
BackgroundColor3 = Color3.fromRGB(26, 26, 38),
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
|||||||
|
|
||||||
local Shared = ReplicatedStorage.Shared
|
local Shared = ReplicatedStorage.Shared
|
||||||
local Cars = require(Shared.Cars)
|
local Cars = require(Shared.Cars)
|
||||||
|
local GameConfig = require(Shared.GameConfig)
|
||||||
local ProfileTemplate = require(Shared.ProfileTemplate)
|
local ProfileTemplate = require(Shared.ProfileTemplate)
|
||||||
local DataManager = require(script.Parent.DataManager)
|
local DataManager = require(script.Parent.DataManager)
|
||||||
|
|
||||||
@@ -125,6 +126,11 @@ function CarManager.spawn(player: Player, carId: string?)
|
|||||||
if not car then
|
if not car then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
-- Defense in depth: never spawn the restricted RX-7 for a non-allowlisted user,
|
||||||
|
-- even if their save somehow contained it.
|
||||||
|
if car.Id == GameConfig.RestrictedStarterCarId and not GameConfig.isRX7Allowed(player.UserId) then
|
||||||
|
return
|
||||||
|
end
|
||||||
-- Only spawn cars the player owns (anti-cheat: no spawning unowned exotics).
|
-- Only spawn cars the player owns (anti-cheat: no spawning unowned exotics).
|
||||||
local owned
|
local owned
|
||||||
for _, o in ipairs(data.OwnedCars) do
|
for _, o in ipairs(data.OwnedCars) do
|
||||||
|
|||||||
@@ -71,6 +71,22 @@ local function onPlayerAdded(player: Player)
|
|||||||
profile:AddUserId(player.UserId) -- GDPR-friendly association
|
profile:AddUserId(player.UserId) -- GDPR-friendly association
|
||||||
profile:Reconcile() -- fill missing template keys
|
profile:Reconcile() -- fill missing template keys
|
||||||
|
|
||||||
|
-- Grant the correct starter car for THIS player (RX-7 only for allowlisted
|
||||||
|
-- UserIds; everyone else gets the public starter). Idempotent: only grants
|
||||||
|
-- if the player has no cars yet (i.e. genuinely first join).
|
||||||
|
local data = profile.Data
|
||||||
|
if #data.OwnedCars == 0 then
|
||||||
|
local starterId = GameConfig.starterCarFor(player.UserId)
|
||||||
|
table.insert(data.OwnedCars, {
|
||||||
|
Id = starterId,
|
||||||
|
Upgrades = { Engine = 0, Tires = 0, Nitro = 0 },
|
||||||
|
})
|
||||||
|
data.EquippedCarId = starterId
|
||||||
|
elseif data.EquippedCarId == "" then
|
||||||
|
-- Safety: if somehow they own cars but have nothing equipped, equip the first.
|
||||||
|
data.EquippedCarId = data.OwnedCars[1].Id
|
||||||
|
end
|
||||||
|
|
||||||
profile.OnSessionEnd:Connect(function()
|
profile.OnSessionEnd:Connect(function()
|
||||||
-- The session was lost (another server stole it, or we released it).
|
-- The session was lost (another server stole it, or we released it).
|
||||||
profiles[player] = nil
|
profiles[player] = nil
|
||||||
|
|||||||
@@ -88,6 +88,10 @@ function Economy.buyCar(player: Player, carId: string): Result
|
|||||||
if not car then
|
if not car then
|
||||||
return { ok = false, reason = "Unknown car" }
|
return { ok = false, reason = "Unknown car" }
|
||||||
end
|
end
|
||||||
|
-- Restricted demo car: the RX-7 can only ever be owned by allowlisted UserIds.
|
||||||
|
if car.Id == GameConfig.RestrictedStarterCarId and not GameConfig.isRX7Allowed(player.UserId) then
|
||||||
|
return { ok = false, reason = "This car is not available" }
|
||||||
|
end
|
||||||
if ownsCar(data, carId) then
|
if ownsCar(data, carId) then
|
||||||
return { ok = false, reason = "You already own this car" }
|
return { ok = false, reason = "You already own this car" }
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export type Car = {
|
|||||||
Color: { number }, -- {r,g,b} 0-1 for the placeholder body until real mesh exists
|
Color: { number }, -- {r,g,b} 0-1 for the placeholder body until real mesh exists
|
||||||
Description: string,
|
Description: string,
|
||||||
Starter: boolean?, -- true = granted free on first join
|
Starter: boolean?, -- true = granted free on first join
|
||||||
|
Restricted: boolean?, -- true = allowlist-only; hidden from dealership for others
|
||||||
}
|
}
|
||||||
|
|
||||||
local Cars = {}
|
local Cars = {}
|
||||||
@@ -44,6 +45,7 @@ Cars.List = {
|
|||||||
Color = { 0.85, 0.15, 0.12 }, -- classic RX-7 red until the real mesh is imported
|
Color = { 0.85, 0.15, 0.12 }, -- classic RX-7 red until the real mesh is imported
|
||||||
Description = "The one that started it all. A rotary-hearted FD RX-7 — light, balanced, and iconic. Your daily driver and first weapon on the expressway.",
|
Description = "The one that started it all. A rotary-hearted FD RX-7 — light, balanced, and iconic. Your daily driver and first weapon on the expressway.",
|
||||||
Starter = true,
|
Starter = true,
|
||||||
|
Restricted = true, -- demo-locked to the owner + son during testing
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id = "hatch_junk",
|
Id = "hatch_junk",
|
||||||
|
|||||||
@@ -10,7 +10,33 @@ local GameConfig = {}
|
|||||||
-- Economy -------------------------------------------------------------------
|
-- Economy -------------------------------------------------------------------
|
||||||
GameConfig.StartingCash = 1500 -- enough to feel, not enough to skip the RX-7
|
GameConfig.StartingCash = 1500 -- enough to feel, not enough to skip the RX-7
|
||||||
GameConfig.StartingRep = 0
|
GameConfig.StartingRep = 0
|
||||||
GameConfig.StartingCarId = "rx7_fc" -- the player's real 1993 Mazda RX-7
|
|
||||||
|
-- Starter cars ---------------------------------------------------------------
|
||||||
|
-- The RX-7 is a RESTRICTED demo car for the owner + son during testing. Only
|
||||||
|
-- UserIds in the allowlist below ever receive / can buy / can spawn it.
|
||||||
|
-- Everyone else starts in the public starter car instead.
|
||||||
|
GameConfig.PublicStarterCarId = "hatch_junk" -- default car for non-allowlisted players
|
||||||
|
GameConfig.RestrictedStarterCarId = "rx7_fc" -- the real 1993 Mazda RX-7 (demo-locked)
|
||||||
|
|
||||||
|
-- >>> FILL THESE IN: Roblox UserIds allowed to own the RX-7 (numbers, not names).
|
||||||
|
-- Find yours at roblox.com/users/<name>/profile — the number in the URL.
|
||||||
|
GameConfig.RX7AllowedUserIds = {
|
||||||
|
-- [1234567] = true, -- R3AL.CH1CK3N (replace with your UserId)
|
||||||
|
-- [7654321] = true, -- son (replace with his UserId)
|
||||||
|
}
|
||||||
|
|
||||||
|
--- True if this player is allowed to own the restricted RX-7.
|
||||||
|
function GameConfig.isRX7Allowed(userId: number): boolean
|
||||||
|
return GameConfig.RX7AllowedUserIds[userId] == true
|
||||||
|
end
|
||||||
|
|
||||||
|
--- The starter car id this specific player should receive on first join.
|
||||||
|
function GameConfig.starterCarFor(userId: number): string
|
||||||
|
if GameConfig.isRX7Allowed(userId) then
|
||||||
|
return GameConfig.RestrictedStarterCarId
|
||||||
|
end
|
||||||
|
return GameConfig.PublicStarterCarId
|
||||||
|
end
|
||||||
|
|
||||||
-- Race rewards (flat MVP model; tune per-race later) -------------------------
|
-- Race rewards (flat MVP model; tune per-race later) -------------------------
|
||||||
GameConfig.RaceBaseReward = 250
|
GameConfig.RaceBaseReward = 250
|
||||||
|
|||||||
@@ -35,13 +35,10 @@ export type ProfileData = {
|
|||||||
local ProfileTemplate: ProfileData = {
|
local ProfileTemplate: ProfileData = {
|
||||||
Cash = GameConfig.StartingCash,
|
Cash = GameConfig.StartingCash,
|
||||||
Rep = GameConfig.StartingRep,
|
Rep = GameConfig.StartingRep,
|
||||||
OwnedCars = {
|
-- Starter car is granted per-player by DataManager after load (depends on
|
||||||
{
|
-- UserId: allowlisted players get the RX-7, everyone else the public car).
|
||||||
Id = GameConfig.StartingCarId,
|
OwnedCars = {},
|
||||||
Upgrades = { Engine = 0, Tires = 0, Nitro = 0 },
|
EquippedCarId = "",
|
||||||
},
|
|
||||||
},
|
|
||||||
EquippedCarId = GameConfig.StartingCarId,
|
|
||||||
LastDailyClaim = 0,
|
LastDailyClaim = 0,
|
||||||
TotalRaces = 0,
|
TotalRaces = 0,
|
||||||
Wins = 0,
|
Wins = 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user