Parent : [[INDEX-FIVEM-GAMEPLAY]]
FiveM - Casino Game Loop Server-Side : mélange Fisher-Yates et callbacks d'argent
Problème
Un jeu de casino (blackjack) doit être entièrement server-authoritative pour éviter le cheating (carte peeking, modification du deck). Le mélange, la distribution, et la gestion de l'argent doivent être contrôlés par le serveur.
Solution
Mélange Fisher-Yates server-side, gestion de table multi-joueurs, callbacks d'argent externes via exports pour découpler la logique de jeu de la logique économique.
local deck = {}
local suits = {'hearts', 'diamonds', 'clubs', 'spades'}
local ranks = {'2','3','4','5','6','7','8','9','10','J','Q','K','A'}
function NewDeck()
deck = {}
for _, suit in ipairs(suits) do
for _, rank in ipairs(ranks) do
table.insert(deck, {suit = suit, rank = rank, value = CardValue(rank)})
end
end
end
function ShuffleDeck()
for i = #deck, 2, -1 do
local j = math.random(i)
deck[i], deck[j] = deck[j], deck[i]
end
end
function DealCard()
return table.remove(deck)
end
-- Callbacks d'argent découplés via exports (à implémenter par l'intégrateur)
local getChipsCallback, takeChipsCallback, giveChipsCallback
exports("SetGetChipsCallback", function(cb) getChipsCallback = cb end)
exports("SetTakeChipsCallback", function(cb) takeChipsCallback = cb end)
exports("SetGiveChipsCallback", function(cb) giveChipsCallback = cb end)
function GetPlayerChips(source)
if getChipsCallback then return getChipsCallback(source) else return 0 end
end
Pourquoi
- Fisher-Yates garantit une distribution uniforme (pas de biais)
- Server-side empêche le peeking de deck côté client
- Callbacks externes permettent d'intégrer n'importe quel système économique (ESX, QBCore, ox_inventory)
Quand l'utiliser
- Jeux de cartes, roulette, machines à sous
- Tout système de jeu avec aléatoire server-authoritative
Quand NE PAS l'utiliser
- Jeux PvP compétitifs (le hasard du deck est une mécanique de casino, pas d'esport)
- Si les callbacks d'argent ne sont pas implémentés → argent free (voir anti-pattern associé)
Exemples
Correct
-- Mélange + distribution + scoring entièrement server-side
RegisterNetEvent('blackjack:placeBet')
AddEventHandler('blackjack:placeBet', function(betAmount)
local src = source
local chips = GetPlayerChips(src)
if chips < betAmount then return end
TakeChips(src, betAmount)
local hand = {DealCard(), DealCard()}
local dealer = {DealCard(), DealCard()}
TriggerClientEvent('blackjack:showHand', src, hand, dealer[1])
end)
Incorrect
-- Le client demande les cartes → trust abuse possible
RegisterNetEvent('blackjack:hit')
AddEventHandler('blackjack:hit', function()
local card = DealCard() -- Serveur distribue
-- MAIS le client peut envoyer l'event sans être à la table
TriggerClientEvent('blackjack:receiveCard', source, card)
end)
Références
- kgv-blackjack/server.lua L1-200
- kgv-blackjack/client.lua L12-30