sc-sync "Server Clients Sync"

Server Clients Sync

Script that synchronize values between the server and the clients.

How it works

Both the server and the client save the values in a table.
Once one of them change the values, it sends an event to the other side to update that value.
There are two types of values:

  • Globals: Synchronized values between the server and all clients.
  • Privates: Synchronized values between the server and a specific client.
    Not all the clients are allowed to change a global value but all the clients are always allowed to change their private values.
    What decide whether a client is allowed to change a global value or not is a server-side checker added by the developer.

Usage

All script’s functionality works with exports.

Server

Before the exports, there are some commands:

  • SCSdebug: Toggle print debug stuff in the console.
  • SCSglobals: Print all the global values in the console.
  • SCScheckers: Print the count of checkers on each global value.
  • SCSprivates: Print all the private values of a specific client in the console.

Server Global

Control the global values from the server.

GetGlobal

Get a global value using a key, example:

local weather = exports["sc-sync"]:GetGlobal("weather")
print("The weather is: " .. weather)
SetGlobal

Set a global value using a key, example:

exports["sc-sync"]:SetGlobal("weather", "clear")

Checker

Control the checkers.

AddGlobalChecker

Add a checker on a global value using a key, example:

exports["sc-sync"]:AddGlobalChecker("weather", function(src, value)
  if GetPlayerName(src) == "Owner" then -- this is just for the example, don't use it :)
    local weather_type = string.upper(value)
    if
      weather_type == "BLIZZARD" or
      weather_type == "CLEAR" or
      weather_type == "CLEARING" or
      weather_type == "CLOUDS" or
      weather_type == "EXTRASUNNY" or
      weather_type == "FOGGY" or
      weather_type == "HALLOWEEN" or
      weather_type == "NEUTRAL" or
      weather_type == "OVERCAST" or
      weather_type == "RAIN" or
      weather_type == "SMOG" or
      weather_type == "SNOW" or
      weather_type == "SNOWLIGHT" or
      weather_type == "THUNDER" or
      weather_type == "XMAS"
    then
      return true
    end
  end
  return false
end)
RemoveGlobalChecker

Remove a checker from a global value using a key and index, example:

local checker_index = exports["sc-sync"]:AddGlobalChecker("weather", function(src, value)
  return GetPlayerName(src) == "Owner" -- this is just for the example, don't use it :)
end)

RegisterCommand("free4all", function()
  exports["sc-sync"]:RemoveGlobalChecker("weather", checker_index)
end, true)

Server Private

Control clients’ private values from the server.

GetPrivate

Get a private value of a client using a key and source, example:

local src = 1 -- any source
local in_task = exports["sc-sync"]:GetPrivate(src, "in_task")
if in_task then
  print("player[" .. tostring(src) .. "] is in the task.")
else
  print("player[" .. tostring(src) .. "] isn't in the task.")
end
SetPrivate

Set a private value of a client using a key and source, example:

local src = 1 -- any source
exports["sc-sync"]:SetPrivate(src, "in_task", true)

Client

Before the exports, there are some commands:

  • SCSdebug: Toggle print debug stuff in the console.
  • SCSglobals: Print all the global values in the console.
  • SCSprivates: Print all the private values in the console.

Client Global

Control global values from the client.

GetGlobal

Get a global value using a key, example:

local weather = exports["sc-sync"]:GetGlobal("weather")
print("The weather is: " .. weather)
SetGlobal

Set a global value using a key, example:

exports["sc-sync"]:SetGlobal("weather", "clear", function(set, justification)
  if set then
    print("Changed the weather successfully.")
  else
    print("Changing the weather failed, reason: " .. justification .. ".")
  end
end)

Client Private

Control private values from the client.

GetPrivate

Get a private value using a key, example:

local in_task = exports["sc-sync"]:GetPrivate("in_task")
if in_task then
  print("In the task.")
else
  print("Not in the task.")
end
SetPrivate

Set a private value using a key, example:

exports["sc-sync"]:SetPrivate("in_task", false)
3 Likes

Is there any reason people should use this instead of state bags? What is the difference?

Not a big difference acually, but I think the major is:

  • the checkers for changing global values, which are more kinda permission or so
  • the fact that more than one checker can be added/removed
  • keys are not really linked to an entity or a player, just the unique string

finally, I’ve created the script for managing global variables
letting specfic players to be able to change them
I thougth that someone may use it, so I posted it.
anyway, thanks for reading, lmk if u have any suggestion or ideas, thanks again

1 Like

Does this just sync the weather of the server to all server players?

When using global states you are not able to store any type of table in it. Is it possible to do so in this script?

no, it’s just an example for the usage, weather could be a global value that is used by another script to check the weather state and apply it for all players

1 Like

yes it’s possible, you can store a table type value
but the table metadata won’t be stored, as you get the table through an export from another script

Not 100% sure what you mean, but I suppose you mean that it cam check if someone can change the global values based on ace premissions. Which in out of self sounds good and all, but I think the server set, client read policy of the state bags suffices on the security front (as long as you don’t blindly trust the client).

Well that’s basically what global states are, but ok.

But regardless of what I think, if people want to use this then, go ahead! I guess it’s also usefull for poeple who want to learn how a system like this could have been made.

And just as a final note I don’t intend to be rude, I just tought it was a little funny that you (at least in my mind) essentialy reacreating an allready existing system.

(And as another “final” note: thanks for sharing stuff with the community, don’t be discoraged to do so in the feuture, even if I’m a little critical lol)

1 Like

I use it for example in my main resource (I have only 1 script-resource) for this reason I have removed the exports and only use the functions, so it is impossible for hackers to change these values.

For state bags, I don’t know if it is currently impossible for them to change them.

okay np, but “hackers” won’t be able to change the variables
as long as a checker is added to allow/deny particular players to change the values

Do you talk about your system or state bags?

Me either, not totally sure what you mean, but what I understood is that your talking about the set policy.
in state bags case, only the server could change a global variable
of course you can create an event to change the global values by a set of players
but if you have more than one event, every one would check its own conditions regardlessing the others
unlike checkers which must all of them be checked successfully in order to change the variable

I ment by that the private/local variable, which are related to entity/player in state bags case.

nah don’t care about it, I’ve created this resource since a while, for a min… I really thought there was no difference :joy::sweat_smile:

thanks man I appreciate it, be critical as you wish it’s actually helps a lot
I’d like to know if there are any issues or suggestions you’d like to share
thanks for your time :blue_heart:

1 Like

both… in state bags case, there is no client able to change any global variable
in my system/script as long as there is a checker, the value won’t change
until the checker pass successfully “return true”