+ Add Server Features

Learn how to extend Stoway by creating new operations, adding API methods, and wiring them to the network protocol.

Overview

How the operations system works

Stoway uses a modular operation system. Each operation (Add, Remove, Swap, Equip, Drop) is a self-contained module that handles a specific inventory action. Adding a new feature involves:

  1. Create the operation module - Handle the core logic
  2. Add an API method - Expose the operation in InventoryService
  3. Wire to network protocol - Allow clients to trigger it
  4. Add replication (optional) - Notify clients of changes
Architecture Overview Diagram
Client Request → InventoryAction (RemoteEvent)
    → OperationHandlers[operation] 
    → OperationModule.Execute()
    → InventoryState updates
    → Replicator.Send*() (optional)
    → Client update

Step 1: Create the Operation Module

Write the core logic for your operation

Create a new file in src/server/StowayServerV1_2/Operations/. Use the template below:

MyOperation.luau Luau
-- src/server/StowayServerV1_2/Operations/MyOperation.luau
-- Description: What your operation does

local MyOperation = {}

-- Type definitions for clarity
export type MyResult = {
    success: boolean,
    reason: string?,
    -- Add your result fields here
}

-- Main execute function
function MyOperation.Execute(
    state: InventoryStateType,
    -- Add your parameters
    param1: string,
    param2: number
): MyResult
    -- 1. Validate inputs
    if not param1 then
        return { success = false, reason = "INVALID_PARAM" }
    end

    -- 2. Perform the operation
    -- Modify state.Items, state.Hotbar, state.Storage as needed

    -- 3. Return result
    return {
        success = true,
        -- Add result fields
    }
end

return MyOperation
💡 Best Practices:
  • Always validate inputs before modifying state
  • Use the existing helpers (SlotManager, LimitChecker, etc.)
  • Return descriptive error messages
  • Keep the operation focused on a single task

Step 2: Add API Method

Expose your operation in InventoryService

Open src/server/StowayServerV1_2/init.luau and add:

init.luau - Imports Luau
-- At the top with other operation imports
local MyOperation = require(script.Operations.MyOperation)
init.luau - API Method Luau
-- Add in the PUBLIC API section

function InventoryService.MyOperation(
    player: Player,
    param1: string,
    param2: number,
    REPLICATE: boolean
)
    local state = PlayerInventories[player]
    if not state then return { success = false, reason = "NO_INVENTORY" } end
    
    local result = MyOperation.Execute(state, param1, param2)
    
    if result.success and REPLICATE then
        -- Add replication if needed
        -- Replicator.SendSomething(player, ...)
    end
    
    return result
end

Step 3: Wire to Network Protocol

Allow clients to trigger the operation

init.luau - Operation Types Luau
-- In the Operations table
local Operations = {
    SWAP = "Swap",
    EQUIP = "Equip",
    UNEQUIP = "Unequip",
    REMOVE = "Remove",
    DROP = "Drop",
    STACK = "Stack",
    -- Add your new operation type
    MY_OPERATION = "MyOperation",
}
init.luau - Operation Handlers Luau
-- In the OperationHandlers table
local OperationHandlers = {
    -- ... existing handlers ...
    
    -- Add your handler
    [Operations.MY_OPERATION] = function(player, args)
        return InventoryService.MyOperation(
            player,
            args.param1,
            args.param2,
            false  -- Replication handled by API method
        )
    end,
}

Step 4: Add Replication (Optional)

Notify clients of changes

If your operation changes visible state, add a replication method. See Replicator.luau for patterns:

Replicator.luau Pattern Luau
-- Add to Replicator module

function Replicator.SendMyOperation(player, -- your data)
    local payload = {
        Operation = "MyOperation",
        -- Your payload data
    }
    InventoryActionRemote:FireClient(player, payload)
end

Example: Craft Operation

Complete working example

Here's a simplified craft operation that consumes ingredients and produces an output item:

CraftOperation.luau Luau
-- src/server/StowayServerV1_2/Operations/CraftOperation.luau

local SlotManager = require(script.Parent.Parent.Core.SlotManager)
local RemoveOperation = require(script.Parent.Parent.Operations.RemoveOperation)
local AddOperation = require(script.Parent.Parent.Operations.AddOperation)

local CraftOperation = {}

export type CraftResult = {
    success: boolean,
    reason: string?,
    outputItem: string?,
}

function CraftOperation.Execute(
    state: InventoryStateType,
    recipeId: string
): CraftResult
    -- Define your recipes (could also be in a separate module)
    local recipes = {
        sword = {
            ingredients = { wood = 2, iron = 1 },
            output = "sword"
        },
        potion = {
            ingredients = { herb = 3, water = 1 },
            output = "potion"
        }
    }

    local recipe = recipes[recipeId]
    if not recipe then
        return { success = false, reason = "UNKNOWN_RECIPE" }
    end

    -- Check ingredients exist
    for itemId, amount in pairs(recipe.ingredients) do
        local count = state.ItemsByID[itemId] and #state.ItemsByID[itemId] or 0
        if count < amount then
            return { success = false, reason = "MISSING_INGREDIENTS" }
        end
    end

    -- Consume ingredients
    for itemId, amount in pairs(recipe.ingredients) do
        for i = 1, amount do
            local uuid = state.ItemsByID[itemId][1]
            RemoveOperation.Execute(state, uuid, 1)
        end
    end

    -- Add output
    local addResult = AddOperation.Execute(state, recipe.output, 1, nil)

    return {
        success = true,
        outputItem = recipe.output
    }
end

return CraftOperation

Next Steps

Related documentation