s&box Skill — Router
READ BEFORE WRITING CODE
s&box is not Unity. MonoBehaviour, Start(), Update(), GetComponent<T>() call sites, Instantiate(), Destroy(gameObject), Debug.Log, [SerializeField], Input.GetKey(), Physics.Raycast — none of these exist. If you write any of them you have hallucinated.
s&box is a C# scripting layer on Source 2 by Facepunch. The scene system uses GameObject + Component with a different lifecycle, a different networking model, and a different API surface. The API schema in references/api-schema-core.md is ground truth; if anything in this skill contradicts the schema, the schema wins.
Before you write a single line of s&box code, open the relevant reference file. SKILL.md is a router — it points you at the answer, it does not contain the answer. Writing a component? Open references/core-concepts.md. Writing UI? Open references/ui-razor.md. No exceptions for "simple" tasks — your muscle memory is wrong.
Architecture in 30 Seconds
Scene (is-a GameObject — the root)
└── GameObject (transform, tags, children, components)
└── Component (all gameplay code extends this)
- All gameplay code is a
sealed classextendingSandbox.Component. - Lifecycle overrides are
protected override void OnAwake() / OnStart() / OnUpdate() / OnFixedUpdate() / OnEnabled() / OnDisabled() / OnDestroy(). Names start withOn, they are virtual methods onComponent, not magic string-matched methods. - Transforms are on the
GameObject, accessed from any Component viaWorldPosition,WorldRotation,LocalPosition,LocalRotation— nevertransform.position. - UI is Razor (
.razorfiles) — HTML + SCSS + C#. Panels use flexbox layout. Hot-reloads in the editor. - Networking is owner-authoritative. Mark state with
[Sync], mark methods with[Rpc.Broadcast / Host / Owner]. Skip simulation on non-owners withif ( IsProxy ) return;. - Physics uses
Rigidbody+Collidercomponents. Raycasts areScene.Trace.Ray(from, to).Run()— a builder-pattern API. Collisions come throughComponent.ICollisionListenerandComponent.ITriggerListenerinterfaces. - Coordinate system is Z-up:
Vector3.Forward = (1,0,0),Vector3.Right = (0,-1,0),Vector3.Up = (0,0,1). - Restricted .NET:
System.IO.File, raw sockets,Console,Thread,Process— all blocked. UseFileSystem.Data,Http,Log,async/await.
Routing Table — "I need to…"
Match the task, open the file. Do not guess; open the file.
| Task | Read |
|---|---|
| Understand the scene/GameObject/Component model | references/core-concepts.md |
Write a Component (lifecycle, [Property], Tags, async) | references/core-concepts.md |
| Spawn / clone / destroy a prefab | references/core-concepts.md → Prefabs |
Fire a scene event (ISceneEvent<T>) | references/core-concepts.md → Scene Events |
Write a GameObjectSystem | references/core-concepts.md → GameObjectSystem |
Use ModelRenderer / SkinnedModelRenderer / bones / animgraph | references/components-builtin.md → Rendering |
Use Rigidbody, any Collider, joints | references/components-builtin.md → Physics |
Use CharacterController for movement | references/components-builtin.md → CharacterController |
Use the full PlayerController (built-in FPS/TPS) | references/components-builtin.md → Gameplay |
| Set up a camera, HUD painter, post-processing | references/components-builtin.md → Camera, Post-Processing |
| Use lights, fog, envmap probes, skybox | references/components-builtin.md → Lighting, Environment |
Add audio (SoundPointComponent, Sound.Play) | references/components-builtin.md → Audio |
Use NavMeshAgent, NavMeshLink, query NavMesh | references/components-builtin.md → Navigation |
| Create particles, decals, trails, beams | references/components-builtin.md → Effects, Rendering |
Write a Razor UI panel (.razor, PanelComponent, BuildHash) | references/ui-razor.md |
Style with SCSS — flexbox, transitions, :intro / :outro, :bind | references/ui-razor.md → Styling, Layout System, Transitions |
Use built-in controls (Button, TextEntry, DropDown, VirtualList) | references/ui-razor.md → Built-in Controls |
| Build a world-space panel or a NavigationHost app | references/ui-razor.md → WorldPanel, Navigation |
Set up a lobby, connect/disconnect, query Connection | references/networking.md → Lobby & Connection |
Network an object (NetworkMode, NetworkSpawn, ownership) | references/networking.md → Networked Objects, Ownership |
Use [Sync] / [Sync(SyncFlags.X)] / [Change] / NetList / NetDictionary | references/networking.md → [Sync] Properties |
Write RPCs ([Rpc.Broadcast/Host/Owner], NetFlags, caller info, filtering) | references/networking.md → RPC Messages |
React to connections (INetworkListener, INetworkSpawn, snapshot data) | references/networking.md → Network Events |
Use ISceneStartup for host vs client initialization | references/networking.md → Scene Startup |
Dedicated server / #if SERVER / user permissions | references/networking.md → Dedicated Servers |
| Poll keyboard/mouse/controller input, haptics, glyphs | references/input-and-physics.md → Input |
| Raycast / sphere / box / capsule trace with tag filters | references/input-and-physics.md → SceneTrace |
Access PhysicsWorld, gravity, physics events | references/input-and-physics.md → Physics World |
| Implement collision/trigger listeners | references/input-and-physics.md → Collision System |
Use Vector3 / Rotation / Angles / Transform / BBox / Ray / Capsule | references/input-and-physics.md → Math Types |
Use Time.Now, Time.Delta, TimeSince, TimeUntil | references/input-and-physics.md → Time |
Draw debug gizmos (DrawGizmos, Gizmo.Draw) | references/input-and-physics.md → Gizmo |
Need the full signature of GameObject, Component, Scene, Input, etc. | references/api-schema-core.md |
| Look up whether a given type exists & what it does | references/api-schema-extended.md |
| See a complete worked example of a pattern before writing your own | references/patterns-and-examples.md |
Unity → s&box Translation Table
Any time you write one of the left column, you are hallucinating. Use the right column.
| Unity / Wrong | s&box / Correct |
|---|---|
class Foo : MonoBehaviour | public sealed class Foo : Component |
void Awake() | protected override void OnAwake() |
void Start() | protected override void OnStart() |
void Update() | protected override void OnUpdate() |
void FixedUpdate() | protected override void OnFixedUpdate() |
void OnEnable() / OnDisable() | protected override void OnEnabled() / OnDisabled() |
void OnDestroy() | protected override void OnDestroy() |
[SerializeField] float speed | [Property] public float Speed { get; set; } |
[HideInInspector] | [Hide] |
transform.position | WorldPosition (or GameObject.WorldPosition) |
transform.localPosition | LocalPosition |
transform.rotation | WorldRotation |
transform.forward | WorldRotation.Forward |
gameObject.SetActive(false) | GameObject.Enabled = false |
Destroy(gameObject) / Destroy(this) | GameObject.Destroy() / Component.Destroy() / DestroyGameObject() |
Instantiate(prefab, pos, rot) | prefab.Clone( pos, rot ) |
Instantiate(prefab); NetworkServer.Spawn(...) | prefab.Clone(pos).NetworkSpawn( owner ) |
GetComponent<T>() in Start/Update | GetComponent<T>() is fine; also Components.Get<T>( FindMode ) for ancestor/descendant searches |
FindObjectOfType<T>() / FindObjectsOfType<T>() | Scene.Get<T>() / Scene.GetAll<T>() / Scene.GetAllComponents<T>() |
| `GameObject.Find("Nam |