@hexium-softworks/inputservice
v1.2.0
Published
Game-agnostic IAS wrapper
Readme
InputService
A game-agnostic Nevermore package for Roblox's Input Action System.
InputService creates and edits real InputContext, InputAction, and
InputBinding instances in ReplicatedStorage.Inputs. The server owns shared
defaults, clients listen to actions and make local presentation changes, and no
client input is treated as server authority by this package.
Installation
pnpm add @hexium-softworks/inputserviceThis package expects a Roblox runtime that can create InputContext,
InputAction, and InputBinding instances.
Model
InputService mirrors Roblox's Input Action System hierarchy:
ReplicatedStorage
Inputs
PlayContext
Sprint
Keyboard
Gamepad
Jump
Keyboard
MenuContext
Confirm
BackInputContextgroups actions and controls priority, sinking, and enablement.InputActionrepresents something the player can do, such as sprint, jump, confirm, or open inventory.InputBindingdescribes how an action is triggered, such as a key, UI button, directional set, pointer index, or scriptable binding.
Server code should create shared gameplay defaults. Client code should connect to signals, enable or disable local presentation contexts, add local-only bindings, and fire scriptable bindings for UI or custom local input.
Server Usage
Register one context:
local InputService = require("InputService")
local inputService = serviceBag:GetService(InputService)
inputService:RegisterContext({
Name = "PlayContext",
Enabled = true,
Priority = 2000,
Sink = true,
Actions = {
{
Name = "Sprint",
DisplayName = "Sprint",
Type = Enum.InputActionType.Bool,
Bindings = {
{
Name = "Keyboard",
KeyCode = Enum.KeyCode.LeftShift,
DisplayName = "Left Shift",
},
{
Name = "Gamepad",
KeyCode = Enum.KeyCode.ButtonL3,
DisplayName = "L3",
},
},
},
},
})Register multiple contexts at startup:
inputService:RegisterContexts({
{
Name = "Gameplay",
Enabled = true,
Priority = 2000,
Sink = false,
Actions = {
{
Name = "Sprint",
DisplayName = "Sprint",
Type = Enum.InputActionType.Bool,
Bindings = {
{
Name = "Keyboard",
KeyCode = Enum.KeyCode.LeftShift,
},
{
Name = "Gamepad",
KeyCode = Enum.KeyCode.ButtonL3,
},
},
},
{
Name = "Jump",
DisplayName = "Jump",
Type = Enum.InputActionType.Bool,
Bindings = {
{
Name = "Keyboard",
KeyCode = Enum.KeyCode.Space,
},
{
Name = "Gamepad",
KeyCode = Enum.KeyCode.ButtonA,
},
},
},
{
Name = "Move",
DisplayName = "Move",
Type = Enum.InputActionType.Direction2D,
Bindings = {
{
Name = "Wasd",
Up = Enum.KeyCode.W,
Down = Enum.KeyCode.S,
Left = Enum.KeyCode.A,
Right = Enum.KeyCode.D,
Vector2Scale = Vector2.new(1, 1),
ClampMagnitudeToOne = true,
},
},
},
},
},
{
Name = "Menu",
Enabled = false,
Priority = 3000,
Sink = true,
Actions = {
{
Name = "Confirm",
DisplayName = "Confirm",
Type = Enum.InputActionType.Bool,
Bindings = {
{
Name = "Keyboard",
KeyCode = Enum.KeyCode.Return,
},
{
Name = "Gamepad",
KeyCode = Enum.KeyCode.ButtonA,
},
},
},
{
Name = "Back",
DisplayName = "Back",
Type = Enum.InputActionType.Bool,
Bindings = {
{
Name = "Keyboard",
KeyCode = Enum.KeyCode.Escape,
},
{
Name = "Gamepad",
KeyCode = Enum.KeyCode.ButtonB,
},
},
},
},
},
{
Name = "Vehicle",
Enabled = false,
Priority = 2500,
Sink = true,
Actions = {
{
Name = "Throttle",
DisplayName = "Throttle",
Type = Enum.InputActionType.Direction1D,
Bindings = {
{
Name = "Keyboard",
Up = Enum.KeyCode.W,
Down = Enum.KeyCode.S,
Scale = 1,
},
},
},
},
},
})Update inputs later without rebuilding the whole tree:
inputService:ConfigureAction("Gameplay", {
Name = "Interact",
DisplayName = "Interact",
Type = Enum.InputActionType.Bool,
Bindings = {
{
Name = "Keyboard",
KeyCode = Enum.KeyCode.E,
},
},
})
inputService:ConfigureBinding("Gameplay", "Jump", {
Name = "Keyboard",
KeyCode = Enum.KeyCode.Space,
DisplayName = "Space",
})Switch active modes by toggling contexts:
local function setMenuOpen(isOpen)
inputService:SetContextEnabled("Gameplay", not isOpen)
inputService:SetContextEnabled("Menu", isOpen)
end
local function setInVehicle(isInVehicle)
inputService:SetContextEnabled("Vehicle", isInVehicle)
endRemove objects when a feature is unloaded:
inputService:RemoveBinding("Gameplay", "Jump", "Keyboard")
inputService:RemoveAction("Gameplay", "Interact")
inputService:RemoveContext("Vehicle")Binding Patterns
Prefer native Input Action System bindings whenever Roblox can express the
shape. One InputAction can have many child InputBinding instances, so a
single action can support keyboard, mouse, gamepad, touch, and alternate inputs
without adding more actions or custom input listeners.
inputService:RegisterContext({
Name = "Gameplay",
Enabled = true,
Priority = 2000,
Actions = {
{
Name = "OpenMap",
DisplayName = "Map",
Type = Enum.InputActionType.Bool,
Bindings = {
{
Name = "KeyboardM",
KeyCode = Enum.KeyCode.M,
DisplayName = "M",
},
{
Name = "KeyboardTab",
KeyCode = Enum.KeyCode.Tab,
DisplayName = "Tab",
},
{
Name = "Gamepad",
KeyCode = Enum.KeyCode.ButtonSelect,
DisplayName = "Select",
},
},
},
},
})Touch UIButton bindings are usually client-local because the button lives in a
player's PlayerGui:
inputServiceClient:ConfigureBinding("Gameplay", "OpenMap", {
Name = "Touch",
UIButton = playerGui.Hud.MapButton,
DisplayName = "Map",
})For "two keys at the same time" style input, use native binding modifiers before adding script. This keeps display labels, rebinding, and device switching inside Roblox's IAS model:
inputService:ConfigureBinding("Gameplay", "OpenMap", {
Name = "CtrlM",
KeyCode = Enum.KeyCode.M,
PrimaryModifier = Enum.KeyCode.LeftControl,
DisplayName = "Ctrl + M",
})
inputService:ConfigureBinding("Gameplay", "Interact", {
Name = "ShiftE",
KeyCode = Enum.KeyCode.E,
PrimaryModifier = Enum.KeyCode.LeftShift,
DisplayName = "Shift + E",
})
inputService:ConfigureBinding("Gameplay", "Ping", {
Name = "GamepadCombo",
KeyCode = Enum.KeyCode.ButtonR1,
PrimaryModifier = Enum.KeyCode.ButtonL1,
DisplayName = "L1 + R1",
})Device-specific patterns:
- PC keyboard/mouse: use
KeyCodefor keys, mouse buttons, and modifier combos. - Gamepad: use
KeyCodefor face buttons, shoulders, triggers, thumbstick buttons, and analog thresholds. - Mobile/touch: bind
UIButtonto on-screen buttons inside player UI. - Directional movement: use
Direction1D,Direction2D, orDirection3Dactions withUp,Down,Left,Right,Forward, andBackward. - Analog triggers: use
PressedThresholdandReleasedThresholdso bool actions feel stable instead of flickering around a boundary. - Micro gamepad: keep bindings simple and expose fewer actions at once; use contexts to swap between gameplay, menus, vehicles, and tools.
inputService:ConfigureAction("Gameplay", {
Name = "Aim",
DisplayName = "Aim",
Type = Enum.InputActionType.Bool,
Bindings = {
{
Name = "Mouse",
KeyCode = Enum.KeyCode.MouseRightButton,
},
{
Name = "GamepadTrigger",
KeyCode = Enum.KeyCode.ButtonL2,
PressedThreshold = 0.55,
ReleasedThreshold = 0.35,
},
},
})
inputService:ConfigureAction("Gameplay", {
Name = "Move",
DisplayName = "Move",
Type = Enum.InputActionType.Direction2D,
Bindings = {
{
Name = "Wasd",
Up = Enum.KeyCode.W,
Down = Enum.KeyCode.S,
Left = Enum.KeyCode.A,
Right = Enum.KeyCode.D,
ClampMagnitudeToOne = true,
},
{
Name = "LeftThumbstick",
KeyCode = Enum.KeyCode.Thumbstick1,
ClampMagnitudeToOne = true,
},
},
})Client Usage
Read shared contexts and bind to action signals:
local InputServiceClient = require("InputServiceClient")
local inputServiceClient = serviceBag:GetService(InputServiceClient)
inputServiceClient:WaitForContext("Gameplay", 10)
local sprintPressed = inputServiceClient:BindPressed("Gameplay", "Sprint", function()
print("Sprint pressed")
end)
local sprintReleased = inputServiceClient:BindReleased("Gameplay", "Sprint", function()
print("Sprint released")
end)
local moveChanged = inputServiceClient:BindStateChanged("Gameplay", "Move", function(value)
print("Move changed", value)
end)BindPressed, BindReleased, and BindStateChanged return connections. Store
and disconnect them with your maid/janitor/cleanup pattern:
maid:GiveTask(sprintPressed)
maid:GiveTask(sprintReleased)
maid:GiveTask(moveChanged)You can also access the full signal bundle:
local signals = inputServiceClient:GetActionSignals("Gameplay", "Jump")
maid:GiveTask(signals.EnabledChanged:Connect(function(enabled)
print("Jump enabled:", enabled)
end))
maid:GiveTask(signals.PreferredBindingChanged:Connect(function(binding)
print("Preferred jump binding:", binding)
end))Toggle local action state:
inputServiceClient:SetActionEnabled("Gameplay", "Sprint", false)
inputServiceClient:SetContextEnabled("Menu", true)Temporal Gestures
Roblox IAS covers bindings, modifiers, contexts, and action state. It does not
try to model timing patterns such as "press M twice within one second" or
"press Dash, then Jump." For those cases, InputServiceClient includes small
event-driven helpers that listen to existing Pressed signals. They do not use
polling, heartbeat loops, UserInputService key-state tracking, remotes, or
server authority.
Double press:
local doubleMapMaid = inputServiceClient:BindMultiPressed("Gameplay", "OpenMap", {
PressCount = 2,
WindowSeconds = 1,
CooldownSeconds = 0.5,
}, function()
mapController:ToggleMap()
end)
maid:GiveTask(doubleMapMaid)Triple press:
maid:GiveTask(inputServiceClient:BindMultiPressed("Debug", "ToggleConsole", {
PressCount = 3,
WindowSeconds = 1.25,
CooldownSeconds = 2,
}, function()
debugConsole:SetVisible(not debugConsole:IsVisible())
end))Ordered sequence:
maid:GiveTask(inputServiceClient:BindActionSequence({
{ ContextName = "Gameplay", ActionName = "Dash" },
{ ContextName = "Gameplay", ActionName = "Jump" },
}, {
WindowSeconds = 0.75,
CooldownSeconds = 1.5,
ResetOnWrongAction = true,
}, function()
abilityController:TryAirDash()
end))Repeated same-action sequence:
maid:GiveTask(inputServiceClient:BindActionSequence({
{ ContextName = "Gameplay", ActionName = "OpenMap" },
{ ContextName = "Gameplay", ActionName = "OpenMap" },
}, {
WindowSeconds = 1,
}, function()
mapController:ToggleExpandedMap()
end))Use these helpers for client UX and intent only. If a gesture triggers gameplay, send the resulting intent through your game's normal server-validated gameplay path.
Displaying Bindings
For ordinary hint UI, use Roblox's recommended InputActionLabel. It resolves
the current action text/image for the player's active device and updates when
Roblox changes the preferred binding.
local label = inputServiceClient:CreateActionLabel("Gameplay", "Sprint", playerGui.Hints, {
Name = "SprintHint",
Size = UDim2.fromOffset(160, 32),
BackgroundTransparency = 1,
TextColor3 = Color3.new(1, 1, 1),
TextSize = 18,
})For composited layouts, conditional visibility, custom animation, or Blend rows,
use PreferredBinding through the display-info helpers:
local displayMaid = inputServiceClient:BindBindingDisplayChanged(
"Gameplay",
"Sprint",
function(info)
textLabel.Text = info.Text
textLabel.Visible = info.HasText
if info.HasImage and info.ImageContent then
imageLabel.ImageContent = info.ImageContent
imageLabel.Visible = true
else
imageLabel.Visible = false
end
end
)
maid:GiveTask(displayMaid)GetBindingDisplayInfo("Gameplay", "Sprint") returns the same shape once:
{
Binding = InputBinding?,
Text = "Left Shift",
Image = "rbxasset://...",
ImageContent = Content?,
HasText = true,
HasImage = true,
TextSource = "DisplayName" | "KeyCode" | "UIButton" | "None",
ImageSource = "DisplayImage" | "KeyCode" | "None",
}The helper prefers InputBinding.DisplayName and InputBinding.DisplayImage,
then falls back to UserInputService:GetStringForKeyCode() and
UserInputService:GetImageForKeyCode() when the preferred binding has a key.
Local Contexts And UI Bindings
Clients can define local-only contexts. These are useful for UI, tutorials, debug tools, accessibility overlays, and rebinding screens. They do not send any claims or config changes to the server through this package.
local closeButton = playerGui.Inventory.CloseButton
inputServiceClient:DefineLocalContext({
Name = "InventoryUi",
Enabled = false,
Priority = 4000,
Sink = true,
Actions = {
{
Name = "Close",
DisplayName = "Close",
Type = Enum.InputActionType.Bool,
Bindings = {
{
Name = "Keyboard",
KeyCode = Enum.KeyCode.Escape,
},
{
Name = "Button",
UIButton = closeButton,
DisplayName = "Close",
},
},
},
},
})
inputServiceClient:BindPressed("InventoryUi", "Close", function()
inputServiceClient:SetContextEnabled("InventoryUi", false)
end)Configure a local binding at runtime:
inputServiceClient:ConfigureBinding("InventoryUi", "Close", {
Name = "Gamepad",
KeyCode = Enum.KeyCode.ButtonB,
DisplayName = "Back",
})Fire a binding from custom client code when the underlying InputBinding type in
your Roblox runtime supports :Fire():
inputServiceClient:ConfigureAction("InventoryUi", {
Name = "Close",
Type = Enum.InputActionType.Bool,
Bindings = {
{
Name = "Script",
Type = Enum.InputBindingType.Scriptable,
},
},
})
inputServiceClient:FireBinding("InventoryUi", "Close", "Script", true)
inputServiceClient:FireBinding("InventoryUi", "Close", "Script", false)Blend And Nevermore UI Patterns
InputService does not depend on Blend, Rx, Binder, or BaseObject, but it fits well with those Nevermore patterns:
- Use a service to define the shared input tree and expose game-specific state.
- Use
BaseObjector a pane/controller object to own UI and input connections. - Use
Maidfor every connection, mounted Blend tree, and temporary tool input. - Use
Blend.Statefor the current list of visible input hints. - Use
PreferredBindingChangedto update labels when the player's device changes. - Use a short-lived maid for tool-specific actions so unequipping the tool removes its input context and hint rows at the same time.
The pattern is to keep actions in the input tree, then render a separate list of
human-readable rows. General actions, such as F to interact, stay in the list
all the time. Tool actions are pushed while the tool is equipped and cleaned up
when it is unequipped.
local Blend = require("Blend")
local Maid = require("Maid")
local InputHintPane = {}
InputHintPane.ClassName = "InputHintPane"
InputHintPane.__index = InputHintPane
function InputHintPane.new(inputServiceClient, playerGui)
local self = setmetatable({}, InputHintPane)
self._maid = Maid.new()
self._inputServiceClient = inputServiceClient
self._rows = Blend.State({})
self._maid:GiveTask(self:_render(playerGui):Subscribe())
self:_setAlwaysAvailableRows()
return self
end
function InputHintPane:_render(playerGui)
return Blend.New "ScreenGui" {
Name = "InputHints",
Parent = playerGui,
ResetOnSpawn = false,
Blend.New "Frame" {
AnchorPoint = Vector2.new(1, 1),
Position = UDim2.fromScale(0.98, 0.96),
Size = UDim2.fromOffset(280, 160),
BackgroundTransparency = 1,
Blend.New "UIListLayout" {
Padding = UDim.new(0, 6),
SortOrder = Enum.SortOrder.LayoutOrder,
},
Blend.ComputedPairs(self._rows, function(_, row)
return Blend.New "TextLabel" {
Size = UDim2.new(1, 0, 0, 28),
BackgroundTransparency = 0.25,
TextXAlignment = Enum.TextXAlignment.Left,
Text = string.format("[%s] %s", row.BindingText, row.DisplayName),
LayoutOrder = row.LayoutOrder,
}
end),
},
}
end
function InputHintPane:_setRows(rows)
self._rows.Value = rows
end
function InputHintPane:_setAlwaysAvailableRows()
self:_setRows({
{
ContextName = "Gameplay",
ActionName = "Interact",
DisplayName = "Interact",
BindingText = "F",
LayoutOrder = 100,
},
{
ContextName = "Gameplay",
ActionName = "Sprint",
DisplayName = "Sprint",
BindingText = "LeftShift",
LayoutOrder = 110,
},
})
end
function InputHintPane:Destroy()
self._maid:DoCleaning()
end
return InputHintPaneFor real UI, avoid hard-coding binding text forever. Start with a fallback, then
listen to each action's PreferredBindingChanged signal and rewrite that row
when Roblox chooses a better keyboard, gamepad, or touch binding for the player.
local function getBindingText(binding)
if not binding then
return "?"
end
if binding.DisplayName ~= "" then
return binding.DisplayName
end
if binding.KeyCode ~= Enum.KeyCode.Unknown then
return binding.KeyCode.Name
end
if binding.UIButton then
return binding.UIButton.Name
end
return binding.Name
end
function InputHintPane:_watchPreferredBinding(row)
local signals = self._inputServiceClient:GetActionSignals(row.ContextName, row.ActionName)
local function update(binding)
local nextRows = table.clone(self._rows.Value)
for index, existing in nextRows do
if existing.ContextName == row.ContextName and existing.ActionName == row.ActionName then
local nextRow = table.clone(existing)
nextRow.BindingText = getBindingText(binding)
nextRows[index] = nextRow
break
end
end
self._rows.Value = nextRows
end
local action = self._inputServiceClient:GetAction(row.ContextName, row.ActionName)
if action then
update(action.PreferredBinding)
end
return signals.PreferredBindingChanged:Connect(update)
endTool-specific input works best as a lifetime-scoped layer. When the user equips a tool, define or enable a local context for the tool, add its rows to the hint list, and store all of that work in a tool maid. When the tool is unequipped, the maid cleans the UI rows and disables or removes the tool context.
function InputHintPane:SetEquippedTool(tool)
if self._toolMaid then
self._toolMaid:DoCleaning()
end
self._toolMaid = Maid.new()
self._maid._toolMaid = self._toolMaid
if not tool then
self:_setAlwaysAvailableRows()
return
end
local contextName = "Tool:" .. tool.Name
local toolRows = {
{
ContextName = contextName,
ActionName = "Primary",
DisplayName = tool.Name .. " Primary",
BindingText = "MouseLeftButton",
LayoutOrder = 200,
},
{
ContextName = contextName,
ActionName = "Reload",
DisplayName = "Reload",
BindingText = "R",
LayoutOrder = 210,
},
}
self._inputServiceClient:DefineLocalContext({
Name = contextName,
Enabled = true,
Priority = 2600,
Sink = false,
Actions = {
{
Name = "Primary",
DisplayName = tool.Name .. " Primary",
Type = Enum.InputActionType.Bool,
Bindings = {
{
Name = "Mouse",
KeyCode = Enum.KeyCode.MouseLeftButton,
},
},
},
{
Name = "Reload",
DisplayName = "Reload",
Type = Enum.InputActionType.Bool,
Bindings = {
{
Name = "Keyboard",
KeyCode = Enum.KeyCode.R,
},
},
},
},
})
local rows = table.clone(self._rows.Value)
for _, row in toolRows do
table.insert(rows, row)
self._toolMaid:GiveTask(self:_watchPreferredBinding(row))
end
self:_setRows(rows)
self._toolMaid:GiveTask(function()
self._inputServiceClient:SetContextEnabled(contextName, false)
self:_setAlwaysAvailableRows()
end)
endIn a full Nevermore game, the tool layer usually comes from a service or Binder instead of from the pane directly:
function ToolInputHintService:Start()
local inputHintPane = InputHintPane.new(
self._serviceBag:GetService(InputServiceClient),
Players.LocalPlayer:WaitForChild("PlayerGui")
)
self._maid:GiveTask(inputHintPane)
self._maid:GiveTask(self._equippedTool.Changed:Connect(function(tool)
inputHintPane:SetEquippedTool(tool)
end))
endThat keeps the responsibilities clean: InputServiceClient owns input instances,
the tool service decides which tool is active, and the Blend pane only renders
the current rows. The same shape works for ability bars, contextual prompts,
vehicle controls, build-mode hotkeys, and accessibility overlays.
Config Reference
Every config requires a non-empty Name with no control characters or /.
Unknown fields are rejected so typos fail early.
Context fields:
| Field | Type | Notes |
| --- | --- | --- |
| Name | string | Required. Used as the InputContext.Name. |
| Enabled | boolean? | Sets whether the context is active. |
| Priority | number? | Higher-priority contexts can win over lower-priority contexts. |
| Sink | boolean? | Controls whether the context sinks input. |
| Actions | { InputActionConfig }? | Actions to create or update under this context. |
Action fields:
| Field | Type | Notes |
| --- | --- | --- |
| Name | string | Required. Used as the InputAction.Name. |
| DisplayName | string? | Human-readable action name. |
| Enabled | boolean? | Sets whether the action is active. |
| Type | Enum.InputActionType? | The action value type, such as Bool, Direction1D, Direction2D, Direction3D, or ViewportPosition. |
| Bindings | { InputBindingConfig }? | Bindings to create or update under this action. |
Binding fields:
| Field | Type | Notes |
| --- | --- | --- |
| Name | string | Required. Used as the InputBinding.Name. |
| Type | Enum.InputBindingType? | Binding type for runtimes that require it. |
| KeyCode | Enum.KeyCode? | Keyboard, mouse, or gamepad key/button binding. |
| UIButton | GuiButton? | UI button binding. |
| UIModifier | GuiButton? | UI modifier button. |
| PrimaryModifier | Enum.KeyCode? | Primary keyboard/gamepad modifier. |
| SecondaryModifier | Enum.KeyCode? | Secondary keyboard/gamepad modifier. |
| DisplayName | string? | Human-readable binding name. |
| DisplayImage | Content? | Binding icon/image content. |
| PressedThreshold | number? | Pressed threshold. Must be greater than or equal to ReleasedThreshold when both are set. |
| ReleasedThreshold | number? | Released threshold. |
| ResponseCurve | number? | Response curve value. |
| Scale | number? | Scalar output scale. |
| Vector2Scale | Vector2? | Vector2 output scale. |
| Vector3Scale | Vector3? | Vector3 output scale. |
| PointerIndex | number? | Pointer index for pointer-style bindings. |
| ClampMagnitudeToOne | boolean? | Clamp vector magnitude to one. |
| Up | Enum.KeyCode? | Up or positive direction key. |
| Down | Enum.KeyCode? | Down or negative direction key. |
| Left | Enum.KeyCode? | Left direction key. |
| Right | Enum.KeyCode? | Right direction key. |
| Forward | Enum.KeyCode? | Forward direction key for 3D actions. |
| Backward | Enum.KeyCode? | Backward direction key for 3D actions. |
Gesture option fields:
| Type | Field | Notes |
| --- | --- | --- |
| MultiPressOptions | PressCount | Number of presses required. Defaults to 2; minimum 2. |
| MultiPressOptions | WindowSeconds | Time window for the presses. Defaults to 1. |
| MultiPressOptions | CooldownSeconds | Minimum time between callback fires. Defaults to 0. |
| ActionSequenceOptions | WindowSeconds | Time window for the whole sequence. Defaults to 1. |
| ActionSequenceOptions | CooldownSeconds | Minimum time between callback fires. Defaults to 0. |
| ActionSequenceOptions | ResetOnWrongAction | Whether a wrong action resets progress. Defaults to true. |
API Reference
Server InputService:
| Method | Description |
| --- | --- |
| GetRootFolder() | Returns ReplicatedStorage.Inputs. |
| RegisterContext(config) | Creates or updates one shared context. |
| RegisterContexts(configs) | Creates or updates many shared contexts. |
| GetContext(contextName) | Returns an InputContext?. |
| GetAction(contextName, actionName) | Returns an InputAction?. |
| GetBinding(contextName, actionName, bindingName) | Returns an InputBinding?. |
| ConfigureAction(contextName, actionConfig) | Creates or updates one action under an existing context. |
| ConfigureBinding(contextName, actionName, bindingConfig) | Creates or updates one binding under an existing action. |
| SetContextEnabled(contextName, enabled) | Enables or disables a context. |
| SetActionEnabled(contextName, actionName, enabled) | Enables or disables an action. |
| RemoveContext(contextName) | Removes a context and returns whether one was removed. |
| RemoveAction(contextName, actionName) | Removes an action and returns whether one was removed. |
| RemoveBinding(contextName, actionName, bindingName) | Removes a binding and returns whether one was removed. |
Client InputServiceClient:
| Method | Description |
| --- | --- |
| GetRootFolder() | Returns or creates the local root folder. |
| GetContext(contextName) | Returns an InputContext?. |
| WaitForContext(contextName, timeoutSeconds?) | Waits for a shared context to replicate. |
| DefineLocalContext(config) | Creates or updates a local-only context. |
| ConfigureAction(contextName, actionConfig) | Creates or updates an action under an existing context. |
| ConfigureBinding(contextName, actionName, bindingConfig) | Creates or updates a binding under an existing action. |
| GetAction(contextName, actionName) | Returns an InputAction?. |
| GetBinding(contextName, actionName, bindingName) | Returns an InputBinding?. |
| GetPreferredBinding(contextName, actionName) | Returns the action's current read-only PreferredBinding. |
| GetDisplayInfoForBinding(binding) | Resolves text/image display metadata for an InputBinding?. |
| GetBindingDisplayInfo(contextName, actionName) | Resolves display metadata from the action's PreferredBinding. |
| BindBindingDisplayChanged(contextName, actionName, callback, fireImmediately?) | Observes preferred binding and display override changes for custom UI. |
| CreateActionLabel(contextName, actionName, parentOrProperties?, properties?) | Creates an InputActionLabel wired to an action. |
| GetActionSignals(contextName, actionName) | Returns cached pressed, released, state, enabled, and preferred-binding signals. |
| BindPressed(contextName, actionName, callback) | Connects to an action's pressed signal. |
| BindReleased(contextName, actionName, callback) | Connects to an action's released signal. |
| BindStateChanged(contextName, actionName, callback) | Connects to an action's state changed signal. |
| BindMultiPressed(contextName, actionName, options, callback) | Connects to double/triple press-style gestures and returns a Maid. |
| BindActionSequence(sequence, options, callback) | Connects to ordered action sequences and returns a Maid. |
| SetContextEnabled(contextName, enabled) | Enables or disables a context locally. |
| SetActionEnabled(contextName, actionName, enabled) | Enables or disables an action locally. |
| FireBinding(contextName, actionName, bindingName, state) | Fires a scriptable binding with boolean, number, Vector2, or Vector3 state. |
Security
- No remotes are created for input claims, rebinding, or context changes.
- Server APIs accept configs only from server code.
- Client-side input signals should be treated as intent only.
- Client-created contexts and bindings are local convenience only.
- Games must validate gameplay effects on the server.
