Skip to content
Snippets Groups Projects
Commit 0674afaf authored by DarkGod's avatar DarkGod
Browse files

Addons/DLCs can be auto-updated ingame

parent c6dbd611
No related branches found
No related tags found
No related merge requests found
......@@ -690,6 +690,21 @@ function _M:checkAddonHash(module, addon, md5)
return true
end
function _M:checkAddonUpdates(list)
if not self.auth then return nil, "no online profile active" end
if #list == 0 then return nil, "nothing to update" end
core.profile.pushOrder(table.serialize{o="CheckAddonUpdates", list=list})
local ok = false
self:waitEvent("CheckAddonUpdates", function(e) ok = e.ok end, 10000)
if not ok then return nil, "bad game addon version" end
ok = ok:unserialize()
print("[ONLINE PROFILE] addon update list returned")
table.print(ok)
return ok
end
function _M:checkBatchHash(list)
if not self.auth then return nil, "no online profile active" end
if config.settings.cheat then return nil, "cheat mode active" end
......
-- TE4 - T-Engine 4
-- Copyright (C) 2009 - 2014 Nicolas Casalini
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--
-- Nicolas Casalini "DarkGod"
-- darkgod@te4.org
require "engine.class"
local Base = require "engine.ui.Base"
local Focusable = require "engine.ui.Focusable"
--- A web browser
module(..., package.seeall, class.inherit(Base, Focusable))
function _M:init(t)
self.w = 50
self.h = 50
self.title = t.title
self.co = assert(t.co, "no downloader coroutine")
self.url = assert(t.url, "no downloader url")
self.dest = assert(t.dest, "no downloader dest")
if fs.exists(self.dest) then
self.dest = fs.getRealPath(self.dest)
else
local _, _, dir, name = self.dest:find("(.+)/([^/]+)$")
if dir then
self.dest = fs.getRealPath(dir)..fs.getPathSeparator()..name
end
end
self.allow_downloads = t.allow_downloads or {}
self.allow_login = t.allow_login
self.custom_calls = t.custom_calls or {}
if self.allow_login == nil then self.allow_login = true end
if self.allow_login and self.url:find("^http://te4%.org/") and profile.auth then
local param = "_te4ah="..profile.auth.hash.."&_te4ad="..profile.auth.drupid
local first = self.url:find("?", 1, 1)
if first then self.url = self.url.."&"..param
else self.url = self.url.."?"..param end
end
if self.url:find("^http://te4%.org/") then
local param = "_te4"
local first = self.url:find("?", 1, 1)
if first then self.url = self.url.."&"..param
else self.url = self.url.."?"..param end
end
print("Creating Downloader with url", self.url)
Base.init(self, t)
end
function _M:generate()
self.mouse:reset()
self.key:reset()
local handlers = {
on_crash = function()
print("WebView crashed, closing C view")
self.view = nil
end,
}
if self.allow_downloads then self:onDownload(handlers) end
self.view = core.webview.new(self.w, self.h, handlers)
if not self.view:usable() then
self.unusable = true
return
end
self.custom_calls._nextDownloadName = function(name)
if name then self._next_download_name = {name=name, time=os.time()}
else self._next_download_name = nil
end
end
for name, fct in pairs(self.custom_calls) do
handlers[name] = fct
self.view:setMethod(name)
end
self.view:loadURL(self.url)
self.loading = 0
self.loading_rotation = 0
self.scroll_inertia = 0
end
function _M:makeDownloadbox(downid, file)
local Dialog = require "engine.ui.Dialog"
local Waitbar = require "engine.ui.Waitbar"
local Button = require "engine.ui.Button"
local d = Dialog.new(self.title or "Download: "..file, 600, 100)
local b = Button.new{text="Cancel", fct=function() self.view:downloadAction(downid, false) game:unregisterDialog(d) end}
local w = Waitbar.new{size=600, text=file}
d:loadUI{
{left=0, top=0, ui=w},
{right=0, bottom=0, ui=b},
}
d:setupUI(true, true)
function d:updateFill(...) w:updateFill(...) end
return d
end
function _M:finish()
if not self.never_clean then
self.downloader = nil
self.view = nil
end
end
function _M:onDownload(handlers)
local Dialog = require "engine.ui.Dialog"
handlers.on_download_request = function(downid, url, file, mime)
if mime == "application/t-engine-addon" and self.allow_downloads.addons and url:find("^http://te4%.org/") then
local name = file
if self._next_download_name and os.time() - self._next_download_name.time <= 3 then name = self._next_download_name.name self._next_download_name = nil end
print("Accepting addon download to:", self.dest)
self.download_dialog = self:makeDownloadbox(downid, file)
self.download_dialog.install_kind = "Addon"
game:registerDialog(self.download_dialog)
self.view:downloadAction(downid, self.dest)
return
elseif mime == "application/t-engine-module" and self.allow_downloads.modules and url:find("^http://te4%.org/") then
local name = file
if self._next_download_name and os.time() - self._next_download_name.time <= 3 then name = self._next_download_name.name self._next_download_name = nil end
print("Accepting module download to:", self.dest)
self.download_dialog = self:makeDownloadbox(downid, file)
self.download_dialog.install_kind = "Game Module"
game:registerDialog(self.download_dialog)
self.view:downloadAction(downid, self.dest)
return
end
self.view:downloadAction(downid, false)
end
handlers.on_download_update = function(downid, cur_size, total_size, percent, speed)
if not self.download_dialog then return end
self.download_dialog:updateFill(cur_size, total_size, ("%d%% - %d KB/s"):format(cur_size * 100 / total_size, speed / 1024))
end
handlers.on_download_finish = function(downid)
if not self.download_dialog then return end
game:unregisterDialog(self.download_dialog)
local kind = self.download_dialog.install_kind
self.download_dialog = nil
self:finish()
coroutine.resume(self.co, true, kind)
end
end
function _M:start()
return coroutine.yield()
end
\ No newline at end of file
......@@ -70,6 +70,14 @@ function table.max(t)
return m
end
function table.print_shallow(src, offset, ret)
if type(src) ~= "table" then print("table.print has no table:", src) return end
offset = offset or ""
for k, e in pairs(src) do
print(("%s[%s] = %s"):format(offset, tostring(k), tostring(e)))
end
end
function table.print(src, offset, ret)
if type(src) ~= "table" then print("table.print has no table:", src) return end
offset = offset or ""
......
......@@ -28,6 +28,7 @@ local Module = require "engine.Module"
local Dialog = require "engine.ui.Dialog"
local Tooltip = require "engine.Tooltip"
local MainMenu = require "mod.dialogs.MainMenu"
local Downloader = require "engine.dialogs.Downloader"
local Shader = require "engine.Shader"
local Zone = require "engine.Zone"
......@@ -209,13 +210,15 @@ A usual problem is shaders and thus should be your first target to disable.]], 7
end
function _M:grabAddons()
if config.settings.no_auto_update_addons then return end
if core.steam then
self.updating_addons = {}
self.logdisplay("#{italic}##ROYAL_BLUe#Retrieving addons to update/download from Steam...#{normal}#")
self.logdisplay("#{italic}##ROYAL_BLUE#Retrieving addons to update/download from Steam...#{normal}#")
core.steam.grabSubscribedAddons(function(mode, teaa, title)
if mode == "end" then
self.updating_addons = nil
self.logdisplay("#{italic}##ROYAL_BLUe#Addons update finished.#{normal}#")
self.logdisplay("#{italic}##ROYAL_BLUE#Addons update finished.#{normal}#")
return
end
......@@ -228,6 +231,57 @@ function _M:grabAddons()
self.logdisplay("#{italic}#Download of #LIGHT_RED#%s#LAST# failed.#{normal}#", self.updating_addons[teaa] or "???")
end
end)
else
if not core.webview then return end
self.logdisplay("#{italic}##ROYAL_BLUE#Retrieving addons to update/download from te4.org...#{normal}#")
local mlist = Module:listModules(true)
list = {}
for i = 1, #mlist do
for j, mod in ipairs(mlist[i].versions) do
if j > 1 then break end
if not mod.is_boot then
local adds = Module:listAddons(mod, true)
for k, add in ipairs(adds) do
if add.addon_version and add.teaa then
local a = {
long_name = add.long_name,
name = add.for_module..'-'..add.short_name,
version = add.addon_version,
id_dlc = add.id_dlc and add.id_dlc[1],
file = add.teaa,
}
table.insert(list, a)
list[a.name] = a
end
end
end
end
end
table.print(list)
local update_list = profile:checkAddonUpdates(list)
if update_list then
local co co = coroutine.create(function()
for i, add in ipairs(update_list) do
if core.webview then
local d = Downloader.new{title="Updating addon: #LIGHT_GREEN#"..list[add.name].long_name, co=co, dest=add.file..".tmp", url=add.download_url, allow_downloads={addons=true}}
local ok = d:start()
if ok then
local wdir = fs.getWritePath()
local _, _, dir, name = add.file:find("(.+)/([^/]+)$")
if dir then
fs.setWritePath(fs.getRealPath(dir))
fs.delete(name)
fs.rename(name..".tmp", name)
fs.setWritePath(wdir)
self.logdisplay("#{italic}#Download of #LIGHT_GREEN#%s#LAST# finished.#{normal}#", list[add.name].long_name)
end
end
end
end
self.logdisplay("#{italic}##ROYAL_BLUE#Addons update finished.#{normal}#")
end)
coroutine.resume(co)
end
end
end
......
......@@ -50,6 +50,7 @@ function _M:init()
end
self.c_compat = Checkbox.new{default=false, width=math.floor(self.iw / 3 - 40), title="Show incompatible", on_change=function() self:switch() end}
self.c_auto_update = Checkbox.new{default=not config.settings.no_auto_update_addons, width=math.floor(self.iw / 3 - 40), title="Auto-update on start", on_change=function() self:switchAuto() end}
self:generateList()
......@@ -82,6 +83,7 @@ function _M:init()
{left=0, top=url1.h, ui=self.c_list},
{left=self.c_list.w+sep.w, top=url1.h, ui=self.c_adds},
{left=0, bottom=0, ui=self.c_compat},
{left=self.c_list.w-self.c_auto_update.w, bottom=0, ui=self.c_auto_update},
{left=self.c_list.w + 5, top=5+url1.h, ui=sep},
}
self:setFocus(self.c_list)
......@@ -150,6 +152,11 @@ function _M:switch()
self.c_list:setList(self.list)
end
function _M:switchAuto()
config.settings.no_auto_update_addons = not self.c_auto_update.checked
game:saveSettings("no_auto_update_addons", ("no_auto_update_addons = %s"):format(config.settings.no_auto_update_addons and "true" or "false"))
end
function _M:unload()
game:grabAddons()
end
......@@ -405,6 +405,30 @@ function _M:orderCheckBatchHash(o)
end
end
function _M:orderCheckAddonUpdates(o)
if not self.sock then cprofile.pushEvent("e='CheckAddonUpdates' ok=false not_connected=true") end
local data = zlib.compress(table.serialize(o.list))
self:command("ADDN SHOULD_UPDATE", #data)
if self:read("200") then
self.sock:send(data)
if self:read("200") then
local _, _, size = self.last_line:find("^([0-9]+)")
size = tonumber(size)
local list = {}
if size and size > 1 then
local body = self:receive(size)
if body then body = zlib.decompress(body) end
if body then body = body:unserialize() end
if body then list = body end
end
cprofile.pushEvent(("e='CheckAddonUpdates' ok=%q"):format(table.serialize(list)))
return
end
end
cprofile.pushEvent("e='CheckAddonUpdates' ok=false")
end
function _M:orderRegisterNewCharacter(o)
self:command("CHAR", "NEW", o.module)
if self:read("200") then
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment