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

# Roblox Datatypes

> Schema constructors for vectors, CFrames, colours, instances and enums.

```luau theme={null}
local T = Flux.Types
```

## Vector2

<ResponseField name="T.Vector2()" type="Type<Vector2>">
  Two `f32` components. 8 bytes.
</ResponseField>

```luau theme={null}
local Cursor = Flux.Server("Cursor", { T.Vector2() })
Cursor:FireAllUnreliable(Vector2.new(0.5, 0.25))
```

***

## Vector3

<ResponseField name="T.Vector3()" type="Type<Vector3>">
  Three `f32` components. 12 bytes.
</ResponseField>

```luau theme={null}
local Position = Flux.Server("Position", {
	T.Instance(),
	T.Vector3(),
})

Position:FireAllUnreliable(character, character.PrimaryPart.Position)
```

<Info>
  `f32` gives roughly 7 significant digits, so world coordinates keep sub-millimetre precision
  anywhere inside Roblox's playable volume. The dynamic encoder would have used `f64` and
  spent 24 bytes plus a tag for the same value.
</Info>

***

## CFrame

<ResponseField name="T.CFrame()" type="Type<CFrame>">
  Position, then rotation as an axis vector plus an angle, all `f32`. 28 bytes.
</ResponseField>

```luau theme={null}
local Aim = Flux.Server("Aim", { T.Instance(), T.CFrame() })
Aim:FireAllUnreliable(character, head.CFrame)
```

<Warning>
  Lossy. Encoded with `CFrame:ToAxisAngle()` and rebuilt with
  `CFrame.fromAxisAngle(axis, angle) + position`.

  An axis-angle round trip will not reproduce the original rotation matrix bit-for-bit, and a
  `CFrame` with no rotation has a degenerate axis. Fine for replicating visual transforms, and
  unsuitable where you need exact equality or accumulate the result over many hops.
</Warning>

<Tip>
  If you only need yaw, which is the common case for character orientation, send a
  `T.Vector3()` position and one angle instead of a full `CFrame`:

  ```luau theme={null}
  local Move = Flux.Server("Move", {
      T.Instance(),
      T.Vector3(),
      T.Float32(),   -- yaw in radians
  })
  ```

  16 bytes rather than 40, and lossless for what you actually care about.
</Tip>

***

## Color3

<ResponseField name="T.Color3()" type="Type<Color3>">
  Three `u8` components. 3 bytes.
</ResponseField>

```luau theme={null}
local Tint = Flux.Server("Tint", { T.Instance(), T.Color3() })
Tint:FireAll(part, Color3.fromRGB(255, 128, 0))
```

<Warning>
  Lossy. Each component is `math.floor(c * 255)` and read back with `Color3.fromRGB`.

  Colours that originated as 8-bit RGB survive exactly. Computed values are quantised and the
  `floor` biases downward, so a component of `0.5` comes back as `127/255`, about `0.498`. Do
  not compare received colours for equality.
</Warning>

***

## Instance

<ResponseField name="T.Instance()" type="Type<Instance>">
  A varint index into the packet's instance side-array. 1 to 3 bytes.
</ResponseField>

```luau theme={null}
local Highlight = Flux.Server("Highlight", { T.Instance() })
Highlight:Fire(player, targetPart)
```

<Info>
  Instances never enter the buffer. They are appended to the writer's `insts` array and passed
  as the remote's second argument, so Roblox performs the reference translation natively. Only
  the index is serialised, which means a deeply nested instance costs the same as a top-level
  one.

  Within one batch, repeat references to the same instance are deduplicated to a single array
  entry.
</Info>

<Warning>
  `Read` returns `r.insts[idx]`, which is `nil` when the receiver cannot see the instance,
  whether because it was not replicated, is server-only, or was destroyed before the flush.
  Flux does not error and the argument count still matches, so your callback runs with a `nil`
  in that position.

  Always nil-check received instances:

  ```luau theme={null}
  Highlight:Connect(function(part)
      if not part then return end
      -- safe
  end)
  ```

  Note also that `T.Instance()` cannot encode a deliberate `nil`. Wrap it in
  [`T.Optional`](/api/types/composites#optional) for that.
</Warning>

***

## Enum

<ResponseField name="T.Enum(enum: Enum)" type="Type<EnumItem>">
  A single `u8` index into the enum's item list. 1 byte.
</ResponseField>

<ParamField path="enum" type="Enum" required>
  The enum class, not an item: `Enum.KeyCode`, not `Enum.KeyCode.Space`.
</ParamField>

```luau theme={null}
local Input = Flux.Server("Input", { T.Enum(Enum.KeyCode) })
Input:Fire(player, Enum.KeyCode.Space)

local Team = Flux.Server("Team", { T.Enum(Enum.TeamColor) })
```

The constructor snapshots `enum:GetEnumItems()` at build time and maps each item to its
zero-based position, so lookups on both ends are a single table index.

<Tip>
  This is the largest single saving in the namespace. The dynamic encoder writes an enum as its
  full string name, and `"Enum.KeyCode.Space"` is 19 bytes plus a length prefix and a tag.
  `T.Enum()` is one byte. If you send enums at any volume, schematise them.
</Tip>

<Warning>
  The index is a `u8`, so this only works for enums with 256 or fewer items. Larger enums
  silently wrap: an item at position 300 truncates to 44 and decodes as the wrong item.

  Most Roblox enums are small, but `Enum.KeyCode` and `Enum.Font` are near or past the boundary
  depending on engine version. Check before relying on it:

  ```luau theme={null}
  print(#Enum.KeyCode:GetEnumItems())
  ```

  Use `T.Any()`, which sends the string form, for anything larger.
</Warning>

<Warning>
  The index is positional, so it depends on the order `GetEnumItems` returns. Both sides compute
  this at runtime from the same engine version, so a live client and server always agree. An
  index is not stable across engine versions, though, so never persist a `T.Enum()` byte to a
  DataStore.
</Warning>

## Size comparison

What each of these costs dynamically against schematised:

| Value                | Dynamic | Schema | Saving |
| -------------------- | ------- | ------ | ------ |
| `Vector2`            | 9       | 8      | 1      |
| `Vector3`            | 13      | 12     | 1      |
| `CFrame`             | 29      | 28     | 1      |
| `Color3`             | 4       | 3      | 1      |
| `Instance`           | 2 to 4  | 1 to 3 | 1      |
| `Enum.KeyCode.Space` | 21      | **1**  | 20     |

<Info>
  The vector and CFrame constructors are already `f32`-based in both paths, so the schema only
  removes the tag byte. Enums are the outlier, and `Float32` over an implicitly-`f64` bare
  number is the other.
</Info>

## Next steps

<CardGroup cols={2}>
  <Card title="Composites" icon="layer-group" href="/api/types/composites">
    Nesting these into optionals, arrays and structs.
  </Card>

  <Card title="Serialization" icon="binary" href="/concepts/serialization#lossy-conversions">
    Every lossy conversion in one place.
  </Card>
</CardGroup>
