Garry's Mod Wiki

File Based Storage

Introduction

Data Storage is what we use when we want data to persist through map changes and server restarts.

Never trust the client with data. Only store, save, change, and load data from the server. If you need to give data to the client, make sure to keep a backup of it on the server!

There are multiple ways of storing data in Garry's Mod. In this tutorial, we will go over File Based Storage.

Rules with data-handling

When handling any data, there are some rules that we should follow:

  • Never trust data that has been handled by a client. Keep all data backed up on the server as this is the only way that you can be sure that the data is authentic. Let's say we started letting clients keep track of their money; what's stopping them from adding a few zeros on the end?

  • Always use identification that the client can't manipulate. If we wanted to store a client's health when they log off, and we used their steam names as identification, what's stopping them from changing it before they next log on? Nothing. This is why we must use identification such as Player:SteamID's, since they can never be changed by the client.

In case you couldn't tell, there is an over-arching theme here: Never trust the client.

Unfortunately, Garry's mod servers aren't predictable - they can easily crash without warning or become overloaded. Therefore, you should deal with the data as quickly as possible to avoid losing it. Do not wait until the player leaves to save the number of tokens that they have earned whilst playing, as the server may suffer a fatal crash before then. Furthermore, you should not save data during the GM:ShutDown hook, as it may be too late to start saving data as some players will become invalid which means that their precious token count is now lost. However, you may be reading this and you think that your data should be saved at the very last minute, or is really not important enough to be saved every time it is altered - in which case, you can use the GM:ShutDown hook at your own risk!

About

Text files are a simple way of storing data. File Based Storage does not require any external modules, as it relies solely on the file library and is pretty straightforward. For these reasons, it is the recommended method for beginners.

Storing tables - util.TableToJSON

This is, perhaps, the easiest method for beginners that will circumvent frustration when converting data types such as Color or Vector, so we will start with this method first.

Turning tables into strings

A text file is basically a string of characters. This means that we need to convert all of our data into a string of characters...

util.TableToJSON is a function that will turn a Lua table into the JSON format. JSON is a popular format of representing certain data (such as Lua tables) as text, which can be saved in a file and turned back into the same data later.

Here is an example of how we can use util.TableToJSON:

-- Here we have a generic table, containing different types of data that we wish to save into a file: local storeOwnerData = { pos = Vector(1000,20,3), rot = Angle(0,90,0), col = Color(255,0,0,20), text = "Dave - Store Owner" } -- Let's see what util.TableToJSON is going to give us to work with: print(util.TableToJSON(storeOwnerData))

Output:

{"text":"Dave - Store Owner","rot":"{0 90 0}","pos":"[1000 20 3]","col":{"r":255.0,"b":0.0,"a":20.0,"g":0.0}}

Notice how the Color, Angle and Vector structures get converted into distinguishable string structures? These unique formats allows the reversing function util.JSONToTable to convert the formats back into their original data structures.

Let's write some further code so that we can see reversal process that will need to take place when reading the file.

-- JSONData now stores the Lua table "storeOwnerData" in JSON format. local JSONData = util.TableToJSON(storeOwnerData) local converted = util.JSONToTable(JSONData) PrintTable(converted)

Output:

col: a = 20 b = 0 g = 0 r = 255 pos = 1000.000000 20.000000 3.000000 rot = 0.000 90.000 0.000 text = Dave - Store Owner

As you can see, the JSON string has been converted back into a table format. Furthermore, we can check that the data types have been returned to their original format by using isvector with converted.pos and isangle with converted.rot which tell us that they have been converted back into a Vector and Angle. Unfortunately, passing converted.col into IsColor highlights that it has not been correctly converted into the Color structure. Fortunately, we can write some code to convert it back to normal later on.

Saving JSON data

Now we've learnt how to convert a table into a storable format, and also how to reverse the stored format into a normal Lua table - we can now learn how to read/write files and save the JSON data.

Believe it or not, saving data to a file only requires one function: file.Write. Upon looking at the documentation we learn that it only needs two things:

  • A filename
  • Data to save So let's go ahead and and write some code to finish saving the storeOwnerData table into a file called storedata.json:
local storeOwnerData = { pos = Vector(1000,20,3), rot = Angle(0,90,0), col = Color(255,0,0,20), text = "Dave - Store Owner" } -- Let's put it in a function so that when we next change the table, we can just call this function again function SaveStoreData() local converted = util.TableToJSON(storeOwnerData) file.Write("storedata.json", converted) end SaveStoreData()

That's it, that's the file saved!

If you want to view the file for your own eyes, you can go into the garrysmod/data folder and the file will be called "storedata.json".

Since September 2019, files can be saved into JSON (.json) format.

Reading JSON data from files, and converting it back to a table

Reading JSON data is just as easy with the key function now being file.Read that takes one argument... the Filename. Here we will also convert the table containing color data into a Color structure too:

local storeOwnerData = {} function ReadStoreData() -- Make sure you use the same filename as the one in file.Write! local JSONData = file.Read("storedata.json") -- JSONData is currently a JSON string - let's convert that into a table: storeOwnerData = util.JSONToTable(JSONData) -- Remember how the col value does not get converted into a Color structure? Let's fix that: local oldCol = storeOwnerData.col storeOwnerData.col = Color(oldCol.r, oldCol.g, oldCol.b, oldCol.a) end

The variable storeOwnerData now contains all of the data and the col value is now a Color!

Writing data types other than tables

The above method is excellent for storing tables, but what if we just want to store a number or a string?

Nothing is particularly different in this scenario, we just don't need the util functions anymore:

Saving the string

local myString = "This is a string that I want to save!" function SaveString() -- We are saving the contents of myString into a file called "stringdata.txt" file.Write("stringdata.txt", myString) end SaveString()

Loading the string

local myString = "" function LoadString() myString = file.Read("stringdata.txt") end LoadString()

Not that difficult at all, is it?

Alternatives to file based storage

Let's say that we needed to keep track of a player's money count. Then, we would need to save to the file every time the player either leaves or has their money count updated, and thus, a 1,000 line file (or even more!) will need to be read, altered, and then completely overwritten with new data. This is slow, and it will get much slower the more players have their data stored in a file.

Therefore, we need an alternative.

Perhaps the most efficient method of storing large amounts of data in Garry's mod is through SQL. Unfortunately, SQL is not as easy to learn as file based storage, however, it is completely worth it if you do decide to learn.

PData could be a viable alternative, however, as of June 2020, I cannot recommend Player:SetPData, as PData has a major issue that has not been resolved yet, which can cause players to share data with a different player entirely. The issue is currently on the SetPData page, and if it is ever removed, then you are probably safe to use it.

Page Links


Special Pages


Wikis

?

Render Time: 127ms

DB GetPage 65
Generate Html 35
SaveChanges (1) 10
Render Body 0
Render Sidebar 13