> ## Documentation Index
> Fetch the complete documentation index at: https://docs.monolisk.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Working with Roblox Studio

> Essential Roblox Studio concepts and workflows for CORP development

<Note>**Reading time:** \~35 minutes</Note>

This guide covers essential Roblox Studio concepts for developers using CORP, especially those new to the Roblox platform. Understanding these fundamentals will help you build games more effectively with CORP.

## Table of Contents

* [Roblox Studio Basics](#roblox-studio-basics)
* [The Instance Hierarchy](#the-instance-hierarchy)
* [Important Services](#important-services)
* [Data Types](#data-types)
* [Tags System](#tags-system)
* [Client-Server Architecture](#client-server-architecture)
* [Replication](#replication)
* [Streaming](#streaming)
* [Working with the Workspace](#working-with-the-workspace)
* [Studio Workflow with CORP](#studio-workflow-with-corp)

## Roblox Studio Basics

**Roblox Studio** is the development environment for creating Roblox games. It provides a 3D editor, scripting environment, and testing tools.

### Key Concepts

* **Place**: A Roblox game world/level
* **Experience**: A published game (can contain multiple places)
* **DataModel**: The root container for all game objects
* **Instance**: Any object in the game (Parts, Scripts, Models, etc.)

### Opening Your Project

1. Open **Roblox Studio**
2. Create a new place or open an existing one
3. Your compiled CORP code will be synced to the game using roblox-ts

## The Instance Hierarchy

Everything in Roblox is an **Instance** - a hierarchical object with properties and methods. Instances form a tree structure called the **DataModel**.

### Common Instance Types

#### Containers

```
Folder          - Organize instances (no visual representation)
Model           - Group of parts with a primary part
Actor           - Container for parallel execution (used by CORP for GameObjects)
Configuration   - Store configuration data
```

#### Parts and Models

```
Part            - Basic 3D object
MeshPart        - Part with a custom mesh
UnionOperation  - Boolean combination of parts
Model           - Collection of parts
```

#### Scripts

```
Script          - Server-side script
LocalScript     - Client-side script
ModuleScript    - Shared reusable code module
```

#### UI

```
ScreenGui       - Container for 2D UI elements
Frame           - Rectangular UI container
TextLabel       - Display text
TextButton      - Clickable button
ImageLabel      - Display images
```

### Hierarchy Example

```
Workspace               (World container)
├── Baseplate (Part)
├── GameScene (Folder)
│   ├── Player (Actor)
│   ├── Enemy (Actor)
│   └── Props (Folder)
│       ├── Tree (Model)
│       └── Rock (Part)

Players                 (Player management)

ReplicatedStorage       (Shared between server/client)
├── Assets (Folder)
└── Shared (Folder)

ServerStorage           (Server-only storage)

ServerScriptService     (Server scripts)
├── GameServer (Script)

StarterPlayer           
├── StarterPlayerScripts (Client scripts)
│   └── GameClient (LocalScript)
└── StarterCharacterScripts (Per-character scripts)
```

## Important Services

Roblox organizes core functionality into **Services** - singleton instances that manage specific aspects of the game.

### Essential Services

#### Workspace

The 3D world where visible game objects exist.

```typescript theme={null}
import { Workspace } from "@rbxts/services";

const part = Workspace.FindFirstChild("Baseplate") as Part;
```

#### Players

Manages connected players.

```typescript theme={null}
import { Players } from "@rbxts/services";

// Server: when a player joins
Players.PlayerAdded.Connect((player) => {
    print(`${player.Name} joined!`);
});

// Client: get local player
const localPlayer = Players.LocalPlayer;
```

#### ReplicatedStorage

Storage accessible by both server and client. Use for shared assets and modules.

```typescript theme={null}
import { ReplicatedStorage } from "@rbxts/services";

const assets = ReplicatedStorage.FindFirstChild("Assets");
```

#### ServerStorage

Server-only storage. Clients cannot access this.

```typescript theme={null}
import { ServerStorage } from "@rbxts/services";

// Only server can access
const serverAssets = ServerStorage.FindFirstChild("Weapons");
```

#### ServerScriptService

Where server Scripts execute.

```typescript theme={null}
import { ServerScriptService } from "@rbxts/services";

// CORP automatically places server scripts here
```

#### RunService

Access to game loop and environment checks.

```typescript theme={null}
import { RunService } from "@rbxts/services";

// Check if code is running on server or client
if (RunService.IsServer()) {
    print("Running on server");
}

if (RunService.IsClient()) {
    print("Running on client");
}

// Game loop
RunService.Heartbeat.Connect((deltaTime) => {
    // Update every frame
});
```

## Data Types

Roblox has unique data types for 3D game development.

### Vector3

Represents a 3D point or direction.

```typescript theme={null}
// Position
const position = new Vector3(0, 10, 0); // x=0, y=10, z=0

// Direction
const forward = new Vector3(0, 0, -1);

// Operations
const sum = position.add(forward);
const scaled = position.mul(2);
const magnitude = position.Magnitude;
```

### CFrame

Represents position AND rotation (Coordinate Frame).

```typescript theme={null}
// Position only
const cframe = new CFrame(0, 10, 0);

// Position with lookAt
const lookAt = CFrame.lookAt(
    new Vector3(0, 5, 0),  // from
    new Vector3(10, 5, 0)  // to
);

// Rotation
const rotated = cframe.mul(CFrame.Angles(0, math.rad(90), 0));

// Get position from CFrame
const pos = cframe.Position;

// CORP GameObjects use CFrame for transforms
gameObject.setTransform(new CFrame(0, 10, 0));
```

### Color3

RGB color with values from 0 to 1.

```typescript theme={null}
// RGB values (0-1)
const red = new Color3(1, 0, 0);
const green = new Color3(0, 1, 0);
const blue = new Color3(0, 0, 1);

// From 0-255 values
const orange = Color3.fromRGB(255, 165, 0);

// Common colors
const white = new Color3(1, 1, 1);
const black = new Color3(0, 0, 0);
```

### Other Important Types

```typescript theme={null}
// Enum values
const material = Enum.Material.Plastic;
const partType = Enum.PartType.Block;

// Ray for raycasting
const ray = new Ray(origin, direction);

// NumberRange, NumberSequence, etc.
```

## Tags System

Roblox's **CollectionService** allows you to tag instances. CORP uses tags extensively to identify GameObjects and scene roots.

### Installing Tag Editor

1. In Roblox Studio, go to **Plugins** → **Plugin Marketplace**
2. Search for "Tag Editor"
3. Install the official Tag Editor plugin

### Using Tags

```typescript theme={null}
import { CollectionService } from "@rbxts/services";

// Add a tag
CollectionService.AddTag(instance, "GameObject");

// Check if tagged
const hasTag = CollectionService.HasTag(instance, "GameObject");

// Get all instances with tag
const gameObjects = CollectionService.GetTagged("GameObject");

// Listen for tagged instances
CollectionService.GetInstanceAddedSignal("GameObject").Connect((instance) => {
    print(`GameObject added: ${instance.Name}`);
});
```

### CORP Tags

CORP recognizes these special tags:

* **`GameObjectSceneRoot`**: Marks the root folder of a scene in Workspace

### Adding Tags in Studio

1. Select an instance in the Explorer
2. Open the **Tag Editor** plugin (View → Tag Editor)
3. Type the tag name (e.g., "GameObjectSceneRoot")
4. Click the + button or press Enter

## Client-Server Architecture

Roblox games use a **client-server architecture**. Understanding this is crucial for multiplayer games.

### The Server

* **Authoritative**: Makes final decisions about game state
* **Validates**: Checks client inputs for cheating
* **Manages**: Game logic, physics, NPC AI
* **Runs**: `Script` instances in `ServerScriptService`

```typescript theme={null}
import { RunService } from "@rbxts/services";

if (RunService.IsServer()) {
    // This code only runs on the server
    print("Server-side logic");
}
```

### The Client

* **Displays**: Renders the game world
* **Inputs**: Captures player input (keyboard, mouse, touch)
* **Predicts**: Can predict movement for responsiveness
* **Runs**: `LocalScript` instances in player-specific locations

```typescript theme={null}
import { RunService } from "@rbxts/services";

if (RunService.IsClient()) {
    // This code only runs on clients
    print("Client-side logic");
}
```

### When Code Runs Where

```typescript theme={null}
// ModuleScript - Runs wherever it's required
export class SharedLogic {
    public static calculate(value: number): number {
        return value * 2;
    }
}

// In a Script (ServerScriptService)
if (RunService.IsServer()) {
    // Server-only logic
    Players.PlayerAdded.Connect((player) => {
        // Give player tools, load data, etc.
    });
}

// In a LocalScript (StarterPlayerScripts)
if (RunService.IsClient()) {
    // Client-only logic
    const camera = Workspace.CurrentCamera;
    // Handle input, update UI, etc.
}
```

### CORP's Approach

CORP abstracts some of this complexity:

* **NetworkObject**: Automatically replicates GameObjects
* **NetworkBehavior**: Components that sync state
* **RPC**: Easy server-client communication
* **Authority Models**: Choose server or client authoritative

```typescript theme={null}
import { Unreal } from "CORP/shared/unreal";

// Check who has authority
if (Unreal.isAuthority()) {
    // Server (or client in client-authoritative mode)
    this.spawnEnemy();
}
```

## Replication

**Replication** is how Roblox syncs game state between server and clients.

### What Replicates

✅ **Automatically Replicated:**

* Instances in Workspace
* Properties of replicated instances (Position, Size, Color, etc.)
* Instance hierarchy changes (parenting)

❌ **Does NOT Replicate:**

* Local variables in scripts
* Instances in ServerStorage or ServerScriptService
* Client-created instances (unless parent is replicated)

### Replication Direction

```
Server → Clients:    Always works (server is source of truth)
Client → Server:     Never automatic (use RemoteEvents/RemoteFunctions)
Client → Client:     Never (must go through server)
```

### CORP's Network System

CORP handles replication for you:

```typescript theme={null}
@RequiresComponent(NetworkObject)
export class PlayerSync extends NetworkBehavior {
    // Automatically replicates to all clients
    public readonly position = new NetworkedVariable<Vector3>(Vector3.zero);
    
    public onStart(): void {
        if (RunService.IsServer()) {
            // Only server modifies
            this.position.setValue(new Vector3(0, 10, 0));
        } else {
            // Clients automatically receive updates
            this.position.changed.connect((newPos) => {
                print(`Position updated to: ${newPos}`);
            });
        }
    }
}
```

### RemoteEvents and RemoteFunctions

Roblox's built-in communication system (CORP's RPC system uses these internally):

```typescript theme={null}
// Server creates RemoteEvent
const event = new Instance("RemoteEvent");
event.Name = "MyEvent";
event.Parent = ReplicatedStorage;

// Server fires to client
event.FireClient(player, "Hello!");

// Client listens
event.OnClientEvent.Connect((message) => {
    print(message); // "Hello!"
});

// Client fires to server
event.FireServer("Hi from client!");

// Server listens
event.OnServerEvent.Connect((player, message) => {
    print(`${player.Name} says: ${message}`);
});
```

**With CORP RPCs**, this is simplified:

```typescript theme={null}
@RPC.Method({
    endpoints: [RPC.Endpoint.CLIENT_TO_SERVER],
    accessPolicy: RPC.AccessPolicy.OWNER
})
public clientMessage(message: string): void {
    print(`Client says: ${message}`);
}
```

## Streaming

**StreamingEnabled** controls whether the entire game world loads at once or streams in based on player location. CORP is designed to work seamlessly with streaming enabled.

### Streaming Modes

#### StreamingEnabled = false

* **Entire world loads immediately**
* **Best for**: Single-player games, small maps, client-authoritative games
* **Memory**: Higher memory usage (entire map loaded)
* **Performance**: Consistent, but limited by total content size

#### StreamingEnabled = true

* **World streams based on player position**
* **Best for**: Multiplayer games, large open worlds, server-authoritative games
* **Memory**: Lower memory footprint per client
* **Performance**: Better for large-scale games

### CORP's Streaming Support

CORP is **fully compatible with StreamingEnabled**. It provides built-in tools to handle streaming gracefully:

#### NetworkObject Streaming Awareness

```typescript theme={null}
export class MyComponent extends NetworkBehavior {
    private networkObject!: NetworkObject;

    public onStart(): void {
        this.networkObject = this.getComponent(NetworkObject)!;

        // Check if instance is currently streamed in
        if (this.networkObject.isStreamedIn()) {
            print("Instance is loaded");
        }

        // Listen for streaming status changes
        this.networkObject.isLoadedChanged.connect((isLoaded) => {
            if (isLoaded) {
                print("Instance streamed in!");
                this.onInstanceLoaded();
            } else {
                print("Instance streamed out!");
                this.onInstanceUnloaded();
            }
        });
    }

    private onInstanceLoaded(): void {
        // Access Roblox instance safely
        const instance = this.getInstance();
        // Work with instance...
    }

    private onInstanceUnloaded(): void {
        // Clean up any instance-specific references
    }
}
```

#### InstanceNetworkVariable

For networked Instance references, CORP provides `InstanceNetworkVariable` which handles streaming automatically:

```typescript theme={null}
import { InstanceNetworkVariable } from "CORP/shared/networking/instance-network-variable";

@RequiresComponent(NetworkObject)
export class TargetSystem extends NetworkBehavior {
    // Automatically handles streaming for instance references
    public readonly targetPart = new InstanceNetworkVariable<Part | undefined>(undefined);

    public onStart(): void {
        // Check streaming status
        if (this.targetPart.isStreamedIn()) {
            const part = this.targetPart.getValue();
            // Use part...
        }

        // Listen for streaming changes
        this.targetPart.streamingStatusChanged.connect((isStreamedIn) => {
            if (isStreamedIn) {
                print("Target is now available!");
            }
        });

        // Wait for instance to stream in if needed
        task.spawn(async () => {
            const part = await this.targetPart.waitForValue();
            print("Got the part:", part.Name);
        });
    }
}
```

### Recommended Settings

#### For Multiplayer Games (Recommended: StreamingEnabled = true)

```typescript theme={null}
// In Roblox Studio: Set Workspace.StreamingEnabled = true

// Benefits:
// ✅ Better performance for large worlds
// ✅ Lower memory per client
// ✅ Supports massive player counts
// ✅ CORP handles streaming automatically

// Use CORP's streaming-aware features:
// - NetworkObject.isStreamedIn()
// - NetworkObject.isLoadedChanged
// - InstanceNetworkVariable
```

#### For Single-Player Games (StreamingEnabled = false)

```typescript theme={null}
// In Roblox Studio: Set Workspace.StreamingEnabled = false

// Benefits:
// ✅ Simpler development (everything always loaded)
// ✅ No streaming concerns
// ✅ Better for smaller, contained experiences

// Required for client-authoritative games
```

### Setting Streaming

In Roblox Studio:

1. Select **Workspace** in Explorer
2. In Properties panel, find **StreamingEnabled**
3. Set based on your game type:
   * **true** for multiplayer/large worlds
   * **false** for single-player/client-authoritative

### Client-Authoritative Games

> **Important**: For client-authoritative games, `Workspace.StreamingEnabled` **must** be set to `false` to ensure all game content loads immediately. See [Unreal System - Authority Models](./unreal-system#authority-models) for details.

Client-authoritative mode requires the full game state to be available on the client, which is incompatible with streaming.

## Working with the Workspace

The **Workspace** is where your 3D game world exists.

### Basic Operations

```typescript theme={null}
import { Workspace } from "@rbxts/services";

// Create a part
const part = new Instance("Part");
part.Size = new Vector3(10, 1, 10);
part.Position = new Vector3(0, 0, 0);
part.Anchored = true;
part.Parent = Workspace;

// Find instances
const folder = Workspace.FindFirstChild("GameScene") as Folder;
const actor = Workspace.WaitForChild("Player") as Actor;

// Raycasting
const origin = new Vector3(0, 10, 0);
const direction = new Vector3(0, -20, 0);
const raycastResult = Workspace.Raycast(origin, direction);

if (raycastResult) {
    const hitPart = raycastResult.Instance;
    const hitPosition = raycastResult.Position;
}
```

### Workspace Organization

Organize your Workspace for CORP:

```
Workspace/
├── Terrain
├── Baseplate (Part)
├── GameScene (Folder) [Tag: GameObjectSceneRoot]
│   ├── Players (Folder)
│   │   └── SpawnPoint (Actor)
│   │       └── $components (Folder)
│   │           └── spawn_point (ModuleScript)
│   ├── Enemies (Folder)
│   │   ├── Enemy1 (Actor)
│   │   │   ├── $components (Folder)
│   │   │   │   ├── enemy_ai (ModuleScript)
│   │   │   │   └── health_system (ModuleScript)
│   │   │   └── Model (MeshPart or Model)
│   │   └── Enemy2 (Actor)
│   │       └── $components (Folder)
│   │           └── enemy_ai (ModuleScript)
│   └── Environment (Folder)
│       ├── Trees (Folder)
│       └── Buildings (Folder)
```

## Studio Workflow with CORP

### Recommended Development Flow

1. **Set Up Development Tools**
   * Install [VS Code](https://code.visualstudio.com/)
   * Install VS Code extensions:
     * [roblox-ts Extension](https://marketplace.visualstudio.com/items?itemName=Roblox-TS.vscode-roblox-ts)
     * [Rojo Extension](https://marketplace.visualstudio.com/items?itemName=evaera.vscode-rojo)
   * Download and install [Rojo Studio Plugin](https://github.com/rojo-rbx/rojo/releases) (Rojo.rbxm)
     * Place in `%LOCALAPPDATA%\Roblox\Plugins` (Windows) or `~/Documents/Roblox/Plugins` (Mac)

2. **Set Up Project**
   * Clone [CORP-TEMPLATE](https://github.com/MonoliskSoftware/CORP-TEMPLATE)
   * Initialize submodules: `git submodule update --init --recursive`
   * Install dependencies: `npm install`
   * Configure Gamepacks in `GAMEPACKS/` folder

3. **Start Development Servers**
   * Terminal 1: Start Rojo server
     ```bash theme={null}
     rojo serve default.project.json
     ```
   * Terminal 2: Watch TypeScript compilation
     ```bash theme={null}
     npx rbxtsc --watch
     ```
   * In Roblox Studio: Click **Rojo** plugin → **Connect** to `localhost:34872`

4. **Code in TypeScript**
   * Write components in your Gamepack's `src/` folder
   * Export components in `exports/components.ts`
   * Save files to auto-compile and sync to Studio

5. **Build Scenes in Studio**
   * Create Folders and Actors in Workspace
   * Tag the scene root folder with `GameObjectSceneRoot`
   * Add `$components` folders to Actors for component configuration

6. **Test in Studio**
   * Click Play (F5) to test
   * Check Output window for prints and errors
   * Iterate on code and scene design
   * Changes sync automatically when you save TypeScript files

7. **Export Assets**
   * Place models, sounds, UI in your Gamepack's `assets/` folder
   * Reference with `%assets%/path/to/asset`

### Component Configuration in Studio

CORP stores component data inside a special `$components` folder within each Actor. Here's the proper hierarchy:

```
Actor: Player
├── $components (Folder)
│   ├── player_controller (ModuleScript or StringValue)
│   └── health_system (ModuleScript or StringValue)
└── ... other child instances
```

#### Creating Component Configuration

**Method 1: ModuleScript (for complex configuration)**

1. Select your Actor in the Explorer
2. Insert a **Folder** and name it exactly `$components`
3. Inside `$components`, insert a **ModuleScript**
4. Name it with your component's mapping name (e.g., `player_controller`)
5. Set the ModuleScript content to return a configuration table:

```lua theme={null}
-- ModuleScript: player_controller
return {
    speed = 16,
    jumpHeight = 50,
    maxHealth = 100
}
```

**Method 2: StringValue (for JSON configuration)**

1. Inside the `$components` folder, insert a **StringValue**
2. Name it with your component's mapping name (e.g., `enemy_ai`)
3. Set the Value to a JSON string:

```json theme={null}
{
    "attackDamage": 25,
    "moveSpeed": 10,
    "attackRange": 15
}
```

#### Component References

To reference other instances, GameObjects, or components, add **ObjectValue** children to the component and use the `Data.factory` methods:

```
Actor: Enemy
├── $components (Folder)
│   └── target_follower (ModuleScript)
│       └── targetRef (ObjectValue)  ← References another Actor
└── ... other children
```

In the component ModuleScript, use the appropriate `Data.factory` reference type:

```lua theme={null}
-- Reference to an Instance (Part, Model, etc.)
return {
    target = { ["$type"] = "instanceReference", ["$referenceId"] = "targetRef" }
}

-- Reference to a GameObject
return {
    target = { ["$type"] = "gameObjectReference", ["$referenceId"] = "targetRef" }
}

-- Reference to a Component on another GameObject
return {
    target = { ["$type"] = "componentReference", ["$referenceId"] = "targetRef" }
}
```

The `$referenceId` must match the name of the ObjectValue child. CORP will resolve the ObjectValue to the actual Instance/GameObject/Component based on the `$type`.

### Testing Workflow

```typescript theme={null}
// In your component
export class DebugComponent extends Behavior {
    public onStart(): void {
        // Use print for debugging
        print("Component started!");
        print(`GameObject name: ${this.gameObject.getName()}`);
        
        // Warn for non-critical issues
        warn("This is a warning");
        
        // Error for critical issues
        error("This is an error");
    }
}
```

Check the **Output** window in Studio to see your prints.

### Hot Reloading with Rojo

With Rojo and roblox-ts watch mode running:

1. Save your TypeScript file in VS Code
2. roblox-ts compiles automatically (Terminal 2)
3. Rojo syncs changes to Studio (Terminal 1)
4. Stop and restart the play session to see changes

**Benefits:**

* ✅ Real-time code synchronization
* ✅ No manual file copying
* ✅ Work in your favorite code editor
* ✅ Version control friendly (edit source, not .rbxl files)

**Troubleshooting:**

* If changes don't sync, check Rojo connection status (green = connected)
* If compilation fails, check Terminal 2 for TypeScript errors
* Restart Rojo server if connection is lost

## Best Practices

### ✅ Do

* **Enable Streaming** for multiplayer/large world games
* **Disable Streaming** for single-player/client-authoritative games
* **Use CORP's streaming features** (`isStreamedIn()`, `isLoadedChanged`, `InstanceNetworkVariable`)
* **Use Tags** to mark CORP scene roots with `GameObjectSceneRoot`
* **Organize Workspace** with folders and meaningful names
* **Test Frequently** in Studio play mode
* **Use `$components` folders** for component configuration
* **Check RunService.IsServer()** when needed
* **Use CFrame** for GameObject transforms
* **Keep Server Authoritative** for multiplayer

### ❌ Avoid

* **Don't access Roblox instances** without checking `isStreamedIn()` when streaming is enabled
* **Don't use StreamingEnabled = true** with client-authoritative games
* **Don't access Workspace directly** in most CORP code (use GameObjects)
* **Don't create instances manually** when CORP can handle it
* **Don't forget to anchor Parts** in the Workspace (if static)
* **Don't hardcode paths** to instances (use VFS for assets)
* **Don't mix LocalScripts and Scripts** unnecessarily (let CORP handle it)

## Common Issues

### "Scene root not found"

* **Cause**: Missing `GameObjectSceneRoot` tag
* **Fix**: Tag a Folder in Workspace with `GameObjectSceneRoot`

### "Component not spawning"

* **Cause**: Component not exported or name mismatch
* **Fix**: Check `exports/components.ts` for correct mapping

### "Client can't access instance"

* **Cause**: Instance is in ServerStorage or ServerScriptService
* **Fix**: Move to ReplicatedStorage or Workspace

### "Instance not found" (with streaming enabled)

* **Cause**: Instance not yet streamed in to client
* **Fix**: Use `NetworkObject.isLoadedChanged` or `InstanceNetworkVariable.waitForValue()`

### "Streaming error with client authority"

* **Cause**: StreamingEnabled is true with client-authoritative game
* **Fix**: Set `Workspace.StreamingEnabled = false` in Studio (required for client authority)

## Next Steps

Now that you understand Roblox Studio basics:

* **[Getting Started](./getting-started)**: Build your first CORP game
* **[Core Concepts](./core-concepts)**: Learn about GameObjects and Components
* **[Scene Management](./scene-management)**: Create scenes in Studio
* **[Networking](./networking)**: Add multiplayer features
* **[Unreal System](./unreal-system)**: Understand Gamepacks and authority

***

**Master Roblox Studio to build amazing CORP games!** 🎮
