Skip to content
Snippets Groups Projects
Commit ca5bcb19 authored by dg's avatar dg
Browse files

savefiles are md5'ed online

git-svn-id: http://svn.net-core.org/repos/t-engine4@5258 51575b47-30f0-44d4-a5cc-537603b46e54
parent b7af82a3
No related branches found
No related tags found
No related merge requests found
......@@ -100,7 +100,7 @@ function _M:defaultSavedFields(t)
always_target=true, gfxmode=true, uniques=true, object_known_types=true,
memory_levels=true, achievement_data=true, factions=true, playing_musics=true,
state=true,
__savefile_version_tokens = true,
__savefile_version_tokens = true, bad_md5_loaded = true,
}
table.merge(def, t)
return def
......
......@@ -663,6 +663,35 @@ function _M:registerSaveChardump(module, uuid, title, tags, data)
print("[ONLINE PROFILE] saved character ", uuid)
end
function _M:setSaveID(module, uuid, savename, md5)
if not self.auth or not self.hash_valid or not md5 then return end
core.profile.pushOrder(table.serialize{o="SaveMD5",
module=module,
uuid=uuid,
savename=savename,
md5=md5,
})
print("[ONLINE PROFILE] saved character md5", uuid, savename, md5)
end
function _M:checkSaveID(module, uuid, savename, md5)
if not self.auth or not self.hash_valid or not md5 then return function() return false end end
core.profile.pushOrder(table.serialize{o="CheckSaveMD5",
module=module,
uuid=uuid,
savename=savename,
md5=md5,
})
print("[ONLINE PROFILE] checking character md5", uuid, savename, md5)
return function()
local ok = false
self:waitEvent("CheckSaveMD5", function(e)
if e.savename == savename and e.ok then ok = true end
end, 30000)
return ok
end
end
function _M:currentCharacter(module, title, uuid)
if not self.auth then return end
core.profile.pushOrder(table.serialize{o="CurrentCharacter",
......
......@@ -32,6 +32,7 @@ module(..., package.seeall, class.make)
_M.current_save = false
_M.hotkeys_file = "/save/quick_hotkeys"
_M.md5_types = {}
--- Init a savefile
-- @param savefile the name of the savefile, usually the player's name. It will be sanitized so dont bother doing it
......@@ -57,6 +58,10 @@ function _M:setCurrent(save)
_M.current_save = save
end
function _M:setSaveMD5Type(type)
self.md5_types[type] = true
end
--- Finishes up a savefile
-- Always call it once done
function _M:close()
......@@ -142,6 +147,8 @@ function _M:saveWorld(world, no_dialog)
fs.delete(self.save_dir..self:nameSaveWorld(world))
fs.rename(self.save_dir..self:nameSaveWorld(world)..".tmp", self.save_dir..self:nameSaveWorld(world))
self:md5Upload("world", self:nameSaveWorld(world))
if not no_dialog then popup:done() end
end
......@@ -211,6 +218,8 @@ function _M:saveGame(game, no_dialog)
fs.delete(self.save_dir..self:nameSaveGame(game))
fs.rename(self.save_dir..self:nameSaveGame(game)..".tmp", self.save_dir..self:nameSaveGame(game))
self:md5Upload("game", self:nameSaveGame(game))
local desc = game:getSaveDescription()
local f = fs.open(self.save_dir.."desc.lua", "w")
f:write(("module = %q\n"):format(game.__mod_info.short_name))
......@@ -279,6 +288,8 @@ function _M:saveZone(zone, no_dialog)
fs.delete(self.save_dir..self:nameSaveZone(zone))
fs.rename(self.save_dir..self:nameSaveZone(zone)..".tmp", self.save_dir..self:nameSaveZone(zone))
self:md5Upload("zone", self:nameSaveZone(zone))
if not no_dialog then popup:done() end
end
......@@ -308,6 +319,8 @@ function _M:saveLevel(level, no_dialog)
fs.delete(self.save_dir..self:nameSaveLevel(level))
fs.rename(self.save_dir..self:nameSaveLevel(level)..".tmp", self.save_dir..self:nameSaveLevel(level))
self:md5Upload("level", self:nameSaveLevel(level))
if not no_dialog then popup:done() end
end
......@@ -337,6 +350,8 @@ function _M:saveEntity(e, no_dialog)
fs.delete(self.save_dir..self:nameSaveEntity(e))
fs.rename(self.save_dir..self:nameSaveEntity(e)..".tmp", self.save_dir..self:nameSaveEntity(e))
self:md5Upload("entity", self:nameSaveEntity(e))
if not no_dialog then popup:done() end
end
......@@ -380,6 +395,8 @@ function _M:loadWorld()
local path = fs.getRealPath(self.save_dir..self:nameLoadWorld())
if not path or path == "" then return nil, "no savefile" end
local checker = self:md5Check("world", self:nameLoadWorld())
fs.mount(path, self.load_dir)
local popup = Dialog:simpleWaiter("Loading world", "Please wait while loading the world...")
......@@ -395,6 +412,9 @@ function _M:loadWorld()
fs.umount(path)
-- We check for the server return, while we loaded the save it probably responded so we do not wait at all
if not checker() then self:badMD5Load() end
popup:done()
return loadedWorld
......@@ -430,6 +450,8 @@ function _M:loadGame()
local loadedGame = self:loadReal("main")
local checker = self:md5Check("game", self:nameLoadGame(), loadedGame)
-- Delay loaded must run
local delay_fct = function()
for i, o in ipairs(self.delayLoad) do
......@@ -440,6 +462,9 @@ function _M:loadGame()
fs.umount(path)
-- We check for the server return, while we loaded the save it probably responded so we do not wait at all
if not checker() then self:badMD5Load() end
popup:done()
return loadedGame, delay_fct
......@@ -469,6 +494,8 @@ function _M:loadZone(zone)
local path = fs.getRealPath(self.save_dir..self:nameLoadZone(zone))
if not path or path == "" then return false end
local checker = self:md5Check("zone", self:nameLoadZone(zone))
fs.mount(path, self.load_dir)
local f = fs.open(self.load_dir.."nb", "r")
......@@ -490,6 +517,9 @@ function _M:loadZone(zone)
o:loaded()
end
-- We check for the server return, while we loaded the save it probably responded so we do not wait at all
if not checker() then self:badMD5Load() end
core.wait.enableManualTick(false)
popup:done()
......@@ -502,6 +532,8 @@ function _M:loadLevel(zone, level)
local path = fs.getRealPath(self.save_dir..self:nameLoadLevel(zone, level))
if not path or path == "" then return false end
local checker = self:md5Check("level", self:nameLoadLevel(zone, level))
fs.mount(path, self.load_dir)
local f = fs.open(self.load_dir.."nb", "r")
......@@ -522,6 +554,9 @@ function _M:loadLevel(zone, level)
o:loaded()
end
-- We check for the server return, while we loaded the save it probably responded so we do not wait at all
if not checker() then self:badMD5Load() end
popup:done()
fs.umount(path)
......@@ -533,6 +568,8 @@ function _M:loadEntity(name)
local path = fs.getRealPath(self.save_dir..self:nameLoadEntity(name))
if not path or path == "" then return false end
local checker = self:md5Check("entity", self:nameLoadEntity(name))
fs.mount(path, self.load_dir)
local f = fs.open(self.load_dir.."nb", "r")
......@@ -553,6 +590,9 @@ function _M:loadEntity(name)
o:loaded()
end
-- We check for the server return, while we loaded the save it probably responded so we do not wait at all
if not checker() then self:badMD5Load() end
popup:done()
fs.umount(path)
......@@ -563,3 +603,52 @@ end
function _M:check()
return fs.exists(self.save_dir..self:nameLoadGame())
end
function _M:md5Upload(type, f)
if not self.md5_types[type] then return end
local p = game:getPlayer(true)
if not p.getUUID then return end
local uuid = p:getUUID()
if not uuid then return end
local md5 = require "md5"
local m = nil
local fff = fs.open(self.save_dir..f, "r")
if fff then
local data = fff:read(10485760)
if data and data ~= "" then
m = md5.sumhexa(data)
end
fff:close()
end
print("Save MD5", uuid, m, type, f)
profile:setSaveID(game.__mod_info.short_name, uuid, f, m)
end
function _M:md5Check(type, f, loadgame)
if not self.md5_types[type] then return function() return true end end
local bad = function() return false end
local p = (loadgame or game):getPlayer(true)
if not p.getUUID then return bad end
local uuid = p:getUUID()
if not uuid then return bad end
local md5 = require "md5"
local m = nil
local fff = fs.open(self.save_dir..f, "r")
if fff then
local data = fff:read(10485760)
if data and data ~= "" then
m = md5.sumhexa(data)
end
fff:close()
end
print("Check MD5", uuid, m, type, f)
return profile:checkSaveID(game.__mod_info.short_name, uuid, f, m)
end
function _M:badMD5Load()
if game.onBadMD5Load then game:onBadMD5Load() end
game.bad_md5_loaded = true
end
......@@ -28,6 +28,7 @@ allow_late_uuid = false
--- Register the character on te4.org and return a UUID for it
function _M:getUUID()
if self.__te4_uuid then return self.__te4_uuid end
local uuid = profile:registerNewCharacter(game.__mod_info.short_name)
if uuid then
self.__te4_uuid = uuid
......
......@@ -25,6 +25,7 @@ local Entity = require "engine.Entity"
Entity.ascii_outline = {x=2, y=2, r=0, g=0, b=0, a=0.8}
-- This file loads the game module, and loads data
local Savefile = require "engine.Savefile"
local KeyBind = require "engine.KeyBind"
local DamageType = require "engine.DamageType"
local Faction = require "engine.Faction"
......@@ -48,6 +49,10 @@ local PlayerHotkeys = require "engine.interface.PlayerHotkeys"
local Quest = require "engine.Quest"
local UIBase = require "engine.ui.Base"
Savefile:setSaveMD5Type("game")
Savefile:setSaveMD5Type("level")
Savefile:setSaveMD5Type("zone")
-- Init settings
config.settings.tome = config.settings.tome or {}
profile.mod.allow_build = profile.mod.allow_build or {}
......
......@@ -365,6 +365,19 @@ function _M:orderGetCharball(o)
end
end
function _M:orderSaveMD5(o)
self:command("CHAR", "SETSAVEID", o.md5, o.uuid, o.module, o.savename)
end
function _M:orderCheckSaveMD5(o)
self:command("CHAR", "CHECKSAVEID", o.md5, o.uuid, o.module, o.savename)
if self:read("200") then
cprofile.pushEvent(string.format("e='CheckSaveMD5' ok=true savename=%q", o.savename))
else
cprofile.pushEvent(string.format("e='CheckSaveMD5' ok=false savename=%q", o.savename))
end
end
function _M:orderCurrentCharacter(o)
self:command("CHAR", "CUR", table.serialize(o))
self.cur_char = o
......
......@@ -12,4 +12,3 @@
- gust of wind trap (for X turn a strong wind blows in a random direction, force-moving things
* slaves in the ring of blood zone should be freed once slaver is dead = achievement for many
* sub-tile entities, like the spiders for the arachnomancer: split a tile in 16 (4x4)
* zone/level save => trigger a game save
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