Container (IoC)
The container is the dependency injection system at the heart of Shiva. It resolves dependencies, manages lifetimes, and decouples modules from each other.
The Problem It Solves
In traditional FiveM resources, everything is global:
-- Old way: direct dependency
local QBCore = exports['qb-core']:GetCoreObject()
QBCore.Functions.GetPlayer(source)If qb-core renames or changes that function, everything breaks. There's no seam to insert a different implementation.
Shiva's container solves this:
-- Shiva way: depend on a contract
local player = Container:make('IPlayer')
player:get(source)The container decides which IPlayer implementation to give you. You don't know or care if it's shiva-player or your custom replacement.
Binding
A module binds its service to a contract in init():
-- Binding a singleton (one instance, reused everywhere)
container:bind('IPlayer', PlayerService)
-- Binding a factory (new instance every time)
container:bind('IPlayer', PlayerService, { factory = true })
-- Binding a value (a plain table, not instantiated)
container:instance('IConfig', Config)Resolving
Other modules resolve their dependencies in start():
-- Resolve a single binding
local player = container:make('IPlayer')
-- Resolve all bindings for a contract (for plugin-style systems)
local allHooks = container:makeAll('IEconomyHook')Lifetimes
| Lifetime | Behaviour |
|---|---|
singleton (default) | One instance created, returned for all make() calls |
factory | New instance created on each make() call |
instance | A pre-existing value, never instantiated |
Checking Bindings
if container:has('IInventory') then
local inv = container:make('IInventory')
endUse has() when a dependency is optional.
Error Handling
If you call container:make('IPlayer') and nothing is bound to 'IPlayer', Shiva throws a ContractNotBound error with a clear message listing what's missing and which module was expected to provide it.