CommunicationBeginner
Coin & Rewards System
A Service tracking player currency with a Signal for gain animations and a Property replicating the live total.
SignalPropertyClient MethodsAutomatic Hooks
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Owl = require(ReplicatedStorage.OwlKnit.Owl)
local CoinService = Owl.CreateService({
Name = "CoinService",
Client = {
CoinsChanged = Owl.CreateSignal(),
TotalCoins = Owl.CreateProperty(0),
},
})
CoinService._data = {}
function CoinService:OwlInit()
-- > // Don't need to put something here, its gonna be overwrite by the framework
end
function CoinService:OwlStart()
-- > // Same here
end
function CoinService:OwlOnPlayerAdded(plr: Player)
self._data[plr.UserId] = 0
self.Client.TotalCoins:SetFor(plr, 0)
task.delay(5, function()
if game.Players:GetPlayerByUserId(plr.UserId) then
self:AddCoins(plr, 100)
end
end)
end
function CoinService:OwlOnPlayerRemoving(plr: Player)
self._data[plr.UserId] = nil
end
function CoinService:AddCoins(plr: Player, amount: number)
local current = self._data[plr.UserId]
if current == nil then return end
local new = current + amount
self._data[plr.UserId] = new
self.Client.TotalCoins:SetFor(plr, new)
self.Client.CoinsChanged:Fire(plr, amount, new)
end
function CoinService.Client:GetMyCoins(plr: Player): number
local token = Owl.GetPlrToken(plr)
if not token then return 0 end
return CoinService._data[plr.UserId] or 0
end
return CoinServicelocal ReplicatedStorage = game:GetService("ReplicatedStorage")
local Owl = require(ReplicatedStorage.OwlKnit.Owl)
local CoinController = Owl.CreateController({ Name = "CoinController" })
function CoinController:OwlInit()
self.CoinService = Owl.GetService("CoinService")
end
function CoinController:OwlStart()
self.CoinService.TotalCoins:Observe(function(total)
updateCoinLabel(total)
end)
self.CoinService.CoinsChanged:Connect(function(added, total)
playGainAnimation(added)
end)
self.CoinService:GetMyCoins()
:andThen(function(coins)
print("Coins at login:", coins)
end)
:catch(warn)
end
return CoinControllerWhat's happening here
- OwlOnPlayerAdded fires for players already present at startup, not just future joins no extra GetPlayers() loop needed.
- TotalCoins (a Property) drives the UI via :Observe which replays the current value immediately on connection.
- CoinsChanged (a Signal) fires once per gain the right tool for a one-shot animation, as opposed to persistent state.
- GetMyCoins checks Owl.GetPlrToken before trusting the plr argument, guarding the RemoteFunction against a spoofed call.
Related docs