Start typing to search packages!
action-buffer
By @zenukopublic
Roblox
Mirrored from Wallyaction-buffer
High-performance circular ring-buffered action queuing and prediction reconciliation for competitive combat games.
Production Origin: This package was extracted and generalized from infrastructure developed for Kabbrawl, a private competitive multiplayer Roblox experience. Kabbrawl itself remains proprietary; this package contains only reusable infrastructure and no proprietary assets or game-specific content.
⚔️ Why Action Buffering?
In fast-paced fighting, action RPG, or sports games, player inputs often arrive while the character is finishing an animation, stunned, or during a frame transition.
Without an input buffer:
- Inputs pressed 50 ms before an attack ends are dropped, making controls feel unresponsive and "stiff".
- With a naive array queue, memory allocations occur every frame, triggering garbage collection hitches.
- Inputs queued too early execute unintentionally after long delays.
action-buffer provides a zero-allocation circular ring buffer with a strict temporal expiration window (e.g. 150 ms) and integrated client prediction reconciliation.
✨ Features
- Zero Memory Allocation in Hot Paths: Circular ring buffers reuse table slots without resizing or GC pressure.
- Temporal Window Consumption: Queued inputs expire naturally if the player cannot act within the window.
- Multi-Channel Isolation: Separate channels for Attack, Dodge, Guard, and Skill inputs.
- Prediction Reconciliation: Match optimistic client-predicted actions against server-confirmed sequence IDs.
🚀 Installation
Via Wally
Add action-buffer to your wally.toml:
[dependencies]
ActionBuffer = "zenukopublic/action-buffer@1.0.0"
📖 Quickstart
local ActionBuffer = require(Packages.ActionBuffer)
local buffer = ActionBuffer.new({
capacity = 8,
windowSeconds = 0.15, -- 150 ms buffer window
})
-- When player presses attack button (even during recovery frames):
UserInputService.InputBegan:Connect(function(input)
if input.KeyCode == Enum.KeyCode.E then
buffer:push("Attack", currentFrame, { attackType = "Slash" })
end
end)
-- In your 60 Hz combat tick / state machine:
RunService.Heartbeat:Connect(function()
if characterState:canAttack() then
local action = buffer:consume("Attack")
if action then
characterState:executeAttack(action.payload.attackType)
end
end
end)
🧪 Testing
Run standalone unit tests headlessly with Lune:
lune run tests/action_buffer.test.luau
📄 License
MIT License. Free for personal and commercial use.
Package Details
Install command (Click to copy)
Version
1.0.0
License
MIT
Safe for commercial use
Automated license review — not legal advice.
