mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-04-10 06:44:29 +00:00
Initial Lua Settings API prototype
This commit is contained in:
parent
49487a17e6
commit
096255534a
@ -4,3 +4,5 @@ PLAYER: scripts/omw/console/player.lua
|
|||||||
GLOBAL: scripts/omw/console/global.lua
|
GLOBAL: scripts/omw/console/global.lua
|
||||||
CUSTOM: scripts/omw/console/local.lua
|
CUSTOM: scripts/omw/console/local.lua
|
||||||
PLAYER: scripts/omw/mwui/init.lua
|
PLAYER: scripts/omw/mwui/init.lua
|
||||||
|
GLOBAL: scripts/omw/settings/global.lua
|
||||||
|
PLAYER: scripts/omw/settings/player.lua
|
||||||
|
150
files/builtin_scripts/scripts/omw/settings/common.lua
Normal file
150
files/builtin_scripts/scripts/omw/settings/common.lua
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
local prequire = function(path)
|
||||||
|
local status, result = pcall(function()
|
||||||
|
return require(path)
|
||||||
|
end)
|
||||||
|
return status and result or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local core = require('openmw.core')
|
||||||
|
local types = require('openmw.types')
|
||||||
|
local storage = require('openmw.storage')
|
||||||
|
local self = prequire('openmw.self')
|
||||||
|
local world = prequire('openmw.world')
|
||||||
|
|
||||||
|
local isPlayerScript = self and true or false
|
||||||
|
local isGlobalScript = world and true or false
|
||||||
|
|
||||||
|
local EVENTS = {
|
||||||
|
SettingChanged = 'omwSettingsChanged',
|
||||||
|
SettingSet = 'omwSettingsGlobalSet',
|
||||||
|
GroupRegistered = 'omwSettingsGroupRegistered',
|
||||||
|
}
|
||||||
|
|
||||||
|
local SCOPE = {
|
||||||
|
Global = 'Global',
|
||||||
|
Player = 'Player',
|
||||||
|
SaveGlobal = 'SaveGlobal',
|
||||||
|
SavePlayer = 'SavePlayer',
|
||||||
|
}
|
||||||
|
|
||||||
|
local groups = storage.globalSection('OMW_Settings_Groups')
|
||||||
|
local saveGlobalSection = storage.globalSection('OMW_Settings_SaveGlobal')
|
||||||
|
|
||||||
|
if isGlobalScript then
|
||||||
|
groups:removeOnExit()
|
||||||
|
saveGlobalSection:removeOnExit()
|
||||||
|
end
|
||||||
|
|
||||||
|
local savePlayerSection = nil
|
||||||
|
if isPlayerScript then
|
||||||
|
savePlayerSection = storage.playerSection('OMW_Setting_SavePlayer')
|
||||||
|
savePlayerSection:removeOnExit()
|
||||||
|
end
|
||||||
|
|
||||||
|
local scopes = {
|
||||||
|
[SCOPE.Global] = storage.globalSection('OMW_Setting_Global'),
|
||||||
|
[SCOPE.Player] = isPlayerScript and storage.playerSection('OMW_Setting_Player'),
|
||||||
|
[SCOPE.SaveGlobal] = saveGlobalSection,
|
||||||
|
[SCOPE.SavePlayer] = savePlayerSection,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function isGlobalScope(scope)
|
||||||
|
return scope == SCOPE.Global or scope == SCOPE.SaveGlobal
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getSetting(groupName, settingName)
|
||||||
|
local group = groups:get(groupName)
|
||||||
|
if not group then
|
||||||
|
error('Unknown group')
|
||||||
|
end
|
||||||
|
local setting = group[settingName]
|
||||||
|
if not setting then
|
||||||
|
error('Unknown setting')
|
||||||
|
end
|
||||||
|
return setting
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getSettingValue(groupName, settingName)
|
||||||
|
local setting = getSetting(groupName, settingName)
|
||||||
|
local scopeSection = scopes[setting.scope]
|
||||||
|
if not scopeSection then
|
||||||
|
error(('Setting %s is not available in this context'):format(setting.name))
|
||||||
|
end
|
||||||
|
if not scopeSection:get(groupName) then
|
||||||
|
scopeSection:set(groupName, {})
|
||||||
|
end
|
||||||
|
return scopeSection:get(groupName)[setting.name] or setting.default
|
||||||
|
end
|
||||||
|
|
||||||
|
local function notifySettingChange(scope, event)
|
||||||
|
if isGlobalScope(scope) then
|
||||||
|
core.sendGlobalEvent(EVENTS.SettingChanged, event)
|
||||||
|
for _, a in ipairs(world.activeActors) do
|
||||||
|
if a.type == types.Player then
|
||||||
|
a:sendEvent(EVENTS.SettingChanged, event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self:sendEvent(EVENTS.SettingChanged, event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function setSettingValue(groupName, settingName, value)
|
||||||
|
local setting = getSetting(groupName, settingName)
|
||||||
|
local event = {
|
||||||
|
groupName = groupName,
|
||||||
|
settingName = setting.name,
|
||||||
|
value = value,
|
||||||
|
}
|
||||||
|
if isPlayerScript and isGlobalScope(setting.scope) then
|
||||||
|
core.sendGlobalEvent(EVENTS.SettingSet, event)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local scopeSection = scopes[setting.scope]
|
||||||
|
if not scopeSection:get(groupName) then
|
||||||
|
scopeSection:set(groupName, {})
|
||||||
|
end
|
||||||
|
local copy = scopeSection:getCopy(groupName)
|
||||||
|
copy[setting.name] = value
|
||||||
|
scopeSection:set(groupName, copy)
|
||||||
|
|
||||||
|
notifySettingChange(setting.scope, event)
|
||||||
|
end
|
||||||
|
|
||||||
|
local groupMeta = {
|
||||||
|
__index = {
|
||||||
|
get = function(self, settingName)
|
||||||
|
return getSettingValue(self.name, settingName)
|
||||||
|
end,
|
||||||
|
set = function(self, settingName, value)
|
||||||
|
setSettingValue(self.name, settingName, value)
|
||||||
|
end,
|
||||||
|
onChange = function(self, callback)
|
||||||
|
table.insert(self.__callbacks, callback)
|
||||||
|
end,
|
||||||
|
__changed = function(self, settingName, value)
|
||||||
|
for _, callback in ipairs(self.__callbacks) do
|
||||||
|
callback(settingName, value)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
local cachedGroups = {}
|
||||||
|
local function getGroup(groupName)
|
||||||
|
if not cachedGroups[groupName] then
|
||||||
|
cachedGroups[groupName] = setmetatable({
|
||||||
|
name = groupName,
|
||||||
|
__callbacks = {},
|
||||||
|
}, groupMeta)
|
||||||
|
end
|
||||||
|
return cachedGroups[groupName]
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
EVENTS = EVENTS,
|
||||||
|
SCOPE = SCOPE,
|
||||||
|
scopes = scopes,
|
||||||
|
groups = groups,
|
||||||
|
getGroup = getGroup,
|
||||||
|
}
|
30
files/builtin_scripts/scripts/omw/settings/global.lua
Normal file
30
files/builtin_scripts/scripts/omw/settings/global.lua
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
local common = require('scripts.omw.settings.common')
|
||||||
|
local register = require('scripts.omw.settings.register')
|
||||||
|
|
||||||
|
local saveScope = common.scopes[common.SCOPE.SaveGlobal]
|
||||||
|
return {
|
||||||
|
interfaceName = 'Settings',
|
||||||
|
interface = {
|
||||||
|
SCOPE = common.SCOPE,
|
||||||
|
getGroup = common.getGroup,
|
||||||
|
registerGroup = register.registerGroup,
|
||||||
|
},
|
||||||
|
engineHandlers = {
|
||||||
|
onLoad = function(saved)
|
||||||
|
common.groups:reset()
|
||||||
|
saveScope:reset(saved)
|
||||||
|
end,
|
||||||
|
onSave = function()
|
||||||
|
return saveScope:asTable()
|
||||||
|
end,
|
||||||
|
onPlayerAdded = register.onPlayerAdded,
|
||||||
|
},
|
||||||
|
eventHandlers = {
|
||||||
|
[common.EVENTS.SettingChanged] = function(e)
|
||||||
|
common.getGroup(e.groupName):__changed(e.settingName, e.value)
|
||||||
|
end,
|
||||||
|
[common.EVENTS.SettingSet] = function(e)
|
||||||
|
common.getGroup(e.groupName):set(e.settingName, e.value)
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
}
|
51
files/builtin_scripts/scripts/omw/settings/interface.lua
Normal file
51
files/builtin_scripts/scripts/omw/settings/interface.lua
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
return function(player)
|
||||||
|
local core = require('openmw.core')
|
||||||
|
local types = require('openmw.types')
|
||||||
|
local world = not player and require('openmw.world')
|
||||||
|
|
||||||
|
|
||||||
|
local sections = require('scripts.omw.settings.sections')
|
||||||
|
local render = player and require('scripts.omw.settings.render') or nil
|
||||||
|
|
||||||
|
local settingChangeEvent = 'omwSettingChanged'
|
||||||
|
local globalSetEvent = 'omwSettingGlobalSet'
|
||||||
|
local registerEvent = 'omwSettingGroupRegistered'
|
||||||
|
|
||||||
|
local groups, scopes, SCOPE, isGlobal = sections.groups, sections.scopes, sections.SCOPE, sections.isGlobal
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
local saveScope = scopes[player and SCOPE.SavePlayer or SCOPE.SaveGlobal]
|
||||||
|
return {
|
||||||
|
interfaceName = 'Settings',
|
||||||
|
interface = {
|
||||||
|
getGroup = getGroup,
|
||||||
|
SCOPE = SCOPE,
|
||||||
|
registerGroup = not player and require('scripts.omw.settings.register') or nil,
|
||||||
|
registerType = player and render.registerType or nil,
|
||||||
|
},
|
||||||
|
engineHandlers = {
|
||||||
|
onLoad = function(saved)
|
||||||
|
if not player then groups:reset() end
|
||||||
|
saveScope:reset(saved)
|
||||||
|
end,
|
||||||
|
onSave = function()
|
||||||
|
return saveScope:asTable()
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
eventHandlers = {
|
||||||
|
[settingChangeEvent] = function(e)
|
||||||
|
getGroup(e.groupName):__changed(e.settingName, e.value)
|
||||||
|
end,
|
||||||
|
[globalSetEvent] = not player and function(e)
|
||||||
|
local setting = getSetting(e.groupName, e.settingName)
|
||||||
|
if isGlobal(setting.scope) then
|
||||||
|
setSettingValue(e.groupName, e.settingName, e.value)
|
||||||
|
else
|
||||||
|
error(('Unexpected Setting event for a non-global setting %s'):format(e.settingName))
|
||||||
|
end
|
||||||
|
end or nil,
|
||||||
|
[registerEvent] = player and render.onGroupRegistered or nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
26
files/builtin_scripts/scripts/omw/settings/player.lua
Normal file
26
files/builtin_scripts/scripts/omw/settings/player.lua
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
local common = require('scripts.omw.settings.common')
|
||||||
|
local render = require('scripts.omw.settings.render')
|
||||||
|
|
||||||
|
local saveScope = common.scopes[common.SCOPE.SavePlayer]
|
||||||
|
return {
|
||||||
|
interfaceName = 'Settings',
|
||||||
|
interface = {
|
||||||
|
SCOPE = common.SCOPE,
|
||||||
|
getGroup = common.getGroup,
|
||||||
|
registerRenderer = render.registerRenderer,
|
||||||
|
},
|
||||||
|
engineHandlers = {
|
||||||
|
onLoad = function(saved)
|
||||||
|
saveScope:reset(saved)
|
||||||
|
end,
|
||||||
|
onSave = function()
|
||||||
|
return saveScope:asTable()
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
eventHandlers = {
|
||||||
|
[common.EVENTS.SettingChanged] = function(e)
|
||||||
|
common.getGroup(e.groupName):__changed(e.settingName, e.value)
|
||||||
|
end,
|
||||||
|
[common.EVENTS.GroupRegistered] = render.onGroupRegistered,
|
||||||
|
}
|
||||||
|
}
|
76
files/builtin_scripts/scripts/omw/settings/register.lua
Normal file
76
files/builtin_scripts/scripts/omw/settings/register.lua
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
local world = require('openmw.world')
|
||||||
|
local types = require('openmw.types')
|
||||||
|
|
||||||
|
local common = require('scripts.omw.settings.common')
|
||||||
|
|
||||||
|
local groups, SCOPE = common.groups, common.SCOPE
|
||||||
|
|
||||||
|
local function validScope(scope)
|
||||||
|
local valid = false
|
||||||
|
for _, v in pairs(SCOPE) do
|
||||||
|
if v == scope then
|
||||||
|
valid = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return valid
|
||||||
|
end
|
||||||
|
|
||||||
|
local function validateSettingOptions(options)
|
||||||
|
if type(options.name) ~= 'string' then
|
||||||
|
error('Setting must have a name')
|
||||||
|
end
|
||||||
|
if options.default == nil then
|
||||||
|
error('Setting must have a default value')
|
||||||
|
end
|
||||||
|
if type(options.description) ~= 'string' then
|
||||||
|
error('Setting must have a description')
|
||||||
|
end
|
||||||
|
if not validScope(options.scope) then
|
||||||
|
error(('Invalid setting scope %s'):format(options.scope))
|
||||||
|
end
|
||||||
|
if type(options.renderer) ~= 'string' then
|
||||||
|
error('Setting must have a renderer')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function addSetting(group, options)
|
||||||
|
validateSettingOptions(options)
|
||||||
|
if group[options.name] then
|
||||||
|
error(('Duplicate setting name %s'):format(options.name))
|
||||||
|
end
|
||||||
|
group[options.name] = {
|
||||||
|
name = options.name,
|
||||||
|
scope = options.scope or SCOPE.Global,
|
||||||
|
default = options.default,
|
||||||
|
description = options.description,
|
||||||
|
renderer = options.renderer,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function registerGroup(groupName, list)
|
||||||
|
if groups:get(groupName) then
|
||||||
|
print(('Overwriting group %s'):format(groupName))
|
||||||
|
end
|
||||||
|
local settings = {}
|
||||||
|
for _, opt in ipairs(list) do
|
||||||
|
addSetting(settings, opt)
|
||||||
|
end
|
||||||
|
groups:set(groupName, settings)
|
||||||
|
for _, a in ipairs(world.activeActors) do
|
||||||
|
if a.type == types.Player and a:isValid() then
|
||||||
|
a:sendEvent(common.EVENTS.GroupRegistered, groupName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function onPlayerAdded(player)
|
||||||
|
for groupName in pairs(groups:asTable()) do
|
||||||
|
player:sendEvent(common.EVENTS.GroupRegistered, groupName)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
registerGroup = registerGroup,
|
||||||
|
onPlayerAdded = onPlayerAdded,
|
||||||
|
}
|
61
files/builtin_scripts/scripts/omw/settings/render.lua
Normal file
61
files/builtin_scripts/scripts/omw/settings/render.lua
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
local ui = require('openmw.ui')
|
||||||
|
local util = require('openmw.util')
|
||||||
|
|
||||||
|
local common = require('scripts.omw.settings.common')
|
||||||
|
|
||||||
|
local renderers = {}
|
||||||
|
local function registerRenderer(name, renderFunction)
|
||||||
|
renderers[name] = renderFunction
|
||||||
|
end
|
||||||
|
|
||||||
|
local groupOptions = {}
|
||||||
|
|
||||||
|
local function renderSetting(groupName, setting, value, index)
|
||||||
|
local renderFunction = renderers[setting.renderer]
|
||||||
|
if not renderFunction then
|
||||||
|
error(('Setting %s of %s has unknown renderer %s'):format(setting.name, groupName, setting.renderer))
|
||||||
|
end
|
||||||
|
local layout = renderFunction(setting, value or setting.default, function(value)
|
||||||
|
local group = common.getGroup(groupName)
|
||||||
|
group:set(setting.name, value)
|
||||||
|
local element = groupOptions[groupName].element
|
||||||
|
element.layout.content[setting.name] = renderSetting(groupName, setting, value, index)
|
||||||
|
element:update()
|
||||||
|
end)
|
||||||
|
layout.name = setting.name
|
||||||
|
-- temporary hacky position and size
|
||||||
|
layout.props = layout.props or {}
|
||||||
|
layout.props.position = util.vector2(0, 100 * (index - 1))
|
||||||
|
layout.props.size = util.vector2(400, 100)
|
||||||
|
return layout
|
||||||
|
end
|
||||||
|
|
||||||
|
local function onGroupRegistered(groupName)
|
||||||
|
local group = common.groups:get(groupName)
|
||||||
|
local layout = {
|
||||||
|
content = ui.content{},
|
||||||
|
}
|
||||||
|
local searchHints = { groupName }
|
||||||
|
local count = 0
|
||||||
|
for _, setting in pairs(group) do
|
||||||
|
count = count + 1
|
||||||
|
layout.content:add(renderSetting(groupName, setting, setting.default, count))
|
||||||
|
table.insert(searchHints, setting.name)
|
||||||
|
end
|
||||||
|
layout.props = {
|
||||||
|
size = util.vector2(400, 100 * count)
|
||||||
|
}
|
||||||
|
local options = {
|
||||||
|
name = groupName,
|
||||||
|
element = ui.create(layout),
|
||||||
|
searchHints = table.concat(searchHints, ' '),
|
||||||
|
}
|
||||||
|
groupOptions[groupName] = options
|
||||||
|
print(('registering group %s'):format(groupName))
|
||||||
|
ui.registerSettingsPage(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
onGroupRegistered = onGroupRegistered,
|
||||||
|
registerRenderer = registerRenderer,
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user