1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-04-01 04:36:41 +00:00

openmw_aux.settings, rework to support local scripts

This commit is contained in:
uramer 2022-04-18 08:42:02 +02:00
parent 38e0f5c0af
commit a35bc1dee0
7 changed files with 204 additions and 195 deletions

View file

@ -0,0 +1,4 @@
local common = require('scripts.omw.settings.common')
return {
group = common.group,
}

View file

@ -1,23 +1,20 @@
local prequire = function(path)
local status, result = pcall(function()
return require(path)
end)
return status and result or nil
end
local storage = require('openmw.storage')
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 selfObject
do
local success, result = pcall(function() return require('openmw.self') end)
selfObject = success and result or nil
end
local playerObject = selfObject and selfObject.type == types.Player and selfObject or nil
local eventPrefix = 'omwSettings'
local EVENTS = {
SettingChanged = 'omwSettingsChanged',
SettingSet = 'omwSettingsGlobalSet',
GroupRegistered = 'omwSettingsGroupRegistered',
SettingChanged = eventPrefix .. 'Changed',
SetValue = eventPrefix .. 'GlobalSetValue',
GroupRegistered = eventPrefix .. 'GroupRegistered',
RegisterGroup = eventPrefix .. 'RegisterGroup',
Subscribe = eventPrefix .. 'Subscribe',
}
local SCOPE = {
@ -27,124 +24,160 @@ local SCOPE = {
SavePlayer = 'SavePlayer',
}
local groups = storage.globalSection('OMW_Settings_Groups')
local saveGlobalSection = storage.globalSection('OMW_Settings_SaveGlobal')
if isGlobalScript then
groups:removeOnExit()
saveGlobalSection:removeOnExit()
local function isPlayerScope(scope)
return scope == SCOPE.Player or scope == SCOPE.SavePlayer
end
local savePlayerSection = nil
if isPlayerScript then
savePlayerSection = storage.playerSection('OMW_Setting_SavePlayer')
savePlayerSection:removeOnExit()
local function isSaveScope(scope)
return scope == SCOPE.SaveGlobal or scope == SCOPE.SavePlayer
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 prefix = 'omw_settings_'
local settingsPattern = prefix .. 'settings_%s%s'
local function isGlobalScope(scope)
return scope == SCOPE.Global or scope == SCOPE.SaveGlobal
local groupsSection = storage.globalSection(prefix .. 'groups')
if groupsSection.removeOnExit then
groupsSection:removeOnExit()
end
local function getSetting(groupKey, settingKey)
local group = groups:get(groupKey)
if not group then
error('Unknown group')
end
local setting = group.settings[settingKey]
if not setting then
error('Unknown setting')
end
return setting
end
local function getSettingValue(groupKey, settingKey)
local setting = getSetting(groupKey, settingKey)
local scopeSection = scopes[setting.scope]
if not scopeSection then
error(('Setting %s is not available in this context'):format(setting.key))
end
if not scopeSection:get(groupKey) then
scopeSection:set(groupKey, {})
end
return scopeSection:get(groupKey)[setting.key] 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
local function values(groupKey, scope)
local player = isPlayerScope(scope)
local save = isSaveScope(scope)
local sectionKey = settingsPattern:format(groupKey, save and '_save' or '')
local section
if player then
section = storage.playerSection and storage.playerSection(sectionKey) or nil
else
self:sendEvent(EVENTS.SettingChanged, event)
section = storage.globalSection(sectionKey)
end
if save and section and section.removeOnExit then
section:removeOnExit()
end
return section
end
local function saveScope(scope)
local saved = {}
for _, group in pairs(groupsSection:asTable()) do
saved[group.key] = values(group.key, scope):asTable()
end
return saved
end
local function loadScope(scope, saved)
if not saved then return end
for _, group in pairs(saved) do
values(group.key, scope):reset(saved[group.key])
end
end
local function setSettingValue(groupKey, settingKey, value)
local setting = getSetting(groupKey, settingKey)
local function groupSubscribeEvent(groupKey)
return ('%sSubscribe%s'):format(eventPrefix, groupKey)
end
local subscriptions = {}
local function handleSubscription(event)
if not subscriptions[event.groupKey] then
subscriptions[event.groupKey] = {}
end
table.insert(subscriptions[event.groupKey], event.object or false)
end
local function subscribe(self)
local groupKey = rawget(self, 'groupKey')
local event = {
groupName = groupKey,
settingName = setting.key,
value = value,
groupKey = groupKey,
object = selfObject,
}
if isPlayerScript and isGlobalScope(setting.scope) then
core.sendGlobalEvent(EVENTS.SettingSet, event)
return
core.sendGlobalEvent(EVENTS.Subscribe, event)
if playerObject then
playerObject:sendEvent(EVENTS.Subscribe, event)
end
local scopeSection = scopes[setting.scope]
if not scopeSection:get(groupKey) then
scopeSection:set(groupKey, {})
end
local copy = scopeSection:getCopy(groupKey)
copy[setting.key] = value
scopeSection:set(groupKey, copy)
notifySettingChange(setting.scope, event)
return groupSubscribeEvent(groupKey)
end
local groupMeta = {
__index = {
get = function(self, settingKey)
return getSettingValue(self.key, settingKey)
end,
set = function(self, settingKey, value)
setSettingValue(self.key, settingKey, value)
end,
onChange = function(self, callback)
table.insert(self.__callbacks, callback)
end,
__changed = function(self, settingKey, value)
for _, callback in ipairs(self.__callbacks) do
callback(settingKey, value)
__newindex = function(self, settingKey, value)
local group = groupsSection:get(rawget(self, 'groupKey'))
local setting = group.settings[settingKey]
if not setting then
error(('Setting %s does not exist'):format(settingKey))
end
local section = values(group.key, setting.scope)
local event = {
groupKey = group.key,
settingKey = settingKey,
value = value,
}
if section.set then
section:set(settingKey, value)
if playerObject then
playerObject:sendEvent(EVENTS.SettingChanged, event)
else
core.sendGlobalEvent(EVENTS.SettingChanged, event)
end
end,
},
if subscriptions[group.key] then
local eventKey = groupSubscribeEvent(group.key)
for _, object in ipairs(subscriptions[group.key]) do
if object then
object:sendEvent(eventKey, event)
else
core.sendGlobalEvent(eventKey, event)
end
end
end
else
if isPlayerScope(setting.scope) then
error(("Can't change player scope setting %s from global scope"):format(settingKey))
else
core.sendGlobalEvent(EVENTS.SetValue, event)
end
end
end,
__index = function(self, key)
if key == "subscribe" then return subscribe end
local settingKey = key
local group = groupsSection:get(rawget(self, 'groupKey'))
local setting = group.settings[settingKey]
if not setting then
error(('Unknown setting %s'):format(settingKey))
end
local section = rawget(self, 'sections')[setting.scope]
if not section then
error(("Can't access setting %s from scope %s"):format(settingKey, setting.scope))
end
return section:get(setting.key) or setting.default
end,
}
local cachedGroups = {}
local function getGroup(groupKey)
if not cachedGroups[groupKey] then
cachedGroups[groupKey] = setmetatable({
key = groupKey,
__callbacks = {},
}, groupMeta)
local function group(groupKey)
if not groupsSection:get(groupKey) then
print(("Settings group %s wasn't registered yet"):format(groupKey))
end
return cachedGroups[groupKey]
local s = {}
for _, scope in pairs(SCOPE) do
local section = values(groupKey, scope)
if section then
s[scope] = section
end
end
return setmetatable({
groupKey = groupKey,
sections = s,
}, groupMeta)
end
return {
EVENTS = EVENTS,
SCOPE = SCOPE,
scopes = scopes,
groups = groups,
getGroup = getGroup,
EVENTS = EVENTS,
isPlayerScope = isPlayerScope,
isSaveScope = isSaveScope,
values = values,
groups = function()
return groupsSection
end,
saveScope = saveScope,
loadScope = loadScope,
group = group,
handleSubscription = handleSubscription,
}

View file

@ -1,30 +1,44 @@
local common = require('scripts.omw.settings.common')
local register = require('scripts.omw.settings.register')
local world = require('openmw.world')
local types = require('openmw.types')
local saveScope = common.scopes[common.SCOPE.SaveGlobal]
return {
interfaceName = 'Settings',
interface = {
SCOPE = common.SCOPE,
getGroup = common.getGroup,
group = common.group,
registerGroup = register.registerGroup,
},
engineHandlers = {
onLoad = function(saved)
common.groups:reset()
saveScope:reset(saved)
common.groups():reset()
common.loadScope(common.SCOPE.SaveGlobal, saved)
end,
onSave = function()
return saveScope:asTable()
common.saveScope(common.SCOPE.SaveGlobal)
end,
onPlayerAdded = register.onPlayerAdded,
},
eventHandlers = {
[common.EVENTS.SettingChanged] = function(e)
common.getGroup(e.groupName):__changed(e.settingName, e.value)
[common.EVENTS.SetValue] = function(event)
local group = common.group(event.groupKey)
group[event.settingKey] = event.value
end,
[common.EVENTS.SettingSet] = function(e)
common.getGroup(e.groupName):set(e.settingName, e.value)
[common.EVENTS.RegisterGroup] = function(options)
if common.groups():get(options.key) then return end
register.registerGroup(options)
end,
}
[common.EVENTS.SettingChanged] = function(event)
local setting = common.groups():get(event.groupKey).settings[event.settingKey]
if common.isPlayerScope(setting.scope) then
for _, a in ipairs(world.activeActors) do
if a.type == types.Player and a:isValid() then
a:sendEvent(common.EVENTS.SettingChanged, event)
end
end
end
end,
[common.EVENTS.Subscribe] = common.handleSubscription,
},
}

View file

@ -1,51 +0,0 @@
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

View file

@ -1,8 +1,10 @@
local common = require('scripts.omw.settings.common')
local render = require('scripts.omw.settings.render')
local ui = require('openmw.ui')
local async = require('openmw.async')
local util = require('openmw.util')
local core = require('openmw.core')
render.registerRenderer('text', function(value, set, arg)
return {
@ -20,26 +22,27 @@ render.registerRenderer('text', function(value, set, arg)
}
end)
local saveScope = common.scopes[common.SCOPE.SavePlayer]
return {
interfaceName = 'Settings',
interface = {
SCOPE = common.SCOPE,
getGroup = common.getGroup,
group = common.group,
registerRenderer = render.registerRenderer,
registerGroup = function(options)
core.sendGlobalEvent(common.EVENTS.RegisterGroup, options)
end,
},
engineHandlers = {
onLoad = function(saved)
saveScope:reset(saved)
common.loadScope(common.SCOPE.SavePlayer, saved)
end,
onSave = function()
return saveScope:asTable()
common.saveScope(common.SCOPE.SavePlayer)
end,
},
eventHandlers = {
[common.EVENTS.SettingChanged] = function(e)
common.getGroup(e.groupName):__changed(e.settingName, e.value)
end,
[common.EVENTS.GroupRegistered] = render.onGroupRegistered,
[common.EVENTS.SettingChanged] = render.onSettingChanged,
[common.EVENTS.Subscribe] = common.handleSubscription,
}
}

View file

@ -3,11 +3,9 @@ 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
for _, v in pairs(common.SCOPE) do
if v == scope then
valid = true
break
@ -41,7 +39,7 @@ local function addSetting(settings, options)
end
settings[options.key] = {
key = options.key,
scope = options.scope or SCOPE.Global,
scope = options.scope,
default = options.default,
renderer = options.renderer,
argument = options.argument,
@ -70,14 +68,14 @@ local function validateGroupOptions(options)
end
local function registerGroup(options)
local groups = common.groups()
validateGroupOptions(options)
if groups:get(options.key) then
print(('Overwriting group %s'):format(options.key))
end
error(('Duplicate group %s'):format(options.key))
end
local group = {
key = options.key,
localization = options.localization,
name = options.name,
description = options.description,
@ -95,7 +93,7 @@ local function registerGroup(options)
end
local function onPlayerAdded(player)
for groupName in pairs(groups:asTable()) do
for groupName in pairs(common.groups():asTable()) do
player:sendEvent(common.EVENTS.GroupRegistered, groupName)
end
end

View file

@ -40,10 +40,10 @@ local function renderSetting(groupKey, setting, value)
if not renderFunction then
error(('Setting %s of %s has unknown renderer %s'):format(setting.key, groupKey, setting.renderer))
end
local group = common.getGroup(groupKey)
value = value or group:get(setting.key)
local group = common.group(groupKey)
value = value or group[setting.key]
local set = function(value)
group:set(setting.key, value)
group[setting.key] = value
renderSetting(groupKey, setting, value)
end
local element = groupOptions[groupKey].element
@ -92,7 +92,7 @@ local function renderSetting(groupKey, setting, value)
end
local function renderGroup(groupKey)
local group = common.groups:get(groupKey)
local group = common.groups():get(groupKey)
local element = groupOptions[groupKey].element
local localization = groupOptions[groupKey].localization
element.layout = {
@ -143,19 +143,27 @@ local function renderGroup(groupKey)
end
local function onGroupRegistered(groupKey)
local group = common.groups:get(groupKey)
local group = common.groups():get(groupKey)
local loc = core.l10n(group.localization)
local options = {
name = groupKey,
name = loc(group.name),
element = ui.create{},
searchHints = '',
localization = core.l10n(group.localization),
localization = loc,
}
groupOptions[groupKey] = options
renderGroup(groupKey)
ui.registerSettingsPage(options)
end
local function onSettingChanged(event)
local group = common.groups():get(event.groupKey)
local setting = group.settings[event.settingKey]
renderSetting(event.groupKey, setting, event.value)
end
return {
onGroupRegistered = onGroupRegistered,
onSettingChanged = onSettingChanged,
registerRenderer = registerRenderer,
}