> For the complete documentation index, see [llms.txt](https://p5yui.gitbook.io/p5yui-framework/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://p5yui.gitbook.io/p5yui-framework/readme.md).

# P5yui's Framework

## Core Concepts

### Services

Services are singleton modules that run exclusively on the server. They handle game logic, player management, data, and anything that must be authoritative.

#### Anatomy of a service

```luau
local MyService = {}

function MyService:init()
    self:require(DataService)   -- declare deps
end

function MyService:ready()
    -- safe to use DataService here
end

function MyService:tick(dt)
    -- runs every heartbeat
end

return MyService
```

### Controllers

Controllers are singleton modules that run exclusively on the client. They handle input, UI state, local effects, and anything that doesn't need to be authoritative, such as animations, sounds, and server communication via remotes.

#### Anatomy of a controller

```luau
local MyController = {}

function MyController:init()
    self:require(UIController) -- declare deps
end

function MyController:ready()
    -- UIController is loaded, safe to use
end

function MyController:tick(dt)
    -- runs every RenderStepped
end

return MyController
```

### Lifecycle

#### :init()

Runs immediately upon client or server startup.

{% hint style="info" %}
Use for dependency declaration and initial setup.
{% endhint %}

#### :ready()

Runs once all dependencies are loaded.

{% hint style="info" %}
Safe for calling methods on required services.
{% endhint %}

#### :tick()

Runs every frame heartbeat, receiving delta time (Δt).

{% hint style="info" %}
Use for frame-by-frame updates like animations or timers.
{% endhint %}

## Systems

### Data System

Built on ProfileStore with a custom wrapper for loading, saving, and updating data. Modified data automatically replicates to the client and caches locally to eliminate redundant `RemoteFunction` calls.

#### Editing the data template

Modify `DataTemplate` to edit a player's starting data.

```luau
local DataTemplate = {
	foo = 1,
	bar = 2
}

return DataTemplate
```

New players or existing players missing these keys will immediately receive these default values upon joining.

#### Getting a player's data

Call `:Get()` via `DataService` to retrieve a player's data.

```luau
function MyService:init()
    self.DataService = require(script.Parent.DataService)
end

function MyService:ready()
    local data = self.DataService:Get(player) -- for a hypothetical player
end
```

#### Updating a player's data

Call `:Update()` via `DataService` to modify a player's data.

```luau
function MyService:init()
    self.DataService = require(script.Parent.DataService)
end

function MyService:ready()
    self.DataService:Update(player, function(data) -- this receives the player's data
        data.foo = 1
        data.bar = "hi"
    end)
end
```

{% hint style="warning" %}
Always use `:Update()` to modify data. This ensures changes correctly replicate to the client.
{% endhint %}

#### Wiping a player's data

Call `:wipe()` via `DataService` to clear a player's data.

```luau
function MyService:init()
    self.DataService = require(script.Parent.DataService)
end

function MyService:ready()
    self.DataService:wipe(player) -- wipes the player if they are online
end
```

Wiping a player resets their data to the `DataTemplate` and replicates the changes to the client.

#### Getting a player's data (on the client)

Use `DataController` to retrieve player data on the client.

```luau
function MyController:init()
    self.DataController = require(script.Parent.DataController)
end

function MyController:ready()
    local foo = self.DataController:Get("foo") -- path to value in data
    print(foo)
end
```

#### Listening to data changes

Connect to `Network.Cache.Event` to listen for changes to specific data keys.

```luau
function MyController:ready()
    Network.Cache.Event:Connect(function(change) -- receives the change
        if change.foo then
            print(change.foo)
        end
    end)
end
```

{% hint style="info" %}
The `change` table matches the structure of `DataTemplate`, but contains only the modified keys.
{% endhint %}

### Stat System

The stat system uses a modifier-based architecture to calculate player attributes dynamically. This allows status effects, equipment, and temporary buffs to scale base values cleanly without permanently altering core player data.

#### Adding a modifier

Register a modifier source entry inside the `collectSources` function within `Shared.StatCalculations`.

```luau
local function collectSources(data: Types.PlayerData)
	local sources = {}	
	
	table.insert(sources,
		{
			target = "foo",
			add = 0,
			mul = 2,
			exp = 0
		}
	)

	return sources
end
```

#### Modifier Configuration Keys

Each source dictionary accepts the following evaluation properties:

* **`target`** — The string identifier of the stat to be modified.
* **`add`** — A flat value added directly to the base stat.
* **`mul`** — A raw multiplier value (e.g., `2` doubles the value, `100` multiplies it by 100).
* **`exp`** — An exponential modifier applied to the total calculation.

#### Getting modifiers

Retrieve modifiers using either a server-isolated service or a shared calculation module.

**Using StatService (Server-Only)**

Call `:GetModifiers()` via `StatService` on the server by passing the target player and the stat path.

```luau
function MyService:init()
    self.DataService = require(script.Parent.DataService)
    self.StatService = require(script.Parent.StatService)
end

function MyService:ready()
    self.DataService:Update(player, function(data)
        local modifiers = self.StatService:GetModifiers(player, "foo") -- passing the path
        local delta = ((1 + modifiers.add) * modifiers.mul) ^ modifiers.exp
        
        foo += delta
    end)
end
```

**Using StatCalculations (Shared)**

Call `:GetModifiers()` via `StatCalculations` on either the client or server by passing the raw data table and the stat path.

```luau
local StatCalculations = require(Shared.StatCalculations)

function MyController:init()
    self.DataController = require(script.Parent.DataController)
end

function MyController:ready()
    local data = self.DataController:Get()
    local modifiers = StatCalculations:GetModifiers(data, "foo") -- passing the path
    local delta = ((1 + modifiers.add) * modifiers.mul) ^ modifiers.exp
    
    foo += delta
end
```

### UI System

The UI system drives user interfaces using tag-based component rendering managed via `UIController`.

#### Components

UIController feature multiple helper functions and everything runs on components. Components have a render function that runs once UIController has fully loaded (dependencies and allat)

**Anatomy of a component**

```luau
local MyComponent = {}

function MyComponent:render(UIController)
    -- MyComponent is loaded, safe to use
end

return MyComponent
```

#### Tag-based Components

Tag-based components listen for specific CollectionService tags to bind modular UI behavior to raw Roblox GUI instances automatically.

**Built-In Components**

* **`DataDependentComponent`** — Automatically updates UI elements when relevant data changes.
* **`HoverClickComponent`** — Applies hover and click animations to GUI instances.
* **`MenuToggleComponent`** — Configures a button to toggle the visibility of a ScreenGui.
* **`RemoteFirerComponent`** — Binds remote-firing functionality directly to a button click.
* **`ScrollableComponent`** — Automatically scales canvas elements and updates a GUI instance's `CanvasSize`.
* **`SliderComponent`** — Binds interactive slider functionality to a Frame.

## Reference

### Layout

The framework strictly organizes server, client, and shared logic within standard Roblox service containers to enforce clean separation of concerns.

```
📁 ServerScriptService
└── 📁 Services
    ├── DataService
        ├── ProfileStore
        └── DataTemplate
    ├── StatService
    └── ...

📁 StarterPlayerScripts
└── 📁 Controllers
    ├── UIController
        ├── DataDependentComponent
        ├── SliderComponent
        └── ...
    ├── DataController
    └── ...

📁 ReplicatedStorage
├── 📁 Network
    ├── 📁 Upgrading
        └── Upgrade
    └── Cache
├── 📁 Shared
    ├── StatCalculations
    ├── Formatter
    └── ...
├── 📁 Templates
    ├── 📁 Inventory
            └── Item
    └── ...
├── 📁 Assets
    ├── 📁 Animations
    ├── 📁 VFX
    └── ...
└── Types
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://p5yui.gitbook.io/p5yui-framework/readme.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
