FastValueService

Russian:
https://devforum.roblox.com/t/fastvalueservice/700921


Repositories:

RojoFastValuesAPI
https://github.com/MFD-gh/RojoFastValuesAPI


Created by @AleksejGrasnich @IvanGregrovnich and @0x6e7367


What is a FastValue?


A FastValue is a Value, when changed, will apply to every server without requiring a server shutdown.

FastFlag

A FastFlag is a Boolean. This is mostly used for BetaFeatures that you have but don’t want to be enabled until you finish them

FastString

A FastString is a String. This will mostly be used as a Code for something, i.e. a promotional code, or an AES Key

FastNumber

A FastNumber is an Int32, Int64, Float or Double. This will mostly be used for Versions.

FastDictionary (or FastTable)

A FastDictionary is a List, Table, Array or Dictionary etc.. This will be used for setting multiple values that you want to have in only one constant.


Download.

file.rbxm
roblox.com


To use FastValueService (Server):

local ServerScriptService = game:GetService("ServerScriptService");
local FastValueServiceContainer = ServerScriptService:WaitForChild("FastValueService");
local FastValueService = require(FastValueServiceContainer:WaitForChild("FastValueService"));
require(ServerScriptService.FastValueService:WaitForChild("FastValueServiceListenerStarter"))();

-- Create a FastFlag called TestFlag with a value of True:
FastValueService:DefineFastFlag("TestFlag", true);

-- Create a FastFlag called TestStringFlag with a string value:
FastValueService:DefineFastFlag("TestStringFlag", "This will error");
-- Will return the error: "FastFlag is not a boolean, consider not using "This will error" (String)"

-- Get a the FastFlag we just created:
FastValueService:GetFastFlag("TestFlag");
-- Will return: "true"

-- Get a FastFlag that doesn't exist:
FastValueService:GetFastFlag("TestFlagThatDoesNotExist");
-- Will return the error: "FastFlag not found. Consider doing a DefineFastFlag("TestFlagThatDoesNotExist", true or false)."

-- To see the rest of the values, scroll down to the API Reference.

To use FastValueService (Client):

local ReplicatedStorage = game:GetService("ReplicatedStorage");
local GetFastValue = ReplicatedStorage:WaitForChild("GetFastValue");

-- To get a FasfValue (FastFlag in this example):
local FastFlag = GetFastValue:InvokeServer("GET", "FASTFLAG", "TestFlag");

-- To create a  FastValue (FastString in this example):
local FastString = GetFastValue:InvokeServer("POST", "FASTSTRING", "TestFlag2", "String");

When you download the File from above. It doesn’t matter where it is put (As long as it isn’t anywhere on the client), and it will setup everything itself:
image


API Docs:

GET


GetFastFlag

bool GetFastFlag(string FlagName)

Parameter(s)

Name Type Default Description
FlagName String nil The name of the FFlag you are attempting to access. If nil then it will error

Return

Return Summary
Boolean True or false depending on if it’s enabled

GetFastString

string GetFastString(string FlagName)

Parameter(s)

Name Type Default Description
FlagName String nil The name of the FastString you are attempting to access. If nil then it will error.

Return

Return Summary
String Whatever value was set

GetFastNumber

int, float, double GetFastNumber(string FlagName)

Parameter(s)

Name Type Default Description
FlagName String nil The name of the FastNumber you are attempting to access. If nil then it will error

Return

Return Summary
Int32, Int64, Float, Double Will be atleast 0+ or 0-

POST


DefineFastFlag

void DefineFastFlag(string FlagName, bool Value)

Parameter(s)

Name Type Default Description
FlagName String nil The name of the FFlag you are attempting to create. If nil then it will error
Value Boolean false Value, can either be true or false

Return

Return Summary
Void, Nil or None Will return nothing

DefineFastString

void DefineFastString(string FlagName, string Value)

Parameter(s)

Name Type Default Description
FlagName String `` The name of the FastString you are attempting to create. If nil then it will error
Value string “” Value, can be empty also, but has to be a string

Return

Return Summary
Void, Nil or None Will return nothing


DefineFastNumber

void DefineFastNumber(string FlagName, int float double Value)

Parameter(s)

Name Type Default Description
FlagName String nil The name of the FNumber you are attempting to create. If nil then it will error
Value Int32, Int64, Float, Double 0 Value, can either be an Int32, Int64, Float or Double

Return

Return Summary
Void, Nil or None Returns nothing

UPCOMING FEATURES

DynamicFlagLibrary


Ignore (Source)

FastValueServiceSetupRunner (ClassName: Script)

--!nocheck

--	// FileName: FastValueServiceSetupRunner.lua
--	// Version: 1.0
--	// Written By: Aleksej Grasnich
--	// Description: Will setup everything in order
--	// TODO:
--			

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")

local this = script
local ToBeReplicated = this:WaitForChild("FastValueService")

local GetFastValue = Instance.new("RemoteFunction", ReplicatedStorage)
GetFastValue.Name = "GetFastValue"

local FastValueServiceInfo = Instance.new("Folder", ToBeReplicated)
FastValueServiceInfo.Name = "Info"

local Link = Instance.new("StringValue", ToBeReplicated)
Link.Name = "https://devforum.roblox.com/t/fastvalueservice/"

local Credit = Instance.new("StringValue", FastValueServiceInfo)
Credit.Name = "Credit"
Credit.Value = "Aleksej Nikolaevich Grasnich, Nikita Nikolaevich Pedko, Ivan Ivanovich Gregrovnich"

local LastUpdateDate = Instance.new("StringValue", FastValueServiceInfo)
LastUpdateDate.Name = "LastUpdate"
LastUpdateDate.Value = "25/07/2020 05:04.123 UTC+9"

local Version = Instance.new("IntValue", FastValueServiceInfo)
Version.Name = "Version"
Version.Value = 4

local DevforumLink = Instance.new("StringValue", FastValueServiceInfo)
DevforumLink.Name = "DevforumLink"
DevforumLink.Value = "https://devforum.roblox.com/t/fastvalueservice/"

ToBeReplicated.Parent = ServerScriptService

script:Destroy()

FastValueService (ClassName: ModuleScript)

--!nocheck

--	// FileName: FastValueService.lua
--	// Version: 2.5
--	// Written By: Aleksej Grasnich and Nikita Pedko
--	// Description: Main handler when using FastValues
--	// TODO:
--			Add support for DynamicFlags


-------------- SERVICES --------------
local DataStoreService = game:GetService("DataStoreService")
local HttpService = game:GetService("HttpService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-------------- DATASTORES --------------
local FastFlagDataStore = DataStoreService:GetDataStore("FlagStore", "FastFlags")
local FastStringDataStore = DataStoreService:GetDataStore("FlagStore", "FastStrings")
local FastNumberDataStore = DataStoreService:GetDataStore("FlagStore", "FastNumbers")
local FastDictionaryDataStore = DataStoreService:GetDataStore("FlagStore", "FastDictionarys")

-------------- CONSTANTS --------------
local FlagFunctions = {}

-------------- FLAGFUNCTIONS --------------
-- STATIC FLAGS
-- FASTFLAG
-- GET
function FlagFunctions:GetFastFlag(FastFlag)
	if typeof(FastFlag) ~= "string" then
		return error("FastFlag searcher root must be a string!")
	end
	local FastFlagReturnedData = FastFlagDataStore:GetAsync(FastFlag)
	if FastFlagReturnedData == nil then
		return error("FastFlag not found. Consider doing a DefineFastFlag(\""..FastFlag.."\", true or false).")
	end
	return FastFlagReturnedData
end
-- PUBLISH
function FlagFunctions:DefineFastFlag(Name, Value)
	if typeof(Value) ~= "boolean" then
		return error("FastFlag is not a boolean, consider not using "..Value.." ("..typeof(Value)..")")
	end
	FastFlagDataStore:SetAsync(Name, Value)
end

-- FASTSTRING
-- GET
function FlagFunctions:GetFastString(FastString)
	if typeof(FastString) ~= "string" then
		return error("FastString searcher root must be a string!")
	end
	local FastStringReturnedData = FastStringDataStore:GetAsync(FastString)
	
	if FastStringReturnedData == nil then
		return error("FastString not found. Consider doing a DefineFastString(\""..FastString.."\", \" \").")
	end
	return FastStringReturnedData
end
-- PUBLISH
function FlagFunctions:DefineFastString(Name, Value)
	if typeof(Value) ~= "string" then
		return error("FastString is not a string, consider not using "..Value.." ("..typeof(Value)..")")
	end
	FastStringDataStore:SetAsync(Name, Value)
end

-- FASTNUMBER
-- GET
function FlagFunctions:GetFastNumber(FastNumber)
	if typeof(FastNumber) ~= "string" then
		return error("FastNumber searcher root must be a string!")
	end
	local FastNumberReturnedData = FastNumberDataStore:GetAsync(FastNumber)
	if FastNumberReturnedData == nil then
		return error("FastNumber not found. Consider doing a DefineFastNumber(\""..FastNumber.."\", 0).")
	end
	return FastNumberReturnedData
end
-- PUBLISH
function FlagFunctions:DefineFastNumber(Name, Value)
	if typeof(Value) ~= "number" then
		return error("FastNumber is not a number, consider not using "..Value.." ("..typeof(Value)..")")
	end
	FastNumberDataStore:SetAsync(Name, Value)
end

-- FASTDICTIONARY
-- GET
function FlagFunctions:GetFastDictionary(FastDictionary, ShowValues)
	if typeof(FastDictionary) ~= "string" then
		return error("FastDictionary searcher root must be a string!")
	end
	local FastDictionaryReturnedData = FastDictionaryDataStore:GetAsync(FastDictionary)
	if FastDictionaryReturnedData == nil then
		return error("FastDictionary not found. Consider doing a DefineFastDictionary(\""..FastDictionary.."\", {}).")
	end	
	local DecodedFastDictionaryReturnedData = HttpService:JSONDecode(FastDictionaryReturnedData)
	if ShowValues then
		warn("Showing values of "..FastDictionary)
		for Key, Value in pairs(DecodedFastDictionaryReturnedData) do
			print(Key,"|",Value,"|",typeof(Value))
		end
		return FastDictionaryReturnedData
	end
	return FastDictionaryReturnedData
end
-- PUBLISH
function FlagFunctions:DefineFastDictionary(Name, Value, Sort)
	if typeof(Value) ~= "table" then
		return error("FastDictionary is not a table, consider not using "..tostring(Value).." ("..typeof(Value)..")")
	end
	if Sort then
		table.sort(Value)
	end
	FastDictionaryDataStore:SetAsync(Name, HttpService:JSONEncode(Value))
end
return FlagFunctions

FastValueServiceListener (ClassName: ModuleScript)

local FastValueService = require(script.Parent)

return function(fromPlayer, Cast, Type, fastValue, ValueToSet)
	if Type:lower() == "fastflag" then
		if Cast:lower() == "get" then
			return FastValueService:GetFastFlag(fastValue)
		elseif Cast:lower() == "post" then
			if type(ValueToSet) ~= "boolean" then
				return error("Type set is not valid")
			else
				return FastValueService:DefineFastFlag(fastValue, ValueToSet)
			end
		else
			return error("Cast is not valid!")
		end
	elseif Type:lower() == "faststring" then
		if Cast:lower() == "get" then
			return FastValueService:GetFastString(fastValue)
		elseif Cast:lower() == "post" then
			if type(ValueToSet) ~= "string" then
				return error("Type set is not valid")
			else
				return FastValueService:DefineFastString(fastValue, ValueToSet)
			end
		else
			return error("Cast is not valid")
		end
	elseif Type:lower() == "fastnumber" then
		if Cast:lower() == "get" then
			return FastValueService:GetFastNumber(fastValue)
		elseif Cast:lower() == "post" then
			if type(ValueToSet) ~= "number" then
				return error("Type set is not valid")
			else
				return FastValueService:DefineFastNumber(fastValue, ValueToSet)
			end
		else
			return error("Cast is not valid")
		end
	else
		return error(Type.." is not a valid FastValue type.")
	end
end

FastValueServiceListenerStarter (ClassName: ModuleScript)

-- I feel as if this is useless, will most likely be deprecated in the future
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local FastValueServiceListenerModule = require(script.Parent:WaitForChild("FastValueServiceListener"))
return function()
	ReplicatedStorage:WaitForChild("GetFastValue").OnServerInvoke = FastValueServiceListenerModule
end
23 Likes

Be careful relying on FFlags to give accurate information. They can be removed or renamed without any warning. I’ve learned this the hard way a few times before.

4 Likes

I think this is for making custom values for use in your own game.

2 Likes

Yes, but it works for both as well. Some examples he provided, such as UseRoactPlayerList3 and UseNewPlayerlistIcons2 are real FFlag names and its implied that they’ll work with this.

2 Likes

Skimming through the code it doesn’t seem to use actual FFlags at all.

2 Likes

My bad for assuming things, thanks for pointing that out lol.

4 Likes

No, because roblox’s

game:GetFastFlag()
game:DefineFastFlag()
settings():GetFFlag()
settings():GetFVariable()

are locked.
Only plugins and command line can use GetFastFlag.
Plus the above examples UseRoactPlayerlist3.
It’s version 3 of my roact playetlist.
As annoying as it is, that’s the path that they choose to annoy me

1 Like

FastFlags (FFlags) are the underlying deployment processes that are dynamic and can be enabled / disabled without the game ever shutting down. It’s how Roblox pushes content updates without everyone getting booted off, they’re also made in advance to actually being enabled. @evaera explains this better on their Fast Flag Watcher.

The Roblox engine uses a system called Fast Flags as part of its deployment process. When code is shipped, not all of it is active by default. Rather, the changes are suppressed by flags that are dynamically enabled and disabled, even after the code is live on production.

Their website shows all of the Fast Flags known and what exactly it’s equal to, it’s something I’d recommend looking at for those who don’t know what Fast Flags are.
Roblox FFlag Watcher by evaera

3 Likes

I quickly just edited your code to remove a few very unnessecary datastore calls, went from about 9 calls iirc, to about 6, won’t seem like a lot, but if you are checking a lot of flags often, it could slow the server down, most unlikely but yeah.

I just put the GetAsync into a variable at the start of the function, that way you removed the datastore call when you wanted to return the fflag.

local DataStoreService = game:GetService("DataStoreService")
local FFlagStore = DataStoreService:GetDataStore("FFlagStore")
local FStringStore = DataStoreService:GetDataStore("FStringStore")
local FIntStore = DataStoreService:GetDataStore("FIntStore")
local FlagCommands = {}

function FlagCommands:GetFastFlag(FFlag)
	local FFlagData = FFlagStore:GetAsync(FFlag)
	if FFlagData == nil then
		FlagCommands:DefineFastFlag(FFlag, false)
	end
	return FFlagData
end

function FlagCommands:GetFastString(FString)
	local FStringData = FStringStore:GetAsync(FString)
	if FStringData == nil then
		FlagCommands:DefineFastString(FString, "")
	end
	return FStringData
end
 
function FlagCommands:GetFastNumber(FInt)
	local FIntData = FIntStore:GetAsync(FInt)
	if FIntData == nil then
		FlagCommands:DefineFastNumber(FInt, 0)
	end
	return FIntData
end


function FlagCommands:DefineFastFlag(Name, Value)
	if type(Value) ~= "boolean" then
		return error("FFlag is not a Bool")
	end
	FFlagStore:SetAsync(Name, Value)
	return Value
end

function FlagCommands:DefineFastString(Name, Value)
	if type(Value) ~= "string" then
		return error("FString is not a string")
	end
	FStringStore:SetAsync(Name, Value)
	return Value
end

function FlagCommands:DefineFastNumber(Name, Value)
	if type(Value) ~= "number" then
		return error("FInt is not an number")
	end
	FIntStore:SetAsync(Name, Value)
	return Value
end

return FlagCommands
3 Likes

Rather than using datastores, couldn’t you use MessagingService to deliver information when needed, so that your module doesn’t interfere with existing datastore scripts?

Messaging service, from what I remember from the top of my head, is faster anyways (<~1MS).

1 Like

Okay, I’ll create a messaging service version and put it below the DataStore one

2 Likes

@iGottic follow up to the MessagingService. You mean I should use it to update the Values that they are appended to?

1 Like

Correct. I have my own MessagingService fastvalue system for a few projects, so there are a few simple steps to setting it up:

  • MessagingService sends the data required to a relevant subscribed topic, like “fastvalue.”
  • Because new servers won’t get the value DataStoreService will have to be used a tiny bit. Set the value from the original topic sender.
  • On all ends, the value is received and the appropriate value is changed.
  • For new servers, a single GetAsync for DataStore can be used to grab the current data.

So while you aren’t 100% migrating away from DataStoreService, it rids the imposing risk of interfering with other data saving scipts, and you speed up response time.

1 Like

I already know how to use it, I was just wondering in what context you were suggesting to use it

2 Likes

This seems like once you have a lot of flags/players, you’re going to hit your datastore caps pretty quickly? Am I mistaken?

1 Like

I mean, how many values do you think people will have?

1 Like

One datastore call per value seems like quite a lot to me. I usually batch all my values inside tables.

In the past I’ve used flags for keeping track of many attributes about a character. Such as awarding special items to players that played in specific stages of the games development. In the end I had probably 20-25 values that were checked on join. Combine that with the inventory datastore and the ban datastore, I couldn’t fathom checking all those values with individual get calls to the datastore.

1 Like

Same here.

Convert your data into tables, which are packaged into JSON formatted strings.

Because I rely on Data Store to save data in my games already, I wouldn’t want to risk overload and therefore dataloss.

2 Likes

Of course I do! I’m just saying, I had 25 values in those tables, replacing them with this solution would make me hit the data-cap quite quickly.

2 Likes

Sometimes using tables in Datastores will error, returning the callback “Only Valid UTF-8 characters are accepted in Datastores”

1 Like