Run this code with the command bar:
local value = "true"
print(type(value))
--> string
local plugin = PluginManager():CreatePlugin()
plugin:SetSetting("key", value)
value = plugin:GetSetting("key")
print(type(value))
--> boolean
Run this code with the command bar:
local value = "true"
print(type(value))
--> string
local plugin = PluginManager():CreatePlugin()
plugin:SetSetting("key", value)
value = plugin:GetSetting("key")
print(type(value))
--> boolean
Isn’t that behavior due to using strings for all values?
Here are some other behaviors that would be interesting to note:
if saving “1” returns a number
if saving “” returns nil
EDIT: those were questions
EDIT 2: and yet my presumptions were correct
Things I am never using again:
[âś“] Plugin Settings
[quote] Things I am never using again:
[âś“] Plugin Settings [/quote]
I decided to make a module to help this.
Basically, it wraps the Plugin object so that GetSetting and SetSetting will use the HTTP JSON encoder, which is a lot more stable. No other changes should be required. In case they someday fix the plugin settings encoder, there’s also an option to enable “transition mode”, which will convert data encoded with BetterSettings to normal plugin settings.
--[[ BetterSettings
BetterSettings is a module that lets your plugin save settings properly.
This module returns a function that wraps around a Plugin object, replacing
the GetSetting and SetSetting functions with equivalents that use Roblox's
HTTP JSON encoder instead of the plugin settings JSON encoder, which does not
work properly.
This function receives three arguments:
- plugin: A Plugin object (e.g. the `plugin` variable).
- encodeKey: A string indicating the key where all settings will be stored.
- transitionMode: A bool indicating whether to transition from old settings to new.
This function returns an object that wraps the Plugin object. You should be
able to access all members normally.
To start using this module, the following line should be sufficient:
plugin = require(script.BetterSettings)(plugin, 'better_settings_key')
If Roblox someday fixes their JSON encoder, and you want to start using it,
then all you have to do is set transitionMode to `true`. This will change the
behavior of GetSetting and SetSetting: GetSetting will decode encodeKey if it
is present in the settings, and decode normally otherwise. Likewise,
SetSetting will encode data normally, and delete the old data in encodeKey if
it is present. For this reason, you should choose an encodeKey that you will
never use in your actual settings, so that there are no conflicts.
plugin = require(script.BetterSettings)(plugin, 'better_settings_key', true)
Once you have given enough time for the settings of the users of your plugin
to transition, then you should be able to stop using BetterSettings altogether
without any problems.
]]
-- Base64
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
function base64Encode(data)
return ((data:gsub('.', function(x)
local r,b='',x:byte()
for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
return r;
end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
if (#x < 6) then return '' end
local c=0
for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
return b:sub(c+1,c+1)
end)..({ '', '==', '=' })[#data%3+1])
end
function base64Decode(data)
data = string.gsub(data, '[^'..b..'=]', '')
return (data:gsub('.', function(x)
if (x == '=') then return '' end
local r,f='',(b:find(x)-1)
for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
return r;
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
if (#x ~= 8) then return '' end
local c=0
for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
return string.char(c)
end))
end
-- JSON
function jsonEncode(...)
local http = game:GetService('HttpService')
return pcall(http.JSONEncode, http, ...)
end
function jsonDecode(...)
local http = game:GetService('HttpService')
return pcall(http.JSONDecode, http, ...)
end
-- Wrapper
function instanceWrapper(instance, wrapper)
local proxy = newproxy(true)
local mt = getmetatable(proxy)
function mt.__index(t, k)
if wrapper[k] ~= nil then return wrapper[k] end
return instance[k]
end
function mt.__newindex(t, k, v)
instance[k] = v
end
function mt.__tostring(t)
return tostring(instance)
end
mt.__metatable = "The metatable is locked"
return proxy
end
return function(plugin, encodeKey, transitionMode)
if type(encodeKey) ~= 'string' then
error('encodeKey must be a string',2)
end
local wrapper = {}
function wrapper:GetSetting(key)
if key == nil then
error('Argument 1 missing or nil')
end
if type(key) ~= 'string' then
error(string.format('bad argument #1 to GetSetting (string expected, got %s)',type(key)),2)
end
local data = plugin:GetSetting(encodeKey)
if type(data) == 'string' then
data = base64Decode(data)
local success, settings = jsonDecode(data)
if success then
return settings[key]
end
elseif not transitionMode then
return plugin:GetSetting(key)
end
return nil
end
function wrapper:SetSetting(key, value)
if key == nil then
error('Argument 1 missing or nil')
end
if type(key) ~= 'string' then
error(string.format('bad argument #1 to GetSetting (string expected, got %s)',type(key)),2)
end
if transitionMode then
local settings
local data = plugin:GetSetting(encodeKey)
if type(data) == 'string' then
data = base64Decode(data)
local success, data = jsonDecode(data)
if success then
settings = data
else
settings = {}
end
else
settings = {}
end
settings[key] = value
local success, data = jsonEncode(settings)
if success then
data = base64Encode(data)
plugin:SetSetting(encodeKey, data)
end
else
-- Convert old settings to new
local data = plugin:GetSetting(encodeKey)
if type(data) == 'string' then
data = base64Decode(data)
local success, settings = jsonDecode(data)
if success then
for k,v in pairs(settings) do
plugin:SetSetting(k, v)
end
-- API requires a non-nil value, but that might change, so
-- try setting it to nil first.
if not pcall(plugin.SetSetting, plugin, encodeKey, nil) then
plugin:SetSetting(encodeKey, false)
end
end
end
plugin:SetSetting(key, value)
end
local settings = plugin:GetSetting(encodeKey)
if type(settings) == 'string' then
settings = base64Decode(settings)
local success, settings = jsonDecode(settings)
if success then
return settings[key]
end
elseif not transitionMode then
return plugin:GetSetting(key)
end
return nil
end
return instanceWrapper(plugin, wrapper)
end