Need help for "OOP"

Hi, I wanna make a fully functional player stats, and i thought using OOP would be a great idea. The problem I tried to make a playersTable, where you can find every playerStats using the userId. So at the top of the script I created a local variable “local playersStatsTable = {}” the problem is that every time i require the script the playersStatsTable reset. I would love to know if there is a solution inside the module script without using some “outside help”. I was thinking of adding a bool value under the script to check if it was required at least once, but i thought it wasnt clean. Is there any cleaner solution?

local PlayerStats = {}

PlayerStats.__index = PlayerStats

local playersTable = {}

function PlayerStats.new(player : Player)
	local self = setmetatable({}, PlayerStats)
	
	-- Player
	self.player = player
	
	-- Base Stats
	self.kills = 0
	self.deaths = 0
	
	-- Killing spree
	self._killSpreeResetTask = nil
	self.killSpreeTimer = 10
	self.currentKillSpree = 0
	self.maxKillSpree = 0	
	
	return self
end

function PlayerStats:updateMaxKillSpree()
	if self.currentKillSpree > self.maxKillSpree then
		self.maxKillSpree = self.currentKillSpree
	end
end

function PlayerStats:updateKillSpree()
	
	self.currentKillSpree += 1
	self:updateMaxKillSpree()
	
	print("Killing spree: " .. self.currentKillSpree)
	
	-- Cancella il precedente reset se esiste
	if self._killSpreeResetTask then
		task.cancel(self._killSpreeResetTask)
	end

	-- Pianifica il reset dopo killSpreeTimer secondi
	self._killSpreeResetTask = task.delay(self.killSpreeTimer, function()
		self.currentKillSpree = 0
	end)
end

function PlayerStats:addKill()
	self.kills += 1
	
	self:updateKillSpree()
end

function PlayerStats:addDeath()
	self.deaths += 1
end

function PlayerStats:printData()

	print(string.format("Kills: %s", self.kills))
	print(string.format("Deaths: %s", self.deaths))
	print(string.format("Current Kill Spree: %s", self.currentKillSpree))
	print(string.format("Max Kill Spree: %s", self.maxKillSpree))
end

function PlayerStats.getPlayerByUserId(userId)
	
end


function PlayerStats.removePlayerByUserId(userId)
	
end

return PlayerStats



2 Likes

it would be better if you could also show us the code that manages the player’s stats.

1 Like

The playersTable should be in the script requiring the PlayerStats object. This is commonly seen in practice by implementing a “manager” or “handler” depending on who you ask about naming conventions. This again, is very common in an object oriented language like java, but its arguably over engineering in luau.

For example, PlayerStatManager would be required once and only once in a central location, and contain the table, whether that be a central data handler script, or a statmanager module of some sort, that you guarantee is only required once.

Or, much simpler, instead of defining playersTable as a local variable, meaning it will have the scope of the requiring script, you can define it as a variable of playerStats. Since roblox modules cache their require values, it will then replicate across server scripts.

Let me know if this clears things up or if I just confused you more.

1 Like

Hey! @mauriepico

The issue is that playersTable is declared as a local variable, which means it resets every time the module is required.

A cleaner solution is to store it inside the module itself so that it persists across requires:

local PlayerStats = {}
PlayerStats.__index = PlayerStats

PlayerStats._playersTable = {}

function PlayerStats.new(player)
	local self = setmetatable({}, PlayerStats)
	-- initialize...
	PlayerStats._playersTable[player.UserId] = self
	return self
end

function PlayerStats.getPlayerByUserId(userId)
	return PlayerStats._playersTable[userId]
end

function PlayerStats.removePlayerByUserId(userId)
	PlayerStats._playersTable[userId] = nil
end

This avoids external flags or globals and keeps your structure clean and modular.

2 Likes

I tried your solution and I like it.
Thank you for the help and to everyone else that answered!