Forked from
tome / Tales of MajEyal
8725 commits behind the upstream repository.
-
dg authored
git-svn-id: http://svn.net-core.org/repos/t-engine4@5683 51575b47-30f0-44d4-a5cc-537603b46e54
dg authoredgit-svn-id: http://svn.net-core.org/repos/t-engine4@5683 51575b47-30f0-44d4-a5cc-537603b46e54
SavefilePipe.lua 6.60 KiB
-- TE4 - T-Engine 4
-- Copyright (C) 2009, 2010, 2011, 2012 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 Dialog = require "engine.ui.Dialog"
--- Savefile code
-- Creates a savefile pipe, savefiles requests can be pushed into it, it will execute them
-- in order and, as much as possible, as a background process, thus not preventing the game from running.<br/>
-- There can only be one pipe! DO NOT TRY TO MAKE MORE
module(..., package.seeall, class.make)
_M.current = nil
--- Init a savefile pipe
-- @param class the name of the Savefile class to use. Defaults to engine.Savefile
-- @param max_before_wait Number of saves allowed in the pipe before the game pauses to do the saves (this is to prevent pushing too many saves and making it very very long)
function _M:init(class, max_before_wait)
assert(not _M.current, "Tried to create more than one savefile pipe!")
_M.current = self
self.saveclass = class or "engine.Savefile"
self.pipe = {}
self.on_done = {}
self.max_before_wait = max_before_wait or 6
self.co = nil
self.current_nb = 0
self.total_nb = 1
end
--- Push a savefile request
-- @param savename the name of the savefile to handle
-- @param type the Savefile method to use. I.e: "game", "level", "zone". This will cann the Savefile:saveGame, Savefile:saveLevel, Savefile:saveZone methods
-- @param object the object to save
-- @param class a class name, if different from the default one
-- @param on_end a function to call when this object is saved; this can be nil
function _M:push(savename, type, object, class, on_end)
if game.onSavefilePush then game:onSavefilePush(savename, type, object, class) end
local screenshot = nil
if type == "game" then
screenshot = game:takeScreenshot(true)
end
class = class or self.saveclass
local Savefile = require(class)
local id = Savefile["nameSave"..type:lower():capitalize()](Savefile, object)
if #self.pipe == 0 then savefile_pipe.current_nb = 0 end
local clone, nb = object:cloneForSave()
self.pipe[#self.pipe+1] = {id=id, savename = savename, type=type, object=clone, nb_objects=nb, baseobject=object, class=class, saveversion=game:saveVersion("new"), screenshot=screenshot, on_end=on_end}
local total_nb = 0
for i, p in ipairs(self.pipe) do total_nb = total_nb + p.nb_objects end
self.total_nb = total_nb
if not self.co or coroutine.status(self.co) == "dead" then
self.co = coroutine.create(function() return self:doThread() end)
game:registerCoroutine("savefilepipe", self.co)
end
if game.onSavefilePushed then game:onSavefilePushed(savename, type, object, class) end
-- Refuse to continue, make the user wait
if #self.pipe >= self.max_before_wait or not config.settings.background_saves then
self:forceWait()
end
return clone
end
--- Push a generic action to be done once saves complete
function _M:pushGeneric(name, fct)
self.on_done[#self.on_done+1] = {name=name, fct=fct}
end
--- Actually do the saves
-- This should run in a coroutine.<br/>
-- Do not call this, this is automatic!
function _M:doThread()
self.saving = true
collectgarbage("collect")
-- collectgarbage("stop")
if game:getPlayer() then game:getPlayer().changed = true end
while #self.pipe > 0 do
local p = self.pipe[1]
local Savefile = require(p.class)
local o = p.object
print("[SAVEFILE PIPE] new save running in the pipe:", p.savename, p.type, "::", p.id, "::", p.baseobject, "=>", p.object, "("..p.nb_objects..")")
local save = Savefile.new(p.savename, config.settings.background_saves)
o.__saved_saveversion = p.saveversion
save["save"..p.type:lower():capitalize()](save, o, true)
if p.screenshot then save:saveScreenshot(p.screenshot) end
save:close()
if p.on_end then
p.on_end(save)
end
table.remove(self.pipe, 1)
end
if game.log then game.log("Saving done.") end
-- collectgarbage("restart")
self.saving = false
if game:getPlayer() then game:getPlayer().changed = true end
-- Run the generic stuff
while #self.on_done > 0 do
local p = self.on_done[1]
print("[SAVEFILE PIPE] on end", p.name)
p.fct()
table.remove(self.on_done, 1)
end
end
--- Force to wait for the saves
function _M:forceWait()
if #self.pipe == 0 then return end
local popup = Dialog:simpleWaiter("Saving...", "Please wait while saving...", nil, 1000, self.total_nb)
core.display.forceRedraw()
local cnt = 0
while coroutine.status(self.co) ~= "dead" do
cnt = cnt + 1
if cnt == 1000 then core.display.forceRedraw() if game:getPlayer() then game:getPlayer().changed = true end cnt = 0 end
coroutine.resume(self.co)
end
popup:done()
end
--- Allow to ignore saveversion token
function _M:ignoreSaveToken(v)
self.ignore_save_token = v
end
--- Load a savefile
-- @param savename the name of the savefile to handle
-- @param type the Savefile method to use. I.e: "game", "level", "zone". This will cann the Savefile:saveGame, Savefile:saveLevel, Savefile:saveZone methods
-- @param class a class name, if different from the default one
function _M:doLoad(savename, type, class, ...)
class = class or self.saveclass
local Savefile = require(class)
local id = Savefile["nameLoad"..type:lower():capitalize()](Savefile, ...)
-- Look for it in the pipe
for i = 1, #self.pipe do
local p = self.pipe[i]
print("[SAVEFILE PIPE] looking for savefile to recover in the memory pipe", p.id, "?=?", id)
if p.id == id then
-- It is still in the save pipe: return it
print("[SAVEFILE PIPE] recovering save from memory in pipe:", savename, type, "::", id)
return p.baseobject
end
end
local cur = Savefile:getCurrent()
local save = Savefile.new(savename)
local ret = save["load"..type:lower():capitalize()](save, ...)
save:close()
Savefile:setCurrent(cur)
-- Check for validity
if _G.type(ret) == "table" and type ~= "game" and type ~= "world" and not self.ignore_save_token then
if not game:saveVersion(ret.__saved_saveversion) then
print("Loading savefile", savename, type, class," with id", ret.__saved_saveversion, "but current game does not know this token => ignoring")
return nil
end
end
return ret
end