Forked from
tome / Tales of MajEyal
13640 commits behind the upstream repository.
-
dg authored
git-svn-id: http://svn.net-core.org/repos/t-engine4@1464 51575b47-30f0-44d4-a5cc-537603b46e54
dg authoredgit-svn-id: http://svn.net-core.org/repos/t-engine4@1464 51575b47-30f0-44d4-a5cc-537603b46e54
PlayerProfile.lua 10.62 KiB
-- TE4 - T-Engine 4
-- Copyright (C) 2009, 2010 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 http = require "socket.http"
local url = require "socket.url"
local ltn12 = require "ltn12"
local lanes = require "lanes"
require "Json2"
------------------------------------------------------------
-- some simple serialization stuff
------------------------------------------------------------
local function basicSerialize(o)
if type(o) == "number" or type(o) == "boolean" then
return tostring(o)
elseif type(o) == "function" then
return string.format("loadstring(%q)", string.dump(o))
else -- assume it is a string
return string.format("%q", o)
end
end
local function serialize_data(outf, name, value, saved, filter, allow, savefile, force)
saved = saved or {} -- initial value
outf(name, " = ")
if type(value) == "number" or type(value) == "string" or type(value) == "boolean" or type(value) == "function" then
outf(basicSerialize(value), "\n")
elseif type(value) == "table" then
saved[value] = name -- save name for next time
outf("{}\n") -- create a new table
for k,v in pairs(value) do -- save its fields
local fieldname
fieldname = string.format("%s[%s]", name, basicSerialize(k))
serialize_data(outf, fieldname, v, saved, {new=true}, false, savefile, false)
end
else
error("cannot save a " .. type(value) .. " ("..name..")")
end
end
local function serialize(data)
local tbl = {}
local outf = function(...) for i,str in ipairs{...} do table.insert(tbl, str) end end
for k, e in pairs(data) do
serialize_data(outf, tostring(k), e)
end
return table.concat(tbl)
end
------------------------------------------------------------
--- Handles the player profile, possibly online
module(..., package.seeall, class.make)
function _M:init(name)
self.generic = {}
self.modules = {}
self.name = name or "default"
self:loadGenericProfile()
self.auth = false
if self.generic.online and self.generic.online.login and self.generic.online.pass then
self.login = self.generic.online.login
self.pass = self.generic.online.pass
self:tryAuth()
self:getConfigs("generic")
self:syncOnline("generic")
end
end
function _M:loadData(f, where)
setfenv(f, where)
local ok, err = pcall(f)
if not ok and err then error(err) end
end
--- Loads profile generic profile from disk
function _M:loadGenericProfile()
local d = "/profiles/"..self.name.."/generic/"
fs.mount(engine.homepath, "/")
for i, file in ipairs(fs.list(d)) do
if file:find(".profile$") then
local f, err = loadfile(d..file)
if not f and err then error(err) end
local field = file:gsub(".profile$", "")
self.generic[field] = self.generic[field] or {}
self:loadData(f, self.generic[field])
if not self.generic[field].__uuid then self.generic[field].__uuid = util.uuid() end
end
end
fs.umount(engine.homepath)
end
--- Loads profile module profile from disk
function _M:loadModuleProfile(short_name)
local d = "/profiles/"..self.name.."/modules/"..short_name.."/"
fs.mount(engine.homepath, "/")
print("[Module Profile] ", engine.homepath.."/"..d)
self.modules[short_name] = self.modules[short_name] or {}
for i, file in ipairs(fs.list(d)) do
if file:find(".profile$") then
local f, err = loadfile(d..file)
if not f and err then error(err) end
local field = file:gsub(".profile$", "")
self.modules[short_name][field] = self.modules[short_name][field] or {}
self:loadData(f, self.modules[short_name][field])
if not self.modules[short_name][field].__uuid then self.modules[short_name][field].__uuid = util.uuid() end
end
end
fs.umount(engine.homepath)
self:getConfigs(short_name)
self:syncOnline(short_name)
self.mod = self.modules[short_name]
self.mod_name = short_name
end
--- Saves a profile data
function _M:saveGenericProfile(name, data, nosync)
data = serialize(data)
-- Check for readability
local f, err = loadstring(data)
setfenv(f, {})
if not f then print("[PROFILES] cannot save generic data ", name, data, "it does not parse:") error(err) end
local ok, err = pcall(f)
if not ok and err then print("[PROFILES] cannot save generic data", name, data, "it does not parse") error(err) end
local restore = fs.getWritePath()
fs.setWritePath(engine.homepath)
fs.mkdir("/profiles/"..self.name.."/generic/")
local f = fs.open("/profiles/"..self.name.."/generic/"..name..".profile", "w")
f:write(data)
f:close()
if restore then fs.setWritePath(restore) end
if not nosync then self:setConfigs("generic", name, data) end
end
--- Saves a module profile data
function _M:saveModuleProfile(name, data, module, nosync)
data = serialize(data)
module = module or self.mod_name
-- Check for readability
local f, err = loadstring(data)
setfenv(f, {})
if not f then print("[PROFILES] cannot save module data ", name, data, "it does not parse:") error(err) end
local ok, err = pcall(f)
if not ok and err then print("[PROFILES] cannot save module data", name, data, "it does not parse") error(err) end
local restore = fs.getWritePath()
fs.setWritePath(engine.homepath)
fs.mkdir("/profiles/"..self.name.."/modules/")
fs.mkdir("/profiles/"..self.name.."/modules/"..module.."/")
local f = fs.open("/profiles/"..self.name.."/modules/"..module.."/"..name..".profile", "w")
f:write(data)
f:close()
if restore then fs.setWritePath(restore) end
if not nosync then self:setConfigs(module, name, data) end
end
-----------------------------------------------------------------------
-- Online stuff
-----------------------------------------------------------------------
function _M:rpc(data)
-- We can work in asynchronous mode, to not delay the main game execution
if data.async and game and type(game) == "table" and not game.refuse_threads then
data.async = nil
local l = lanes.linda()
function handler(data)
local http = require "socket.http"
local url = require "socket.url"
require "Json2"
print("[ONLINE PROFILE] async rpc called", "http://te4.org/lua/profilesrpc.ws/"..data.action)
local body, status = http.request("http://te4.org/lua/profilesrpc.ws/"..data.action, "json="..url.escape(json.encode(data)))
if not body then l:send("final", nil)
else l:send("final", json.decode(body))
end
return true
end
local th = lanes.gen("*", handler)(data)
-- Tell the game to monitor this thread and end it when it's done
game:registerThread(th, l)
else
print("[ONLINE PROFILE] rpc called", "http://te4.org/lua/profilesrpc.ws/"..data.action)
local body, status = http.request("http://te4.org/lua/profilesrpc.ws/"..data.action, "json="..url.escape(json.encode(data)))
if not body then return end
return json.decode(body)
end
end
function _M:getNews()
print("[ONLINE PROFILE] get news")
local data = self:rpc{action="GetNews"}
if not data then print("[ONLINE PROFILE] no news") return end
print("[ONLINE PROFILE] news ", data.title)
return data
end
function _M:tryAuth()
print("[ONLINE PROFILE] auth")
local data = self:rpc{action="TryAuth", login=self.login, pass=self.pass}
if not data then print("[ONLINE PROFILE] not auth") return end
print("[ONLINE PROFILE] auth as ", data.name, data.hash)
self.auth = data
end
function _M:getConfigs(module)
if not self.auth then return end
local data = self:rpc{action="GetConfigs", login=self.login, hash=self.auth.hash, module=module}
if not data then print("[ONLINE PROFILE] get configs") return end
for name, val in pairs(data) do
print("[ONLINE PROFILE] config ", name)
local field = name
local f, err = loadstring(val)
if not f and err then error(err) end
if module == "generic" then
self.generic[field] = self.generic[field] or {}
self:loadData(f, self.generic[field])
self:saveGenericProfile(field, self.generic[field], true)
else
self.modules[module] = self.modules[module] or {}
self.modules[module][field] = self.modules[module][field] or {}
self:loadData(f, self.modules[module][field])
self:saveModuleProfile(field, self.modules[module][field], module, true)
end
end
end
function _M:setConfigs(module, name, val)
if not self.auth then return end
if name == "online" then return end
if type(val) ~= "string" then val = serialize(val) end
local data = self:rpc{async=true, action="SetConfigs", login=self.login, hash=self.auth.hash, module=module, data={[name] = val}}
if not data then return end
print("[ONLINE PROFILE] saved ", module, name, val)
end
function _M:syncOnline(module)
if not self.auth then return end
local sync = self.generic
if module ~= "generic" then sync = self.modules[module] end
if not sync then return end
local data = {}
for k, v in pairs(sync) do if k ~= "online" then data[k] = serialize(v) end end
local data = self:rpc{async=true, action="SetConfigs", login=self.login, hash=self.auth.hash, module=module, data=data}
if not data then return end
print("[ONLINE PROFILE] saved ", module)
end
function _M:newProfile(Login, Name, Password, Email)
print("[ONLINE PROFILE] profile options ", Login, Email, Name, Password)
local data = self:rpc{action="NewProfile", login=Login, email=Email, name=Name, pass=Password}
if not data then print("[ONLINE PROFILE] could not create") return end
print("[ONLINE PROFILE] profile id ", data.id)
self:performlogin(Login, Password, Name, Email)
end
function _M:performlogin(login, pass, name, email)
self.login=login
self.pass=pass
print("[ONLINE PROFILE] attempting log in ", self.login, self.pass)
self:tryAuth()
if (profile.auth) then
self.generic.online = { login=login, pass=pass,name=name or "", email=email or "" }
self:saveGenericProfile("online", self.generic.online)
self:getConfigs("generic")
self:syncOnline("generic")
end
end
function _M:logOut()
profile.generic.online = nil
profile.auth = nil
local restore = fs.getWritePath()
fs.setWritePath(engine.homepath)
fs.delete("/profiles/"..self.name.."/generic/online.profile")
if restore then fs.setWritePath(restore) end
end
function _M:checkFirstRun()
local result = self.generic.firstrun
if not result then
firstrun = { When=os.time() }
self:saveGenericProfile("firstrun", firstrun, false)
end
return result
end