Modules and funcs
A module is one shared table for the whole game, typed from its declaration. A func is a file that returns one function.
You'll learn a module with state, module inside it, a func, and when to pick which.
Class, module or func
Many objects, each with its own state.
one player's walletOne of it in the whole game.
a registry, a service, a catalogJust one function in a file.
WholeA module with state
Bank keeps every player's wallet, on the server. Its properties are declared with their types and values, its functions beside them:
--!strictlocal Players = game:GetService("Players")local Wallet = require(script.Parent.Wallet)local Whole = require(script.Parent.Whole)local Bank = {}Bank.Wallets = {} :: {[Player]: Wallet.Wallet}Bank.Count = 0function Bank.Open(player: Player) Bank.Wallets[player] = Wallet.new(0) Bank.Count += 1endfunction Bank.Give(player: Player, amount: number) local wallet = Bank.Wallets[player] if not wallet then return end wallet:Add(Whole(amount), "Pickup")endfunction Bank.Start() Players.PlayerAdded:Connect(Bank.Open)endreturn Bank
types script.Parent.Typeslocal Players = game:GetService("Players")use Walletuse Wholeserver module Bank = { doc { Every player's wallet } Wallets : {[Player] : Wallet} = {} Count : number = 0 doc { Opens a wallet for the player } function Open(player : Player) module.Wallets[player] = Wallet.new(0) module.Count += 1 end function Give(player : Player, amount : number) local wallet = module.Wallets[player] if not wallet then return end wallet:Add(Whole(amount), "Pickup") end function Start() Players.PlayerAdded:Connect( module.Open) end}
What it compiles toWallet/Bank.luau
--!strict local Types = require(script.Parent.Types) export type Reason = Types.Reason export type Entry = Types.Entry export type Wallet = Types.Wallet type WalletClass = Types.WalletClass export type Bank = Types.Bank export type Coins = Types.Coins export type Whole = Types.Whole local Players = game:GetService("Players") local Wallet = require(script.Parent.Wallet) local Whole = require(script.Parent.Whole) --[[$server]] local Bank : Bank = {} :: any local module = Bank --[[$doc Every player's wallet ]] Bank.Wallets = {} Bank.Count = 0 --[[$doc Opens a wallet for the player ]] function Bank.Open(player : Player) module.Wallets[player] = Wallet.new(0) module.Count += 1 end function Bank.Give(player : Player, amount : number) local wallet = module.Wallets[player] if not wallet then return end wallet:Add(Whole(amount), "Pickup") end function Bank.Start() Players.PlayerAdded:Connect( module.Open) end return Bank
- A property is
Name : Type = value: every one needs a type and a value. Others read it asBank.Count. - Functions are called with a dot,
Bank.Give(player, 25). There is noself. - Inside, the module is
module:module.Count,module.Open(player). A bareOpen(...)is an error. - Everything in a module is public. What it keeps to itself goes above the declaration, as plain Luau.
serverin front makes it server only (page 8).use WalletfindsWallet.shotbeside it, and{[Player] : Wallet}names the class's type with no import.
A func
A file that is one function is a func. Its type goes into the folder's Types under its name, so Whole is a type too:
--!strict-- Rounds down, and never below 0local function whole(amount: number): number return math.max(0, math.floor(amount))endreturn whole
types script.Parent.Typesdoc { Rounds down, and never below 0 }func Whole(amount : number) : number return math.max(0, math.floor(amount))end
What it compiles toWallet/Whole.luau
--!strict local Types = require(script.Parent.Types) export type Reason = Types.Reason export type Entry = Types.Entry export type Wallet = Types.Wallet type WalletClass = Types.WalletClass export type Bank = Types.Bank export type Coins = Types.Coins export type Whole = Types.Whole --[[$rootdoc Rounds down, and never below 0 ]] local function Whole(amount : number) : number return math.max(0, math.floor(amount)) end return Whole
Tip A func has no members, so the doc { } above it documents the whole file. State it keeps between calls goes in locals above it.
Run it
Init/Server.shot now only starts the bank, and a scenario can reach any module. This one replaces Hello:
@server enabled use @shot.Wallet.Bank Bank.Start()
-- Pickup: a player who joins gets a wallet, and Bank.Give adds coins to it. return function(world) local Bank = world.Server.Require("Wallet.Bank") world:Boot() local ana = world:AddPlayer("Ana") world:Step(0.1) Bank.Give(ana, 25.7) local wallet = Bank.Wallets[ana] world.Logf("Ana has %s", wallet:Describe()) assert(wallet.Balance == 25, "25.7 rounds down to 25") end
==== Pickup [client:Ana print] client started Ana has 25 coins ==== summary Pickup OK (2 lines, 0 errors, 0.1s virtual) 1 of 1 scenario(s) passed
Inside the module, the other functions are reached through module. With a bare Open in Start:
./Wallet/Bank.shot:28 TypeError: Unknown global 'Open'; consider assigning to it first 1 errors in 1 file (7 files checked, 2.2 s)
Add a function Balance(player : Player) : number to Bank that returns the player's balance, or 0 with no wallet, and log it from the scenario with world.Logf("%s", Bank.Balance(ana)).
