-- DSL PNP 4.0 Main Script
-- By: Zachary Hiland
-- 2/09/2014
-- Not actually hosted, shown as included in testing.xml
dslpnp = dslpnp or {}
dslpnp.update = dslpnp.update or {}
dslpnp.config = dslpnp.config or {}
local defaults = {
general_scripts = {
"Gauges",
"Window Manager"
},
basic_scripts = {
"growl",
"triggers",
"aliases",
"timers",
"fileIO",
"extensions",
"data",
},
scripts_list = {
"borders",
"statusbar",
"support",
"filesend",
"character",
},
initialize = {
"borders",
"statusbar",
"support",
"filesend",
"character",
},
connect_events = {
"resetTimers",
"resetTriggers",
"resetAliases",
},
file_path = getMudletHomeDir() .. "/PNP/",
download_path = "http://shatteredcloud.com/scripts/",
package_name = "testing",
version_check_download = "versions.txt",
version_check_save = "version_check.txt",
versions_file_name = "versions.txt",
}
local download_queue = download_queue or {}
local downloading = false
local available = {}
local unavailable = {}
local scripts_list = {}
local init_list = {}
local event_list = {}
-- Creates a duplicate of the given table
-- This is a local copy of table.copy used by table.update
local function table_copy(tbl)
local new = {}
for k,v in pairs(tbl) do
if type(v) == "table" then
new[k] = table_copy(v)
else
new[k] = v
end
end
return new
end
-- Updates the first table with values from the second table
-- This is a local copy of table.update as it will function in the next Mudlet release
local function table_update(t1, t2)
local tbl = table_copy(t1)
for k,v in pairs(t2) do
if type(v) == "table" then
tbl[k] = table_update(tbl[k] or {}, v)
else
tbl[k] = v
end
end
return tbl
end
local function fileOpen(filename,mode)
local errors
mode = mode or "read"
assert(table.contains({"read","write","append","modify"},mode),"Invalid mode: must be 'read', 'write', 'append', or 'modify'.")
if mode ~= "write" then
local info = lfs.attributes(filename)
if not info then
errors = "Invalid filename: no such file."
return nil, errors
end
if info.mode ~= "file" then
errors = "Invalid filename: path points to a directory."
return nil, errors
end
end
local file = {}
file.name = filename
file.mode = mode
file.type = "fileIO_file"
file.contents = {}
if file.mode == "read" or file.mode == "modify" then
local tmp = io.open(file.name,"r")
local linenum = 1
for line in tmp:lines() do
file.contents[linenum] = line
linenum = linenum + 1
end
tmp:close()
end
setmetatable(file,dslpnp.fileIO)
return file, nil
end
local function fileClose(file)
assert(file.type == "fileIO_file", "Invalid file: must be file returned by fileIO.open.")
local tmp
if file.mode == "write" then
tmp = io.open(file.name,"w")
elseif file.mode == "append" then
tmp = io.open(file.name,"a")
elseif file.mode == "modify" then
tmp = io.open(file.name,"w+")
end
if tmp then
for k,v in ipairs(file.contents) do
tmp:write(v .. "\n")
end
tmp:flush()
tmp:close()
tmp = nil
end
return true
end
-- THIS FUNCTION INITIALIZES SCRIPTS, GETS CALLED ON CONNECT/RECONNECT
function dslpnp.initialize()
init_list = table.n_union(defaults.initialize, dslpnp.config.initialize or {})
event_list = table.n_union(defaults.connect_events, dslpnp.config.connect_events or {})
-- run required events
for k,v in ipairs(event_list) do
raiseEvent(v)
end
-- initialize scripts
for k,v in ipairs(init_list) do
raiseEvent("onConfig",v)
end
end
local function load_package()
-- uninstall old package
uninstallPackage(defaults.package_name)
-- install new package
installPackage(defaults.file_path .. defaults.package_name .. ".xml")
end
local function check_available()
for k, v in ipairs(scripts_list) do
-- check if script is listed in available or unavailable table
if (not table.contains(available, v)) and (not table.contains(unavailable,v)) then
-- if script not accounted for, not all scripts are loaded, return false
return false
end
end
-- if all scripts accounted for, return true
return true
end
local function load_scripts()
local path, name
if check_available() then
for k, v in ipairs(available) do
if table.contains(unavailable,v) then
table.remove(unavailable,table.index_of(unavailable, v))
end
path = defaults.file_path .. v .. ".lua"
name = string.gsub(v,"DSL_PNP_", "")
if io.exists(path) then
dofile(path)
print("Script: " .. name .. " loaded successfully.")
else
print("Script: " .. name .. " does not exist.")
end
end
echo("\n")
for k,v in ipairs(unavailable) do
name = string.gsub(v,"DSL_PNP_", "")
print("Script: " .. name .. " not loaded.")
end
echo("\n")
dslpnp.initialize()
end
end
local function start_download()
-- get info from queue
local info = download_queue[1]
if info then
local path, address = info[1], info[2]
-- remove current item from queue
table.remove(download_queue,1)
-- begin download
downloadFile(path,address)
downloading = true
else
load_scripts()
end
end
local function queue_download(path, address)
-- add item to queue
table.insert(download_queue, {path, address})
if not downloading then
-- start new download if none in progress
start_download()
end
end
local function get_version_check()
-- create PNP folder in Mudlet home directory if necessary
lfs.mkdir(getMudletHomeDir() .. "/PNP")
-- download current version info
queue_download(defaults.file_path .. defaults.version_check_save, defaults.download_path .. defaults.version_check_download)
end
local function check_versions()
local version_path = defaults.file_path .. defaults.versions_file_name
local check_path = defaults.file_path .. defaults.version_check_save
local new_version, old_version
local check_file, version_file
local found
-- read in check file
check_file = fileOpen(check_path,"read")
if io.exists(version_path) then
-- read in version file
version_file = fileOpen(version_path,"modify")
else
-- create new version file
version_file = fileOpen(version_path,"write")
end
-- check versions for all loaded scripts
for k, v in ipairs(scripts_list) do
new_version = nil
found = false
-- find new version info for current script
for k2, v2 in ipairs(check_file.contents) do
if string.find(v2,v) then
found = true
new_version = string.match(v2,"[%w%.]+ : ([%w%.]+)")
old_version = nil
-- find old version info for current script
for k3, v3 in ipairs(version_file.contents) do
if string.find(v3,v) then
old_version = string.match(v3,"[%w%.]+ : ([%w%.]+)")
-- update old version info
version_file.contents[k3] = v2
break
end
end
-- compare version info
if new_version ~= old_version then
-- download new version of old script
queue_download(defaults.file_path .. v .. ".lua", defaults.download_path .. v .. ".lua")
if not old_version then
-- insert missing version info
table.insert(version_file.contents, v2)
end
else
table.insert(available, v)
end
break
end
end
-- scripts not found are listed as unavailable
if not found then table.insert(unavailable, v) end
end
-- write new version info to file
fileClose(version_file)
-- close version check file
fileClose(check_file)
-- try to load scripts
load_scripts()
end
local function finish_download(path)
-- start next download in queue
if download_queue[1] then
start_download()
else
downloading = false
end
-- run version checking once file downloaded
if string.find(path,defaults.version_check_save) then
check_versions()
elseif string.find(path,".xml") then
load_package()
else
for k, v in ipairs(scripts_list) do
if string.find(path,v) then
table.insert(available,v)
break
end
end
if not downloading then
load_scripts()
end
end
end
local function fail_download(...)
-- begin next download
start_download()
-- add failed download to list of unavailable scripts
table.insert(unavailable, arg[1])
end
function dslpnp.update.update_package()
-- create PNP folder in Mudlet home directory if necessary
lfs.mkdir(getMudletHomeDir() .. "/PNP")
-- download newest package
queue_download(defaults.file_path .. defaults.package_name .. ".xml", defaults.download_path .. defaults.package_name .. ".xml")
end
function dslpnp.update.update_scripts()
available = {}
unavailable = {}
dslpnp.config.basic_scripts = table_update(defaults.basic_scripts, dslpnp.config.basic_scripts or {})
dslpnp.config.scripts_list = dslpnp.config.scripts_list or defaults.scripts_list or {}
dslpnp.config.general_scripts = table_update(defaults.general_scripts, dslpnp.config.general_scripts or {})
scripts_list = table.n_union(dslpnp.config.basic_scripts, dslpnp.config.scripts_list)
for k,v in ipairs(scripts_list) do
scripts_list[k] = "DSL_PNP_" .. string.title(v)
end
scripts_list = table.n_union(scripts_list, dslpnp.config.general_scripts)
get_version_check()
end
function dslpnp.update.clear_versions()
local version_path = defaults.file_path .. defaults.versions_file_name
if io.exists(version_path) then
-- read in version file
version_file = fileOpen(version_path,"modify")
version_file.contents = {}
fileClose(version_file)
end
end
function dslpnp.update.eventHandler(event, ...)
if event == "sysConnectionEvent" then
dslpnp.update.update_scripts()
elseif event == "sysDownloadDone" then
finish_download(...)
elseif event == "sysDownloadError" then
fail_download(...)
end
end
registerAnonymousEventHandler("sysConnectionEvent","dslpnp.update.eventHandler")
registerAnonymousEventHandler("sysDownloadDone", "dslpnp.update.eventHandler")
registerAnonymousEventHandler("sysDownloadError", "dslpnp.update.eventHandler")
Self Updating Package Script
Self Updating Package Script
So, someone requested a copy of my self updating package script, so here it is. Please keep in mind that it is still a work in progress, and this isn't stripped down to be generic (so it is still structured to be part of the larger script package that I built and maintain for a game I play). Basically, it grabs a version check file from a remote server, compares the versions listed there with the versions stored locally, and then downloads any files that are out of date. Once all files are downloaded, all of the files are loaded in sequence, using doFile. Comments and suggestions are welcome.
Re: Self Updating Package Script
Sorry to ask but im a little confused, what does this represent?
local defaults = {
general_scripts = {
"Gauges",
"Window Manager"
},
basic_scripts = {
"growl",
"triggers",
"aliases",
"timers",
"fileIO",
"extensions",
"data",
},
scripts_list = {
"borders",
"statusbar",
"support",
"filesend",
"character",
},
initialize = {
"borders",
"statusbar",
"support",
"filesend",
"character",
},
connect_events = {
"resetTimers",
"resetTriggers",
"resetAliases",
},
file_path = getMudletHomeDir() .. "/PNP/",
version_check_download = "version.txt",
version_check_save = "version_check.txt",
versions_file_name = "versions.txt",
}
the things I deleted I understand what that does its the others which Im unsure on :/
local defaults = {
general_scripts = {
"Gauges",
"Window Manager"
},
basic_scripts = {
"growl",
"triggers",
"aliases",
"timers",
"fileIO",
"extensions",
"data",
},
scripts_list = {
"borders",
"statusbar",
"support",
"filesend",
"character",
},
initialize = {
"borders",
"statusbar",
"support",
"filesend",
"character",
},
connect_events = {
"resetTimers",
"resetTriggers",
"resetAliases",
},
file_path = getMudletHomeDir() .. "/PNP/",
version_check_download = "version.txt",
version_check_save = "version_check.txt",
versions_file_name = "versions.txt",
}
the things I deleted I understand what that does its the others which Im unsure on :/
Re: Self Updating Package Script
As stated, that is my script as is, not turned into some generalized format. So that is telling it what scripts to download, read in, and then later initialize for my script package. It also includes some events that it fires once everything is loaded up (because some of my scripts want those events), as well as the location to save files, and the file names of the file to download, what to save it as, and the local file to read to compare the downloaded version file to.
For most purposes, you can just use getMudletHomeDir instead of the file_path variable I have, most people won't need the connect_events or initialize tables at all, and the three different lists of script names can just be condensed into one list, because you don't need to have them broken down by category the way that I have them.
For most purposes, you can just use getMudletHomeDir instead of the file_path variable I have, most people won't need the connect_events or initialize tables at all, and the three different lists of script names can just be condensed into one list, because you don't need to have them broken down by category the way that I have them.
Re: Self Updating Package Script
I've been working on something similar, though different as well lately: a package manager fo Mudlet.
The basic idea is to build something like apt on Ubuntu, wich is able to resolve dependencies, look for updates and find new packages hosted in one place. That would have the advantage, that not every package needs the update code and existing packages are easily integrated into the system. The disadvantage is, that a dedicated server instance is needed for my idea.
I have both components on my github, though they are not quite finished yet.
ETA: it might help to actually add my github, right? https://github.com/keneanung
The basic idea is to build something like apt on Ubuntu, wich is able to resolve dependencies, look for updates and find new packages hosted in one place. That would have the advantage, that not every package needs the update code and existing packages are easily integrated into the system. The disadvantage is, that a dedicated server instance is needed for my idea.
I have both components on my github, though they are not quite finished yet.
ETA: it might help to actually add my github, right? https://github.com/keneanung
Re: Self Updating Package Script
So, I'm not going to lie, I find reading code in XML format rather difficult. But, from scanning what you have, do you basically just have it redownload all of the managed packages every time? Or do you have some sort of version management going on that I missed?
Re: Self Updating Package Script
Of course not. The client stores the current version it has installed in a database (like you have in a plain text file) and is able to retrieve the version strings and dependencies as well as some other things of all available packages via JSON from the server(s). If a newer version is found on one of the servers, it downloads the package and installs it.
You can also install new packages via the package manager or uninstall existing packages (important, so it doesn't reinstall it on update).
You can also install new packages via the package manager or uninstall existing packages (important, so it doesn't reinstall it on update).
Re: Self Updating Package Script
Upon further analysis of your code we have a quite similar approach, although mine seems to be a bit less hardcoded.
I uploaded my current work in progress to github into https://github.com/keneanung/MudletPack ... ee/cleanup which contains some bugfixes but primarily localizing and creation of functions to have everything in one place.
I uploaded my current work in progress to github into https://github.com/keneanung/MudletPack ... ee/cleanup which contains some bugfixes but primarily localizing and creation of functions to have everything in one place.
Re: Self Updating Package Script
Yeah, I also do this, albeit differently. I'm loading my system via Lua modules which allows for quick and easy updating without the need to restart Mudlet.
Then the Mudlet side of things:
Still a work in progress, but yet another way this can be accomplished.
Re: Self Updating Package Script
I just noticed most of you guys have things like -
function get_modules()
local exceptions = {
"string",
"package",
"_G",
"os",
"table",
"math",
"coroutine",
"luasql",
"debug",
"rex_pcre",
"lfs",
"io",
"luasql.sqlite3",
"gmod",
"zip",
"socket"
}
-
How or what do you do to get that? Or what does that represent? As each time I export/package manger export it only gives me a .zip of the whole system...
function get_modules()
local exceptions = {
"string",
"package",
"_G",
"os",
"table",
"math",
"coroutine",
"luasql",
"debug",
"rex_pcre",
"lfs",
"io",
"luasql.sqlite3",
"gmod",
"zip",
"socket"
}
-
How or what do you do to get that? Or what does that represent? As each time I export/package manger export it only gives me a .zip of the whole system...
Re: Self Updating Package Script
Just display the package.loaded table for that info.
It's less a Mudlet thing and more a Lua thing. Because of the way I load my modules and update, I had to add that blacklist as setting those particular modules to nil would be.... Bad, to say the least.
It's less a Mudlet thing and more a Lua thing. Because of the way I load my modules and update, I had to add that blacklist as setting those particular modules to nil would be.... Bad, to say the least.