One-file projects
Keep a whole project in one readable text file, <Name>.shotsystem, that you can diff, edit by hand and run with shot run.
A .shotsystem file holds every folder, script, value and scenario of a project: one line per instance, one tab per level, and each script's text as it is. shot new Wallet.shotsystem makes an empty one:
shotsystem 1
id 15382021e71e627f423aae6d70735f91A whole project
Read a project top to bottom, like a tree of folders. This one is whole: a module and its types, a value with an attribute and a tag, and a scenario that plays the module.
Wallet.shotsystem
shotsystem 1
export Wallet.Bank
id 15382021e71e627f423aae6d70735f91
Folder Wallet #c27ea5a5
Script Bank #3f9a1c2e
source
types script.Parent.Types
module Bank = {
doc { Coins held }
Balance : Coins = 0
doc { Adds coins and returns the new balance }
function Deposit(amount : Coins) : Coins
module.Balance += amount
return module.Balance
end
}
StringValue Currency #55aa0f31
Value = "Coins"
attribute Max = 20
tag Tuning
Script Types #9e21b7c4
source
public type Coins = number
scenario Deposit #0b5e7d13
source
local Bank = require(root.Wallet.Bank)
assert(Bank.Deposit(5) == 5, "the first deposit gives 5")
print("balance", Bank.Balance)shot tree --project Wallet.shotsystem prints what it holds:
Wallet.shotsystem
./ (the project)
Wallet/
Bank.shot
Currency (StringValue)
Types.shot
Scenarios/ (never part of the program)
Deposit.luauand shot run Wallet.shotsystem Deposit plays the scenario:
ok Wallet:Scenarios.Deposit
balance 5
1 of 1 scenario(s) passedThe header
Say what the file is and what the project offers in its first lines. The header is every line up to the first empty line: shotsystem 1 comes first, then export Wallet.Bank, the one script or module the project exports, as a dotted path from the project root (see THE EXPORT below). shot write can't change the header, so you edit these lines by hand, while shot doesn't have the project open (see WHILE SHOT HAS IT OPEN below).
The id line
The project carries its own id. After the empty line comes id and the id shot new made: leave it as it is. A file without one gets a new one the next time it is saved.
Lines with no indent between the id line and the first instance belong to the project root itself:
Cart.shotsystem
shotsystem 1
id 15382021e71e627f423aae6d70735f91
attribute Version = 2
tag Draft
Folder Cart #4f1d8e2ashot props . --project Cart.shotsystem reads them:
attributes:
Version = 2 (number)
tags: DraftInstances
Every object of a project is one line you can read at a glance: its class, its name and its id.
StringValue Currency #55aa0f31One tab deeper come the instance's own lines, in this order:
- 1. its
Value, on the value classes (see THE CLASSES below); - 2. its
attributelines; - 3. its
taglines; - 4. a script's
sourceblock; - 5. its children, each an instance line of its own.
A name with spaces or signs goes in quotes, and so does such a tag:
Folder "Daily notes" #70000001
StringValue First #70000002
Value = "Buy milk"
tag "To do"The classes
Build a project from folders, scripts and values. These are the classes, with the one property a value class has, Value:
Folder | a folder |
Script | ShotScript; the tools show it as <Name>.shot |
Luau | plain Luau; the tools show it as <Name>.luau |
StringValue | Value = "Coins" |
NumberValue | Value = 1.5 |
IntValue | Value = 99, a whole number |
BoolValue | Value = true |
ObjectValue | Value = #8b03e5f1, another instance by its id, or Value = root, the project root |
Color3Value | Value = Color3(0.2, 0.4, 1) |
Vector3Value | Value = Vector3(10, 0, 10) |
A Value line is written only when it isn't the default: "", 0, false, no instance, Color3(0, 0, 0) or Vector3(0, 0, 0). The other classes have no properties in the file: keep anything else on them as attributes.
Attributes and tags
Hang any setting on any instance, and mark instances with tags. attribute <Name> = <value> sets an attribute, and the value's form is its type:
"My shop" | text, in double quotes, escaped as in JSON |
0.25 | a number |
true | true or false |
Vector2(4, 8) | a Vector2 |
Vector3(0, 5, -2) | a Vector3 |
Color3(1, 0.5, 0) | a Color3, each part from 0 to 1 |
UDim(0, 6) | a UDim: scale, offset |
UDim2(0.5, 0, 1, -10) | a UDim2: x scale, x offset, y scale, y offset |
CFrame(1, 2, 3) | a CFrame at that position |
NumberRange(1, 10) | a NumberRange: min, max |
Rect(0, 0, 64, 32) | a Rect: x0, y0, x1, y1 |
BrickColor(21) | a BrickColor, by its number |
tag <Tag> adds a tag, and tags keep the order you write them in. A value class's Value takes the same forms.
Settings.shotsystem
shotsystem 1
id 6a0e3c95d17b4f28b9e1c04d5a7f3e62
Folder Settings #4d2a91c7
attribute Title = "My shop"
attribute Rate = 0.25
attribute Open = true
attribute Offset = Vector2(4, 8)
attribute Spawn = Vector3(0, 5, -2)
attribute Tint = Color3(1, 0.5, 0)
attribute Pad = UDim(0, 6)
attribute Size = UDim2(0.5, 0, 1, -10)
attribute Where = CFrame(1, 2, 3)
attribute Range = NumberRange(1, 10)
attribute Box = Rect(0, 0, 64, 32)
attribute Brick = BrickColor(21)
tag Saved
StringValue Currency #8b03e5f1
Value = "Coins"
ObjectValue Home #e7c1290a
Value = #8b03e5f1
ObjectValue Top #19f4b6d3
Value = rootshot props Settings --project Settings.shotsystem reads them back:
Settings (Folder)
attributes:
Box = 0, 0, 64, 32 (Rect)
Brick = Bright red (BrickColor)
Offset = 4, 8 (Vector2)
Open = true (boolean)
Pad = 0, 6 (UDim)
Range = 1, 10 (NumberRange)
Rate = 0.25 (number)
Size = {0.5, 0}, {1, -10} (UDim2)
Spawn = 0, 5, -2 (Vector3)
Tint = 1, 0.5, 0 (Color3)
Title = My shop (string)
Where = 1, 2, 3 (CFrame)
tags: Saved
children (3): Currency, Home, Top (shot tree Settings)Scripts as plain text
Read and diff a script right in the project file. Under source, every line of the script sits one tab deeper than the source line, as it is: nothing is escaped or quoted, and an empty line stays empty. A Script holds ShotScript (see Overview), a Luau plain Luau. When the text doesn't end with a newline, the line says so:
Luau Answer #a4b9d6e1
source (no newline at end)
return 42Scenarios
Keep the code that plays a project inside the project. A scenario is a line scenario <Name> #<id> with no indent, after the instances, and its source block of Luau. It is never part of the program: only shot run plays it, and its code sees script, require and root, the project root, so require(root.Wallet.Bank) reaches the module in the whole project above (see Running a project).
The export
Give a project one thing to offer: a program to run, or a library its scenarios play. The header's export line names it (shot new writes none: add it by hand), and its kind decides what shot run does:
- A program is a
Luaumodule, or aScriptthat declares no class, module or func.shot runruns it once, printing as it goes. - A library is a
Scriptthat declares a module, class or func.shot runlists its scenarios, andshot run Wallet.shotsystem Deposit(orA,B, orall) plays them.
Greeter.shotsystem
shotsystem 1
export Greeter.Main
id 9c4b2e7f0a1d4e8b8f6a3c5d7e9b1f20
Folder Greeter #d1e6a3b8
StringValue Greeting #e2f7b4c9
Value = "Hello"
Luau Main #f3a8c5d0
source
local greeter = script.Parent
local function greet(name : string) : string
return greeter.Greeting.Value .. ", " .. name
end
print(greet("Ada"))
Luau Answer #a4b9d6e1
source (no newline at end)
return 42shot run Greeter.shotsystem prints:
Hello, Adaand shot run Wallet.shotsystem, on the library of the whole project above, lists:
Wallet.shotsystem exports the library Wallet.Bank: it runs through its scenarios (shot run <file> <name>, <A,B> or all)
DepositAn export that isn't there, or isn't a script or a module, is refused. The path is dotted: export Wallet/Bank is refused the same way.
Wallet.shotsystem exports Wallet.Bnk, and it has no Wallet.Bnk
Wallet.shotsystem exports Wallet.Currency, a StringValue: an export is a script or a moduleId kinds
Rename an instance and whatever points at it still finds it: the id stays with the instance. Every instance line ends in # and 8 lowercase hex digits, and the tools make them: shot create, and shot write of a new file, give the new instance one, and shot import gives a whole folder fresh ones.
In the Settings project above, shot create IntValue Settings Limit --project Settings.shotsystem adds
IntValue Limit #85329efcand after shot set Settings/Currency Name Money --project Settings.shotsystem the ObjectValue Home still points at it, by its id:
ObjectValue Home #e7c1290a
Value = #8b03e5f1
IntValue Limit #85329efc
StringValue Money #8b03e5f1
Value = "Coins"shot props Settings/Home --project Settings.shotsystem:
Settings/Home.instance (ObjectValue)
Value = Settings/MoneyWhen you write lines by hand, leave the id off a new one. When the file is saved, a line without an id gets one, a line whose id an earlier line already has gets a new one, and an ObjectValue pointing at an id no line has is cleared. So to copy instances, copy their lines: the copy gets new ids.
Orders.shotsystem, as written
shotsystem 1
id 15382021e71e627f423aae6d70735f91
Folder Orders #a41c07e2
Folder Cart #b52d18f3
IntValue Items #c63e29a4
Value = 2
Folder Cart2 #b52d18f3
IntValue Items #c63e29a4
Value = 2Orders.shotsystem, after shot tree --project Orders.shotsystem
shotsystem 1
id 15382021e71e627f423aae6d70735f91
Folder Orders #a41c07e2
Folder Cart #b52d18f3
IntValue Items #c63e29a4
Value = 2
Folder Cart2 #29ac9c90
IntValue Items #88b85b53
Value = 2As folders and files
Use shot's file commands on a project as on a folder on disk. They see it as folders and files (shot props shows an instance's lines):
Folder | a folder |
Script | <Name>.shot |
Luau | <Name>.luau |
any other instance | <Name>.instance: its class on line 1, then its own lines without the indent |
a scenario | Scenarios/<Name>.luau |
the header | .shotsystem at the root |
shot map --project Wallet.shotsystem lists them:
Shot project Wallet.shotsystem: 2 files in 1 folders
Wallet/
Bank.shot module Bank
Types.shot types public Coins
Currency.instance StringValueshot export Wallet.shotsystem out writes them as real files:
out/
.shotsystem
Scenarios/
Deposit.luau
Wallet/
Bank.shot
Currency.instance
Types.shotout/
StringValue
Value = "Coins"
attribute Max = 20
tag Tuningshot import out Copy.shotsystem makes a new project file from such a folder, header included. The trip loses some things: shot export leaves out a Folder's attributes and tags and the project root's, and shot import gives every instance a new id, so an ObjectValue's Value no longer points anywhere and is cleared. The Settings project above comes back with no attributes or tag on Settings and no Value on Home. shot import won't write over a file:
Copy.shotsystem already exists (export it or pick another name)Writing an .instance file changes its instance in place and keeps its id. With that text, but Value = "Gems" and attribute Max = 50, in currency.txt,
shot write Wallet/Currency.instance --input currency.txt --project Wallet.shotsystemreplies
replaced Wallet/Currency.instance (4 lines)
check:
0 errors (2 files checked, 0.1 s)and the project file then holds
StringValue Currency #55aa0f31
Value = "Gems"
attribute Max = 50
tag TuningWriting it by hand
Write the lines in any order and let shot tidy them. A file you write may list children in any order, put an instance's own lines in any order before its children, leave empty lines between instances and end its lines in CRLF. Whenever shot saves the file, after a change or right after opening a file it had to fix, it writes the tidy form; a file that is only out of order stays as you wrote it until the next change.
Shop.shotsystem, as written (with CRLF line ends)
shotsystem 1
id 2f8c6a1e9d0b47c3a5e4f6b7c8d9e0a1
Folder Shop #a41c07e2
NumberValue Price
attribute Sale = true
Value = 4.5
attribute Label = "Hat"
IntValue Stock #b52d18f3
Value = 0
Folder Cart #b52d18f3
BoolValue Open #c63e29a4
tag Saved
Value = trueShop.shotsystem, after shot tree --project Shop.shotsystem
shotsystem 1
id 2f8c6a1e9d0b47c3a5e4f6b7c8d9e0a1
Folder Shop #a41c07e2
Folder Cart #2d73e0ee
BoolValue Open #c63e29a4
Value = true
tag Saved
NumberValue Price #65692af1
Value = 4.5
attribute Label = "Hat"
attribute Sale = true
IntValue Stock #b52d18f3- Children are sorted by name. Under each instance come its
Value, then its attributes by name, then its tags as written. - The empty lines are gone, and every line ends in LF.
- Stock's
Value = 0is the default, so it is left out. - Price had no id and got one. Cart repeated Stock's id, so Cart, the later line, got a new one.
A source block keeps its text exactly, so a script typed with CRLF line ends keeps them. Next to the file, shot also keeps dated copies of it in Shop.history/, in the same format (shot history lists them).
While shot has it open
Edit the file by hand while shot doesn't have the project open, and every edit is read. A command with --project, or shot export, opens the project, and it stays open for the commands after it: they read and change the open copy, and save it to the file after each change. So while it is open, shot doesn't see a hand edit, and its next change writes over it. shot run Wallet.shotsystem reads the file without opening the project.
Change Currency's Value = "Coins" to "Gems" in an editor while the whole project above is open, and shot props Wallet/Currency --project Wallet.shotsystem still says
Wallet/Currency.instance (StringValue)
Value = Coinsthen shot tag Wallet/Currency Shown --project Wallet.shotsystem saves the open copy, and the file says Value = "Coins" again.
To hand-edit a project that is open, close it first: shot help lists the command that stops it, under ShotSystem files. Then edit the file, and the next command reads your edit. While the project is open, change an instance or a script by writing its file with shot write (see AS FOLDERS AND FILES above). The header has no such command, so edit it while the project is closed.
Mistakes
Find a slip by its line number. A file with a mistake is refused whole, with the line and what was expected, and stays as it is: shot run and every command with --project refuse it. The messages:
line 1: a ShotSystem file starts with the line `shotsystem 1`
line 5: lines are indented with tabs, not spaces
line 5: indented 2 tabs, but this line can be indented at most 1 (one more than the instance it belongs to)
line 4: expected a name after Folder (a name with spaces or signs is written in quotes, like "My name")
line 4: expected #id after the name (# and 8 lowercase hex digits, like #3f9a1c2e; a name with spaces is written in quotes)
line 6: StringValue.Value takes a string ("text"); expected a value: "text", a number, true, false, Vector3(1, 2, 3) and the like
line 6: IntValue.Value takes a whole number
line 6: a StringValue has no property Size (it has Value)
line 5: a Folder has no property Value
line 5: attribute Open takes a value: "text", a number, true, false, Vector3(1, 2, 3) and the like
line 5: attribute Spawn takes Vector3(x, y, z)
line 6: Folder Wallet (line 4) has children above this line; an instance's properties, attributes, tags and source go before its children
line 8: Luau Main has a second source