ConfigManager - Handy Library for Options Menus

Showcase your libraries, tools and other projects that help your fellow love users.
Post Reply
acb5649
Prole
Posts: 2
Joined: Sat Nov 26, 2016 10:04 pm

ConfigManager - Handy Library for Options Menus

Post by acb5649 »

I'm in the middle of making my first Lua game and I needed a way to save the user's preferences for the game window. If they set my game to fullscreen mode, I think it had better reopen in fullscreen mode. It works by directly editing the conf.lua file instead of using an outside file to save values. It's my first published bit of Lua code, so I'd love any suggestions or recommendations.

https://github.com/acb5649/ConfigManager
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: ConfigManager - Handy Library for Options Menus

Post by zorg »

acb5649 wrote:I'm in the middle of making my first Lua game and I needed a way to save the user's preferences for the game window. If they set my game to fullscreen mode, I think it had better reopen in fullscreen mode. It works by directly editing the conf.lua file instead of using an outside file to save values. It's my first published bit of Lua code, so I'd love any suggestions or recommendations.

https://github.com/acb5649/ConfigManager
First thing, while using lua io functions isn't a sin, this won't work in many cases, like when you zipped up your project into a .love file.
I'd use love's own filesystem stuff instead. (Though i must admit, i thought io.open needed a full absolute path... if it works for you with only "conf.lua", then that's a surprise for me)

Second issue would be that love.filesystem doesn't allow you to write to the application directory, so even if you tried to write something to conf.lua, it will write that file into your save directory, if you have set an identity for the project, that is.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
acb5649
Prole
Posts: 2
Joined: Sat Nov 26, 2016 10:04 pm

Re: ConfigManager - Handy Library for Options Menus

Post by acb5649 »

Thank you, I feel a little silly for forgetting Love projects are zipped for distribution.

I guess I'll leave it up while I work on a replacement, since I can't use this code for release either.

EDIT:
New working version is now online, but it does use extra files stored in the LOVE save directory.
It now uses 100% love.filesystem calls instead of io, and does not impact startup speed. See the included conf.lua file for usage example. Basically, if you want to let the user control a configuration, like MSAA, use:

Code: Select all

t.window.msaa = loadMSAA() or 0


The config file will check the save directory for a file with the MSAA information inside. If such file is not found, is defaults to 0 because of the "or" condition.

Again, any and all feedback is appreciated. Look how well it went last time!
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: ConfigManager - Handy Library for Options Menus

Post by ivan »

Yes, you should always save progress and settings in the appdata/user directory,
writing files to the installation directory is not good practice and might not work depending on the user's permissions.
Having said that, the code needs work.
Saving a separate ".dat" file for each setting is not ideal,
the easiest option is to save everything in a single Lua table or use bartbes "inifile".
Also, note that:

Code: Select all

function loadWindowPosWidth()
  local file = "windowwidth.dat"
  if love.filesystem.exists(file) then
    local size = love.filesystem.getSize(file)
    local data = love.filesystem.read(file, size)
    return tonumber(data)
  else
    return nil
  end
end
Is the same as:

Code: Select all

function loadWindowPosWidth()
  local file = "windowwidth.dat"
  if love.filesystem.exists(file) then
    local size = love.filesystem.getSize(file)
    local data = love.filesystem.read(file, size)
    return tonumber(data)
  end
end
PS. Subfolders for each "version" of the configmanager is a good idea too, since APIs change over time.
Either that or try to include the savefile version somewhere in there.
User avatar
Positive07
Party member
Posts: 1014
Joined: Sun Aug 12, 2012 4:34 pm
Location: Argentina

Re: ConfigManager - Handy Library for Options Menus

Post by Positive07 »

And this:
ivan wrote:

Code: Select all

function loadWindowPosWidth()
  local file = "windowwidth.dat"
  if love.filesystem.exists(file) then
    local size = love.filesystem.getSize(file)
    local data = love.filesystem.read(file, size)
    return tonumber(data)
  end
end
As this, since read tries to read it all if no size is specified

Code: Select all

function loadWindowPosWidth()
  local file = "windowwidth.dat"
  if love.filesystem.exists(file) then
    local data = love.filesystem.read(file)
    return tonumber(data)
  end
end
for i, person in ipairs(everybody) do
[tab]if not person.obey then person:setObey(true) end
end
love.system.openURL(github.com/pablomayobre)
User avatar
ivan
Party member
Posts: 1915
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

Re: ConfigManager - Handy Library for Options Menus

Post by ivan »

I think the entire API could be rewritten in 2 functions:

Code: Select all

-- very bad example writing each key to a separate file
function api.loadString(key)
  local file = key..".dat"
  if love.filesystem.exists(file) then
    return love.filesystem.read(file)
  end
end

-- load custom values if their type matches the default
function api.load(defaults, destination)
  -- overwrites the defaults if no destination table is specified
  destination = destination or defaults
  -- iterate default values
  for k, v in pairs(defaults) 
    local expected = type(v)
    -- convert based on expected type
    local custom = api.loadString(k)
    if expected == "boolean" then
       if custom == "true" then
         custom = true
       elseif custom == "false" then
         custom = false
       end
    elseif expected == "number" then
       custom = tonumber(custom)
    end
    -- check loaded type vs default type
    if type(custom) == expected then
      destination[k] = custom
    end
  end
  return destination
end
Basically you need a table of default values and you overwrite that:

Code: Select all

local defaults =
{
Width=800,
Height=600,
Borderless=true,
Resizable=0,
MinWidth=0,
MinHeight=0,
Fullscreen=true,
FullscreenType="string",
Vsync=true,
MSAA=0,
Display=0,
HighDPI=true,
WindowPosWidth=0
}

api.load(defaults)
PS. Haven't tested the code, but I hope the technique is clear.
I used this approach in my games and it works quite well, except that you have to do some extra validation,
in particular with paired values like width/height...
Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest