Places
A Roblox place as an Impostor project and back: shot import <place.rbxl>, shot export <Name>.ssp <place.rbxl>, the libraries an export carries, the scripts it can't write as code, and how each script runs.
A worked example
Garden.rbxl is a place from Studio: a Model Lamp in the Workspace, holding a part Bulb and a switched-off server script Glow, and a server script Welcome in ServerScriptService. These commands make it a project, add a script that uses a Roblox library, and write the game back out as a place:
shot import Garden.rbxl
shot new Kit --kind roblox
shot link Kit.ssp --project Garden.ssp
shot write ServerScriptService/Hello.shot --project Garden.ssp
shot check --project Garden.ssp
shot export Garden.ssp GardenOut.rbxlHello.shot uses Kit, shot new's library, through the link under ReplicatedStorage:
@server enabled
use root.ReplicatedStorage.Kit
print(Kit.Hello())shot import printed:
imported Garden.rbxl into Garden.ssp (an Impostor project, kind Impostor) in 0.2 s:
the .ssp: 5 services (Workspace, ServerScriptService, ReplicatedStorage, StarterPlayer, Lighting), 3 instances, 2 scripts (1 moved out of the world, shown in place)
Garden.world/: 1 Workspace children and Terrain.rbxm, world.json
everything was carried
shot open Garden.sspshot check said 0 errors, and shot export printed:
exported Garden.ssp into GardenOut.rbxl (binary) in 0.1 s:
from the .ssp: 5 services (Lighting, ReplicatedStorage, ServerScriptService, StarterPlayer, Workspace), 5 instances, 3 scripts (1 put back where their markers were)
from Garden.world/: 1 Workspace child and the Terrain
ShotScript: 1 script written compiled (the Luau the place runs, no ShotScript text)
libraries and links: 1 project the code requires, in ReplicatedStorage/Shot (3 modules compiled), each at the path its requires take:
ReplicatedStorage/Shot/Kit (Kit.ssp (the link ReplicatedStorage/Kit))
what shot import listed as not carried (out-of-scope classes, services and properties the project file doesn't keep, RBX attributes, references it couldn't keep) isn't in the project, so it isn't in this place either
- link holders not written (a linked project the code requires is in ReplicatedStorage/Shot) (1): ReplicatedStorage/Kit (Kit.ssp)GardenOut.rbxl holds the game: Glow back inside Lamp and still switched off, Welcome as it was, Kit's modules in ReplicatedStorage/Shot/Kit, and Hello a server Script whose Source is the compiled Luau:
--!strict
local Kit = require(game:GetService("ReplicatedStorage"):WaitForChild("Shot"):WaitForChild("Kit"))
print(Kit.Hello())Import
shot import <place.rbxl | .rbxlx> [<Name>.ssp] makes an Impostor project from a whole place; with no name it is the place's own, Garden.ssp beside Garden.rbxl. The services go into the .ssp as text and the Workspace's children into Garden.world/, one .rbxm each (One-file projects, THE FILE; The .ssp format, IMPOSTOR FILES). Every script stays editable text in the .ssp, one inside a Model in the world too: Glow is Workspace/shot tree, and a marker keeps its place in Lamp.rbxm. What the project file can't keep is listed under "not carried:" in the reply, never dropped without a word, and the export's reply says it isn't in the place either.
Export
shot export <Name>.ssp <place.rbxl | .rbxlx> writes an Impostor project as a place: binary for .rbxl, XML for .rbxlx. Each ShotScript script goes as its compiled Luau, the code the place runs, without its ShotScript text; plain Luau goes as it is. The .ssp stays the source: importing GardenOut.rbxl again gives Hello.server.luau, plain Luau, not Hello.shot. The export writes only a new file, and only from an Impostor project:
GardenOut.rbxl already exists; shot export never writes over a file (pick another name)
Kit.ssp is a Roblox project, not an Impostor; shot export writes the place an Impostor project (shot import <place.rbxl>, shot new --kind impostor) holdsA build error stops it and nothing is written. With use Kit in Hello.shot in place of use root.ReplicatedStorage.Kit, it said:
Garden.ssp has 1 error, so nothing is written (shot build lists the same; fix them, then export again):
ServerScriptService/Hello.shot:2 use Kit: no Kit in this folder or any folder above it (use looks at each folder's own files and folders, up to the project root); it is in ReplicatedStorage: use root.ReplicatedStorage.KitLibraries
Every project the code requires, through a link or a use external line (Externals), goes into the place compiled, and so does each project those require: ReplicatedStorage/
Scripts that can't go as code
A ShotScript script whose compiled Luau can't run in a place (it uses a System project's service or built-in module, or its Luau doesn't compile) is written as its text, which doesn't run, and listed. So is every script that requires it, however far down: through use lines, through a require written by hand (an instance chain from script, game or workspace, or a local bound to one, and require("@Alias") of an external), and from plain .luau files. shot check calls the first one an error, but only a build error stops the export, so read the list. These three, added to Garden.ssp beside an empty ReplicatedStorage/
ReplicatedStorage/Codec.shot
types script.Parent.Types
use JsonService
module Codec = {
function Pack(value : any) : string
return JsonService:Encode(value)
end
}
ServerScriptService/Save.shot
@server enabled
local Codec = require(game:GetService("ReplicatedStorage").Codec)
print(Codec.Pack({ coins = 5 }))
ReplicatedStorage/Raw.luau
local Codec = require(script.Parent.Codec)
return { Go = function() : string return Codec.Pack(1) end }made shot check say
Garden.ssp/ReplicatedStorage/Codec.shot:2:5 JsonService is a System project's service: an Impostor project has noneand the export wrote the place with this note:
- 3 ShotScript scripts not exportable, written as the project file keeps them (their text, which doesn't run):
ReplicatedStorage/Codec: uses JsonService, a System project's service
ReplicatedStorage/Raw (plain Luau, written as it is): uses ReplicatedStorage/Codec, which isn't exportable
ServerScriptService/Save: uses ReplicatedStorage/Codec, which isn't exportableSave and Raw are listed only for their require of Codec. Raw is plain Luau, so it is written as it is either way; its row says it reaches a script that doesn't run.
How each script runs
An export keeps how each script runs. GardenOut.rbxl, and a second export with Off.shot (@server disabled) and StarterPlayer/@client enabled) added, held:
Workspace/Lamp/Glow Script, RunContext Server, switched off plain Luau, as Garden.rbxl had it
ServerScriptService/Welcome Script, RunContext Legacy plain Luau, as Garden.rbxl had it
ServerScriptService/Hello Script, RunContext Server @server enabled
ServerScriptService/Off Script, RunContext Server, switched off @server disabled
StarterPlayerScripts/Cli Script, RunContext Client @client enabledA plain Luau script keeps its RunContext and its Enabled through import and export, since the .ssp keeps both. A .shot's @server or @client line sets them, and disabled after it switches the script off (shot set <script> Enabled false writes that word: One-file projects, THE TREE). A .shot with no side line inside the world keeps the place's (The .ssp format, IMPOSTOR FILES). A module has neither.
In mime
Mime edits an open Impostor project as Studio edits a place: every change below is one undo step, and the place is saved with it.
Mime's Game Settings window reads and sets an open Impostor project's place settings, one undo step a change: Avatar (R6, R15 or PlayerChoice), Gravity, WalkSpeed, UseJumpPower, JumpPower, JumpHeight and HttpEnabled, the properties Studio's Game Settings window sets on StarterPlayer, Workspace and HttpService. A service the place lacks is made by the change that sets it.
Mime's Animation Editor edits an open Impostor project's KeyframeSequences, one undo step a request, and they are saved with the place, so Play runs them. It makes a sequence and sets its Name, Loop and Priority; adds, moves and deletes keyframes; poses each joint with an EasingStyle (Linear, Constant, Elastic, Cubic, Bounce or CubicV2) and an EasingDirection (In, Out or InOut); and adds, moves, renames and deletes KeyframeMarkers, Studio's animation events. A joint is the path of part names from the rig's root (HumanoidRootPart/
The Roblox View's strip has Studio's Material and Color buttons (the Home tab), each split in two: the face sets the pick on the selected parts (each selected part, and every part under a selected Model or folder), and the arrow opens a picker, where a click picks and sets at once. Material sets a base material, Plastic until another is picked, and Color a BrickColor's Color3, Medium stone grey (a new Part's) until another is picked. With no part selected both are greyed, with why.
View > Show Grid shows the grid with snapping off too, and is kept with the snap settings. View > Wireframe Rendering draws each shown part's edges as lines in its colour over the sky; a MeshPart or a union is drawn as its box, where Studio draws its triangles. Both hold for every Roblox View.
The Roblox View draws Studio's constraint details: a constraint's two Attachments and the line between them while the constraint or one of its Attachments is selected, every constraint while Create is making one, and a selected Attachment's dot. It draws joints, ropes, rods, springs and movers alike, each kind in its own tint, yellow when selected or under the pointer, and the lines show through parts, as Studio's do. A WeldConstraint or a NoCollisionConstraint joins two parts, so its line runs from centre to centre. A click on a dot or a line selects that Attachment or constraint.
The Explorer's Play Sound, on a Sound in the world, plays it at its Volume and PlaybackSpeed, looped when it is Looped, and not from where it is; it plays until Stop Sound, another Play Sound or the Sound leaving the world, one Sound at a time. Its file comes from the asset cache, an rbxasset:// one from Studio's own content and a localasset:// one from disk. Nothing is downloaded, so a Sound whose file isn't there has Play Sound greyed, with why.
The one selected GuiObject gets Studio's eight resize handles, and a drag on its body moves it when it is the topmost GUI there and no UIListLayout, UIGridLayout, UIPageLayout or UITableLayout places it. A ghost box and a readout of the offset or the size follow the pointer; Escape cancels, and Alt leaves the press to the camera. An upright object's moving edges and centre snap to its parent's (the screen's, under a ScreenGui) and its siblings' edges and centres within 6 pixels, each drawn as a pink guide; a rotated one doesn't snap. The release is one change of Position and Size, only what moved: the Offsets change and the Scales stay, as Studio's drag does, with the AnchorPoint and the Rotation counted, so the opposite edge stays put. A UIScale above it isn't counted.
In play
Mime's Play runs a place's movers and its BasePart booleans as Roblox does, within these limits.
LinearVelocity, AngularVelocity, VectorForce, AlignPosition and AlignOrientation move the part their Attachment0 is on, on the server (a mover parented in that Attachment, where scripts often put it, counts too): Line, Plane and Vector modes; MaxForce and MaxTorque, and MaxAxesForce and MaxPlanarAxesForce along RelativeTo's axes; ForceLimitsEnabled false, which holds the velocity rigidly; and ReactionForceEnabled and ReactionTorqueEnabled, which push a two-attachment Align's Attachment1 part back. An Align closes its gap at its Responsiveness, at most MaxVelocity or MaxAngularVelocity, and all at once with RigidityEnabled. A part whose mover is removed goes on as it was moving.
The legacy BodyVelocity, BodyPosition, BodyGyro and BodyForce move their parent part a world axis at a time, each axis capped by MaxForce or MaxTorque: BodyVelocity closes the gap to its Velocity at a rate set by P, BodyPosition and BodyGyro are springs (P the stiffness, D the damping), and BodyForce is a force at the centre of mass.
Assumed, not checked against Roblox: LinearVelocity acts at the centre of mass, and VectorForce at Attachment0 (at the centre with ApplyAtCenterOfMass); Responsiveness is an exponential rate a second; AlignType Parallel means pointing the same way; RelativeTo Attachment1 takes that Attachment's axes, not its motion; and BodyVelocity's force is P times its velocity gap.
Not done yet: movers on a living character's root (knockback and dashes: its controller moves it), Torque, BodyAngularVelocity and BodyThrust. A mover parented anywhere but its part or its Attachment doesn't hold the part, which moves as a loose part does.
BasePart:UnionAsync, SubtractAsync and IntersectAsync run Csg.SolidAsync's geometry (Unions, BOOLEANS ON BUFFERS) and give a new unparented UnionOperation, an IntersectOperation from IntersectAsync, with the look of the part they are called on, Csg's MeshData and PhysicsData, and its Size as its InitialSize. A MeshPart's or union's file comes from the asset cache or Studio's own files, never the network, and the options table isn't read. A call that can't be done stops with the method's name and why.
See also
| One-file projects | the .ssp file: making an Impostor project, switching a script off |
| The .ssp format | an Impostor file's exact lines: its Roblox properties, its world, the scripts in it |
| Roblox places | an Impostor in Mime: the place's world in a 3D view, one command to send it back |
| Externals | use external and links: the projects an export carries |
| The shot command | shot link, shot build and the other commands |
