Skip to content

Providers

This article is a stub. You can help Prvd 'M Wrong by expanding it.

Prvd 'M Wrong is used to create service providers, which provide a specific function for a game. Use providers to compose the top-level logic of a game.

Setup

Prvd 'M Wrong is boring. You get the freedom to structure a game however you like and use whatever complementary libraries you want. This article assumes the following structure, though you are free to divvy it up:

ReplicatedStorage
 └ Packages
    └ prvd
ServerScriptService
 ├ Providers
 └ Server.server.luau
ReplicatedStorage
 └ Packages
    └ prvd
ServerScriptService
 ├ Providers
 └ Server.server.ts

Let's keep the startup code in Server.server.luau .ts. Prvd 'M Wrong games begin with a starting root.

Create a starting root with prvd.root():

Server.server.luau
1
2
3
4
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local prvd = require(ReplicatedStorage.Packages.prvd)

local root = prvd.root()
Server.server.ts
1
2
3
import * as prvd from "@prvdmwrong/core";

const root = prvd.root();

Most Prvd 'M Wrong games store providers inside ModuleScripts. Let's include all descendant modules in the Providers directory through useModules():

Server.server.luau
local root = prvd.root()
    :useModules(script.parent.Providers:GetDescendants())
Server.server.ts
const root = prvd.root()
  .useModules(script.parent.Providers.GetDescendants());

Now Prvd 'M Wrong can be started:

Server.server.luau
4
5
6
local root = prvd.root()
    :useModules(script.parent.Providers:GetDescendants())
    :start()
Server.server.ts
3
4
5
const root = prvd.root()
  .useModules(script.parent.Providers.GetDescendants())
  .start();

That's the bare minimum needed to start a Prvd 'M Wrong game. start() returns a started root which can be stopped by calling stop():

Server.server.luau
-- later when the server shuts down
game:BindToClose(function()
    root:stop()
end)
Server.server.ts
// later, when the server shuts down
game.BindToClose(() => root.stop());

Roots will be further elaborated later. Now, you can make Prvd 'M Wrong's simplest object: providers.

Providers

Prvd 'M Wrong is used to create service providers, which provide a specific function for a game. Use providers to compose the top-level logic of a game.

As your first provider, lets create a provider to track some player session information.

Create a new ModuleScript under the Providers directory called PlayerProvider. Then, create a new table for your provider:

ServerScriptService
 ├ Providers
+│ └ PlayerProvider.luau
 └ Server.server.luau
PlayerProvider.luau
1
2
3
4
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local prvd = require(ReplicatedStorage.Packages.prvd)

local PlayerProvider = {}

To construct a provider, call prvd as if it is a constructor with the provider:

PlayerProvider.luau
4
5
6
local PlayerProvider = {}

return prvd(PlayerProvider)

It's strongly recommended to module return the provider constructed from prvd() so providers can be gradually built up before being returned by the module. This allows Luau to infer useful types and for Prvd 'M Wrong to automatically name it after the ModuleScript name.

Do NOT lazily instantiate a provider with prvd(), this "seals" the provider and will cause type errors:

-- bad!
local PlayerProvider = prvd({})

-- TypeError: Cannot add property 'someFutureProperty' to table '{ }' & '{| loadOrder: ... |}'
PlayerProvider.someFutureProperty = {}

return PlayerProvider

Do NOT instantiate the prvd() in one go, this worsens Luau type safety:

-- bad!
return prvd {
  someFutureMethod = function(self)
    -- self is inferred as `a` here... not very useful!
  end
}

DO gradually build the provider, then wrap the module return with prvd():

-- good!
local PlayerProvider = {}
return prvd(PlayerProvider)

Create a new ModuleScript under the Providers directory called PlayerProvider. Then, create a new class for your provider:

ServerScriptService
 ├ Providers
+│ └ PlayerProvider.ts
 └ Server.server.ts
PlayerProvider.ts
1
2
3
import "@prvdmwrong/core";

export class PlayerProvider {}

To construct a provider, import the Provider class decorator from Prvd 'M Wrong and use it:

PlayerProvider.ts
1
2
3
4
import { Provider } from "@prvdmwrong/core";

@Provider
export class PlayerProvider {}

Games often track player session information, such as when the player joined or the player's leaderstats. Let's define a PlayerSession type, along with an playerSessions property to store each player's session information:

PlayerProvider.luau
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local prvd = require(ReplicatedStorage.Packages.prvd)

export type PlayerSession = {
    joinedAt: number,
    coins: number
}

local PlayerProvider = {}
PlayerProvider.playerSessions = {} :: { [Player]: PlayerSession }

return prvd(PlayerProvider)
PlayerProvider.ts
import { Provider } from "@prvdmwrong/core";

export interface PlayerSession {
  joinedAt: number;
  coins: number;
}

@Provider
export class PlayerProvider {
  playerSessions = new Map<Player, PlayerSession>();
}

Lifecycles

Dependencies

Why Providers?

Luau

TypeScript