Object-Oriented Programming with DataStores?

Hello fine developers of the DevForums,

I’ve recently gotten into Object-Oriented Programming (OOP) and it’s been very interesting and fun. I learn from @jonbyte’s highly detailed and cool tutorial (thanks JonByte, you’re cool). You get to program in another way where you could make your own methods as well, which is even more fun. I then realized that you could program DataStores in OOP, so I made an attempt.

local default_data = {};

local PlayerData = {};
PlayerData._index = PlayerData;

function PlayerData.new(Player)
	local self = setmetatable({}, PlayerData);
	self.CashData = game:GetService("DataStoreService"):GetDataStore("Cash");
	self.DataKey = Player.UserId .. "-cash";
	self.Value = Player.leaderstats.Cash.Value;
	
	return self;
end

function PlayerData:Save()
	local Result;
	local Success, errmessage = pcall(function()
		Result = self.CashData:SetAsync(self.DataKey, self.Value);
	end)
	
	if Success then
		print("Data save was successful.");
	else
		print("There was a problem with saving the data.");
		warn(errmessage);
	end
end

function PlayerData:Load()
	local Result;
	local success, errmessage = pcall(function()
		Result = self.CashData:GetAsync(self.DataKey);
	end)
	
	if success then
		if Result then
			print("Current Cash: " .. Result);
		else
			return nil;
		end
	else
		print("There was a problem with loading the data.");
		warn(errmessage);
	end
end

This was my attempt, but then I ran into a question that I couldn’t answer…

How would I load the player’s data? How would I check if the player already had data before loading it? There were a few problems here and I think it’s because of the way I did the whole thing. I don’t fully understand all the aspects of OOP yet. So I may be doing something wrong here. I don’t really know what to do so what should I do?

3 Likes

Assuming that script is a ModuleScript, which it should be, you would have to use the global require function to get the table (PlayerData in your case) returned by the module.

local PlayerData = require(path_to_module)

Then you would be able to create new “objects” of PlayerData for each new player.

Players.PlayerAdded:Connect(function(Player)
  local newPlayerData = PlayerData.new(Player)
end)

Next you can use the “newPlayerData” and call the methods on it:

Players.PlayerAdded:Connect(function(Player)
  local newPlayerData = PlayerData.new(Player)
  newPlayerData:Load()
  -- etc
end)
1 Like

Why does it say “attempt to call a nil value”? It’s coming from the :Load(). This has to be the most less-descriptive error I ever got.

Oh, it’s because you did _index instead of __index for your metamethod.

2 Likes

Hi @jonbyte

Doesn’t GetDataStore() method need to be under protected call pcall() because it can generate error due to internet inactivity, BTW I’m also new to lua…

Is this correct to initial self with local and one more thing, in tutrial a new variable was create to assign the metatable to, like;

function PlayerData.new(Player)
	local metaTable = setmetatable({}, PlayerData);
	metaTable.CashData = game:GetService("DataStoreService"):GetDataStore("Cash");
	metaTable.DataKey = Player.UserId .. "-cash";
	metaTable.Value = Player.leaderstats.Cash.Value;
	
	return metaTable;
end

You are thinking of when you send requests to the datastore, but no if you are just calling GetDataStore you don’t need to wrap that in a pcall.

What do you mean by this statement?

Also in your code provided, remember that setmetatable returns the table that you attached the metatable to. So calling it metatable would be mislabelling it. In the scope of that function, self wouldn’t be anything since we are using dot syntax to create the function in the table. So I generally use self as the variable to represent the table returned from setmetatable.

1 Like

Thanks for clearification, and what I was asking either local is required to declare self or not but within function scope self is acting just like any other variable, am I right?

Well, in the scope of the function, yes, self can act as just any variable name; however, you wouldn’t need to add a local beside it and here is why:

When you are adding something to a table, more specifically a dictionary you would do it like this.

local table = {}
table.Something = true

You wouldn’t add “local” before it because that wouldn’t make sense.

I hope this clears things up for you.