SheetValues
by boatbomber
I saw a few people asking what open-source values libraries exist for Roblox for things like fflags, and most of them relied on small providers. I prefer to use Google’s services, since they’re big and reliable. So I wrote this.
Using Google Sheets allows you to update your values from your phone, desktop, or tablet. It’s supported on all devices which makes it a really good “console” for live value editing.
This system updates every 30 seconds (and that number is configurable within the module). This allows you to propagate changes to all your servers really fast. Only one server actually calls the API, the rest get it through MessagingService or DatastoreService. This keeps your HttpService usage down to a minimum, and keeps Google from being annoyed at us.
Setup:
Getting started is really easy. Gone are the days of API keys and custom sheet macro scripts.
All you need is the share link, set to “Anyone on the internet with this link can view”. Copy that link.
The link will look something like this:
docs.google .com/spreadsheets/d/ALPHANUMERIC_SPREAD_ID/edit?usp=sharing
Copy the big spread id out of that link, as that’s how our system will know what spread to read from.
If you’re using multiple sheets in a single spread, the SheetId will be at the end of the main url. Look for “#gid=” and copy everything after the equals symbol. This is really useful for having one spread per game, with multiple sheets in it for your various systems.
docs.google .com/spreadsheets/d/ALPHANUMERIC_SPREAD_ID/edit#gid=NUMERIC_SHEET_ID
Pass that into SheetValues.new("ALPHANUMERIC_SPREAD_ID", "NUMERIC_SHEET_ID")
and it will return a SheetManager linked to that sheet. Note that the SheetId parameter is optional and will default to the first (or only) sheet in your spread.
What should your Google Sheet look like?
Well, rows are turned into Values with each column entry being a Property of the Value.
Here’s the structure:
The first row of the Sheet is the Header. This row will NOT become a Value, rather it defines how we parse the subsequent rows into Values. Each entry into row 1 becomes the key for that column (Property).
Example:
Name PropertyName AnotherProp
TestValue 100 true
NextValue 300 false
This results in two Values being stored and structured like so:
SheetManager.Values = {
["TestValue"] = {
PropertyName = 100,
AnotherProp = true
},
["NextValue"] = {
PropertyName = 300,
AnotherProp = false
},
}
It’s not strictly enforced, but it is STRONGLY recommended that you have a “Name” Property so that it will index your values by Name (will use row number if no Name prop exists), as it is much easier to for you to work with.
If you have a boolean or number entered, it will attempt to convert the string into your intended datatype.
To create special types, you can explicitly mark them by having the property be “Type(val)”, like “Vector3(1,0,3)”
Supported explicit property Types (not case sensitive):
- string (for ensuring a number/boolean remains a string)
- array
- dictionary
- Vector3
- Vector2
- UDim2
- UDim
- Color3 (0-1)
- RGB (0-255)
- BrickColor
- CFrame
- Enum
- Rect
API:
function SheetValues.new(SpreadId: string, SheetId: string?)
returns a new SheetManager
function SheetManager:UpdateValues()
gets the latest values of the sheet
(This is called automatically and is only exposed for critical cases)
function SheetManager:GetValue(ValueName: string, DefaultValue: any)
returns the Value or DefaultValue if the Value doesn’t exist
(This is the same as doing "SheetManager.Values.ValueName or DefaultValue"
and only exists for style purposes)
function SheetManager:GetValueChangedSignal(ValueName: string)
returns a RBXScriptSignal that fires when the given Value changes, passing two arguements in the fired event (NewValue, OldValue)
function SheetManager:Destroy()
cleans up the SheetManager
table SheetManager.Values
dictionary of your values
number SheetManager.LastUpdated
Unix timestamp of the last time SheetManager.Values was updated
string SheetManager.LastSource
Name of the service used to retrieve the current SheetManager.Values (Google API, Datastore, Datastore Override, MsgService Subscription)
(Used for debugging)
RBXScriptSignal SheetManager.Changed(NewValues: table)
Fires when SheetManager.Values is changed
Example:
A good use of these live updating values is developing a anticheat system.
You can create Values with properties like PunishmentsEnabled so that you can test various methods and thresholds without punishing false positives while you work.
Additionally, you can add properties to the Values for thresholds and cheat parameters, so you can fine tune your system without needing to restart the game servers, allowing you to gather analytics and polish your system with ease.
Sheet used by the Example Code:
Name PunishmentEnabled Threshold
SpeedCheat FALSE 35
local SheetValues = require(script.SheetValues)
local AnticheatSheet = SheetValues.new("SPREADSHEET_ID")
local function PunishCheater(Player)
if not AnticheatSheet.Values.SpeedCheat.PunishmentEnabled then
-- Punishments aren't enabled, don't punish
return
end
Player:Kick("Cheating")
end
local function CheckSpeedCheat(Player)
if Speeds[Player] > AnticheatSheet.Values.SpeedCheat.Threshold then
SendAnalytics("SpeedTriggered", Speeds[Player])
PunishCheater(Player)
end
end