update: packages,lua,retro (4 files)

This commit is contained in:
Richard Ward
2025-12-30 19:42:51 +00:00
parent 51f60f5500
commit 2b4e923e5d
4 changed files with 393 additions and 2 deletions

View File

@@ -0,0 +1,95 @@
--[[
Libretro combo and motion inputs
Complex input sequences and fighting game motions
]]
local input = require("lua.retro_input")
---@class RetroCombosModule
local M = {}
-- ============================================================================
-- Combo/Macro System
-- ============================================================================
---Execute a sequence of inputs
---@param session_id string Session identifier
---@param inputs ComboInput[] Array of input specifications
---@param player? number Player number (default 0)
function M.combo(session_id, inputs, player)
for _, input_spec in ipairs(inputs) do
if input_spec.button then
input.tap(session_id, input_spec.button, player, input_spec.duration_ms)
elseif input_spec.analog then
input.flick_analog(session_id, input_spec.analog.stick,
input_spec.analog.x, input_spec.analog.y, input_spec.duration_ms, player)
end
if input_spec.wait_after_ms then
sleep(input_spec.wait_after_ms)
end
end
end
-- ============================================================================
-- Fighting Game Motions
-- ============================================================================
--- Common fighting game motions
M.MOTION = {
-- Quarter circle forward (↓↘→)
QCF = function(session_id, player)
input.tap(session_id, "down", player, 30)
input.diagonal(session_id, "right", "down", 30, player)
input.tap(session_id, "right", player, 30)
end,
-- Quarter circle back (↓↙←)
QCB = function(session_id, player)
input.tap(session_id, "down", player, 30)
input.diagonal(session_id, "left", "down", 30, player)
input.tap(session_id, "left", player, 30)
end,
-- Dragon punch / Shoryuken (→↓↘)
DP = function(session_id, player)
input.tap(session_id, "right", player, 30)
input.tap(session_id, "down", player, 30)
input.diagonal(session_id, "right", "down", 30, player)
end,
-- Half circle forward (←↙↓↘→)
HCF = function(session_id, player)
input.tap(session_id, "left", player, 30)
input.diagonal(session_id, "left", "down", 30, player)
input.tap(session_id, "down", player, 30)
input.diagonal(session_id, "right", "down", 30, player)
input.tap(session_id, "right", player, 30)
end,
-- Charge back then forward
CHARGE_BF = function(session_id, charge_ms, player)
charge_ms = charge_ms or 800
input.hold_direction(session_id, "left", charge_ms, player)
input.tap(session_id, "right", player, 30)
end,
-- 360 motion (for grapplers)
CIRCLE = function(session_id, player)
local dirs = {"right", "down", "left", "up"}
for _, dir in ipairs(dirs) do
input.tap(session_id, dir, player, 20)
end
end,
}
---Execute a motion then press a button
---@param session_id string Session identifier
---@param motion function Motion from MOTION table
---@param button string Button to press after motion
---@param player? number Player number (default 0)
function M.special_move(session_id, motion, button, player)
motion(session_id, player)
input.tap(session_id, button, player)
end
return M

View File

@@ -0,0 +1,169 @@
--[[
Libretro input handling
Button presses, analog sticks, combos, and fighting game motions
]]
local constants = require("lua.retro_constants")
---@class RetroInputModule
local M = {}
-- Re-export constants for convenience
M.BUTTON = constants.BUTTON
M.AXIS = constants.AXIS
-- ============================================================================
-- Basic Input Functions
-- ============================================================================
---Press and release a button (tap)
---@param session_id string Session identifier
---@param button string Button from BUTTON table
---@param player? number Player number (default 0)
---@param duration_ms? number Hold duration in ms (default 50)
function M.tap(session_id, button, player, duration_ms)
player = player or 0
duration_ms = duration_ms or 50
M.press(session_id, button, player)
sleep(duration_ms)
M.release(session_id, button, player)
end
---Press a button (hold down)
---@param session_id string Session identifier
---@param button string Button from BUTTON table
---@param player? number Player number (default 0)
function M.press(session_id, button, player)
player = player or 0
http.post("/api/v1/retro/sessions/" .. session_id .. "/input", {
player = player,
button = button,
pressed = true
})
end
---Release a button
---@param session_id string Session identifier
---@param button string Button from BUTTON table
---@param player? number Player number (default 0)
function M.release(session_id, button, player)
player = player or 0
http.post("/api/v1/retro/sessions/" .. session_id .. "/input", {
player = player,
button = button,
pressed = false
})
end
---Release all buttons for a player
---@param session_id string Session identifier
---@param player? number Player number (default 0)
function M.release_all(session_id, player)
player = player or 0
for _, btn in pairs(M.BUTTON) do
M.release(session_id, btn, player)
end
end
-- ============================================================================
-- Direction Shortcuts
-- ============================================================================
---Tap UP button
---@param session_id string Session identifier
---@param player? number Player number (default 0)
function M.up(session_id, player)
M.tap(session_id, M.BUTTON.UP, player)
end
---Tap DOWN button
---@param session_id string Session identifier
---@param player? number Player number (default 0)
function M.down(session_id, player)
M.tap(session_id, M.BUTTON.DOWN, player)
end
---Tap LEFT button
---@param session_id string Session identifier
---@param player? number Player number (default 0)
function M.left(session_id, player)
M.tap(session_id, M.BUTTON.LEFT, player)
end
---Tap RIGHT button
---@param session_id string Session identifier
---@param player? number Player number (default 0)
function M.right(session_id, player)
M.tap(session_id, M.BUTTON.RIGHT, player)
end
---Hold a direction
---@param session_id string Session identifier
---@param direction string "up", "down", "left", "right"
---@param duration_ms number How long to hold in milliseconds
---@param player? number Player number (default 0)
function M.hold_direction(session_id, direction, duration_ms, player)
M.press(session_id, direction, player)
sleep(duration_ms)
M.release(session_id, direction, player)
end
---Move in a diagonal direction
---@param session_id string Session identifier
---@param horizontal string "left" or "right"
---@param vertical string "up" or "down"
---@param duration_ms? number Hold duration (default 50)
---@param player? number Player number (default 0)
function M.diagonal(session_id, horizontal, vertical, duration_ms, player)
duration_ms = duration_ms or 50
M.press(session_id, horizontal, player)
M.press(session_id, vertical, player)
sleep(duration_ms)
M.release(session_id, horizontal, player)
M.release(session_id, vertical, player)
end
-- ============================================================================
-- Analog Stick Input
-- ============================================================================
---Set analog stick position
---@param session_id string Session identifier
---@param stick string "left" or "right"
---@param x number -1.0 to 1.0 (left to right)
---@param y number -1.0 to 1.0 (up to down)
---@param player? number Player number (default 0)
function M.set_analog(session_id, stick, x, y, player)
player = player or 0
http.post("/api/v1/retro/sessions/" .. session_id .. "/input/analog", {
player = player,
stick = stick,
x = math.max(-1.0, math.min(1.0, x)),
y = math.max(-1.0, math.min(1.0, y))
})
end
---Center (release) analog stick
---@param session_id string Session identifier
---@param stick string "left" or "right"
---@param player? number Player number (default 0)
function M.center_analog(session_id, stick, player)
M.set_analog(session_id, stick, 0, 0, player)
end
---Move analog stick in a direction and return to center
---@param session_id string Session identifier
---@param stick string "left" or "right"
---@param x number -1.0 to 1.0 horizontal axis
---@param y number -1.0 to 1.0 vertical axis
---@param duration_ms? number How long to hold (default 100)
---@param player? number Player number (default 0)
function M.flick_analog(session_id, stick, x, y, duration_ms, player)
duration_ms = duration_ms or 100
M.set_analog(session_id, stick, x, y, player)
sleep(duration_ms)
M.center_analog(session_id, stick, player)
end
return M

View File

@@ -0,0 +1,125 @@
-- Tests for permission access checking
-- Tests check_access function with various scenarios
local checkAccess = require("permissions.check_access")
describe("Permission Access Checking", function()
describe("Basic access checks", function()
it("should allow access when user level meets requirement", function()
local result = checkAccess.check_access(3, {
enabled = true,
minLevel = 2
})
assert.is_true(result.allowed)
assert.is_nil(result.reason)
end)
it("should deny access when user level is too low", function()
local result = checkAccess.check_access(1, {
enabled = true,
minLevel = 3
})
assert.is_false(result.allowed)
assert.equals("Insufficient permission level", result.reason)
assert.equals(3, result.requiredLevel)
end)
it("should deny access when resource is disabled", function()
local result = checkAccess.check_access(5, {
enabled = false,
minLevel = 1
})
assert.is_false(result.allowed)
assert.equals("Resource is currently disabled", result.reason)
end)
end)
describe("Database requirements", function()
it("should allow access when database is enabled and required", function()
local result = checkAccess.check_access(3, {
enabled = true,
minLevel = 2,
databaseRequired = true
}, {}, true)
assert.is_true(result.allowed)
end)
it("should deny access when database is disabled but required", function()
local result = checkAccess.check_access(3, {
enabled = true,
minLevel = 2,
databaseRequired = true
}, {}, false)
assert.is_false(result.allowed)
assert.equals("Database is required but not enabled", result.reason)
end)
it("should allow access when database is disabled and not required", function()
local result = checkAccess.check_access(3, {
enabled = true,
minLevel = 2
}, {}, false)
assert.is_true(result.allowed)
end)
end)
describe("Feature flags", function()
it("should allow access when all required flags are enabled", function()
local result = checkAccess.check_access(3, {
enabled = true,
minLevel = 2,
featureFlags = {"flag1", "flag2"}
}, {
flag1 = true,
flag2 = true
})
assert.is_true(result.allowed)
end)
it("should deny access when required flag is missing", function()
local result = checkAccess.check_access(3, {
enabled = true,
minLevel = 2,
featureFlags = {"flag1", "flag2"}
}, {
flag1 = true,
flag2 = false
})
assert.is_false(result.allowed)
assert.equals("Required feature flag 'flag2' is not enabled", result.reason)
end)
it("should allow access when no flags are required", function()
local result = checkAccess.check_access(3, {
enabled = true,
minLevel = 2
}, {})
assert.is_true(result.allowed)
end)
end)
describe("Combined checks", function()
it("should pass all checks when all requirements are met", function()
local result = checkAccess.check_access(5, {
enabled = true,
minLevel = 4,
databaseRequired = true,
featureFlags = {"advanced_mode"}
}, {
advanced_mode = true
}, true)
assert.is_true(result.allowed)
end)
it("should fail on first check that doesn't pass", function()
local result = checkAccess.check_access(3, {
enabled = false,
minLevel = 4,
databaseRequired = true
}, {}, true)
assert.is_false(result.allowed)
assert.equals("Resource is currently disabled", result.reason)
end)
end)
end)

View File

@@ -1,4 +1,6 @@
-- This file has been split into smaller modules in editor/
-- Use require("editor") or the individual submodules instead
--- This file has been split into smaller modules in editor/
--- Use require("editor") or the individual submodules instead
---@module editor
---@deprecated Use editor.init instead
return require("editor.init")