Server and client

Mark code server or client and shot check stops each side requiring the other's code, and one name can stand for a module with a version on each side.

A file is shared unless you mark it server or client. shot check reports a require that crosses sides the wrong way, and a split module or diffsource func lets code on either side reach its own version through one name.

Sides

Every file has a side:

class / module / funcboth: shared, runs on server and client (the default)
server class / module / funcserver only
client class / module / funcclient only
@server ... / @client ... scriptthe script's side
split module, diffsource funcboth

What may require what:

both codemay require: both
server codemay require: both, server
client codemay require: both, client

Breaking it is an error at the require: "client code requires server MarketServer (Market/MarketServer.shot)".

The common case: a shared class (plain class) that needs a server-only module gets "both code requires server ...". If the class only makes sense on the server, mark it: server class Till = { ... }.

Everything compiles into ReplicatedStorage.Shot, server files included: classes, modules and funcs as ordinary ModuleScripts. server and client only decide what may require what; nothing is hidden or moved.

Split module

One name for a client module and a server module, so code on both sides, like the two Init scripts, can require one thing.

Market/Market.shot

types script.Parent.Types

split module Market = {
    Client -> script.Parent.MarketClient,
    Server -> script.Parent.MarketServer
}

Market/MarketClient.shot

types script.Parent.Types

client module MarketClient = {
    function Open()
    end
}

Market/MarketServer.shot

types script.Parent.Types

server module MarketServer = {
    function Start()
    end
}

Init/Client.shot

@client enabled

local Market = require(@shot.Market)

Market.Client.Open()
  • On the server, Market.Server is the server module and Market.Client is nil. On the client it is the other way round.
  • A split module named after its folder (Market/Market.shot) is the folder's entry point: require(@shot.Market).
  • The type checker thinks both fields are always there. The nil side is caught only by ShotScript's own rule, in client and server files, when the module is held in a local made by local Market = require(...): a client file using Market.Server gives "client code uses Market.Server". Shared (both) code is not checked; test the side first (RunService:IsServer()) before touching either field there.
  • Entries are separated by a comma. Both lines are needed ("split module Market has no Server -> line"), and each must point at a file ("split Market: nothing at Client -> script.Parent.MarketClien").

Diffsource func

One name for a client func and a server func with the same signature. The value is the client func on the client and the server func on the server.

Utils/UniqueIdentifier/UniqueIdentifier.shot

types script.Parent.Types

diffsource func UniqueIdentifier = {
    Client -> script.Parent.UIDClient,
    Server -> script.Parent.UIDServer
}

The two funcs, each in its own file: client func UIDClient() : string in UIDClient.shot and server func UIDServer() : string in UIDServer.shot.

Checked:

  • both entries are funcs ("diffsource UniqueIdentifier: Client -> script.Parent.UIDClient is not a func"),
  • the Client entry is a client (or both) func and the Server entry a server (or both) func ("diffsource UniqueIdentifier: Client -> UIDServer is a server func"),
  • both have the same parameter and return types ("diffsource UniqueIdentifier: Client is () -> string but Server is () -> number").