diff --git a/game/engines/default/engine/Module.lua b/game/engines/default/engine/Module.lua index a2fed66a42b5adc55f03c7987663159f8653eb2e..c338eed1013c8b3d728d2387676f4e43eac393e1 100644 --- a/game/engines/default/engine/Module.lua +++ b/game/engines/default/engine/Module.lua @@ -246,11 +246,6 @@ function _M:instanciate(mod, name, new_game, no_reboot) mod.version_name = ("%s-%d.%d.%d"):format(mod.short_name, mod.version[1], mod.version[2], mod.version[3]) - profile.generic.modules_loaded = profile.generic.modules_loaded or {} - profile.generic.modules_loaded[mod.short_name] = (profile.generic.modules_loaded[mod.short_name] or 0) + 1 - - profile:saveGenericProfile("modules_loaded", profile.generic.modules_loaded) - -- Turn based by default core.game.setRealtime(0) @@ -290,7 +285,8 @@ function _M:instanciate(mod, name, new_game, no_reboot) end profile:addStatFields(unpack(mod.profile_stats_fields or {})) - profile:loadModuleProfile(mod.short_name) + profile:setConfigsBatch(true) + profile:loadModuleProfile(mod.short_name, mod) profile:currentCharacter(mod.version_string, "game did not tell us") -- Init the module code @@ -339,6 +335,9 @@ function _M:instanciate(mod, name, new_game, no_reboot) end end print("[MODULE LOADER] done loading module", mod.long_name) + + profile:saveGenericProfile("modules_loaded", {name=mod.short_name, nb={"inc", 1}}) + profile:setConfigsBatch(false) end --- Setup write dir for a module diff --git a/game/engines/default/engine/PlayerProfile.lua b/game/engines/default/engine/PlayerProfile.lua index 2606d4ae1782941a5859173112ed875dff92aac8..b844bddd82e5ff8909eb47b63a248eaf690bdb4a 100644 --- a/game/engines/default/engine/PlayerProfile.lua +++ b/game/engines/default/engine/PlayerProfile.lua @@ -83,7 +83,7 @@ function _M:init() { [checkstats] = { invalid = { read={online=true}, write="online" }, valid = { read={online=true}, write="online" } }, ["^allow_build$"] = { invalid = { read={offline=true,online=true}, write="offline" }, valid = { read={offline=true,online=true}, write="online" } }, - ["^achievement%..*$"] = { invalid = { read={offline=true,online=true}, write="offline" }, valid = { read={online=true}, write="online" } }, + ["^achievements$"] = { invalid = { read={offline=true,online=true}, write="offline" }, valid = { read={online=true}, write="online" } }, } self.auth = false @@ -131,6 +131,14 @@ function _M:umountProfile(online, pop) if pop then fs.setWritePath(pop) end end +-- Define the fields that are sync'ed online, and how they are sync'ed +local generic_profile_defs = { + firstrun = {nosync=true, {firstrun="number"}, receive=function(data, save) save.firstrun = data.firstrun end }, + online = {nosync=true, {login="string:40", pass="string:40"}, receive=function(data, save) save.login = data.login save.pass = data.pass end }, + modules_played = { {name="index:string:30"}, {time_played="number"}, receive=function(data, save) max_set(save, data.name, data, "time_played") end, export=function(env) for k, v in pairs(env) do add{name=k, time_played=v} end end }, + modules_loaded = { {name="index:string:30"}, {nb="number"}, receive=function(data, save) max_set(save, data.name, data, "nb") end, export=function(env) for k, v in pairs(env) do add{name=k, nb=v} end end }, +} + --- Loads profile generic profile from disk -- Generic profile is always read from the "online" profile function _M:loadGenericProfile() @@ -207,7 +215,7 @@ function _M:filterSaveData(field) end --- Loads profile module profile from disk -function _M:loadModuleProfile(short_name) +function _M:loadModuleProfile(short_name, mod_def) if short_name == "boot" then return end -- Delay when we are currently saving @@ -238,84 +246,106 @@ function _M:loadModuleProfile(short_name) load(false) -- Load from offline profile load(true) -- Load from online profile - self:getConfigs(short_name) - self:syncOnline(short_name) - self.mod = self.modules[short_name] self.mod_name = short_name + + self:getConfigs(short_name, nil, mod_def) + self:syncOnline(short_name, mod_def) end --- Saves a profile data -function _M:saveGenericProfile(name, data, nosync) +function _M:saveGenericProfile(name, data, no_sync, nowrite) -- Delay when we are currently saving if savefile_pipe and savefile_pipe.saving then savefile_pipe:pushGeneric("saveGenericProfile", function() self:saveGenericProfile(name, data, nosync) end) return end - data = serialize(data) + if not generic_profile_defs[name] then print("[PROFILE] refusing unknown generic data", name) return end - -- Check for readability - local f, err = loadstring(data) - if not f then print("[PROFILE] cannot save generic data ", name, data, "it does not parse:") print(err) return end - setfenv(f, {}) - local ok, err = pcall(f) - if not ok and err then print("[PROFILE] cannot save generic data", name, data, "it does not parse") print(err) return end + profile.generic[name] = profile.generic[name] or {} + local dataenv = profile.generic[name] + local f = generic_profile_defs[name].receive + setfenv(f, { + inc_set=function(dataenv, key, data, dkey) + local v = data[dkey] + if type(v) == "number" then + elseif type(v) == "table" and v[1] == "inc" then v = (dataenv[key] or 0) + v[2] + end + dataenv[key] = v + data[dkey] = v + end, + max_set=function(dataenv, key, data, dkey) + local v = data[dkey] + if type(v) == "number" then + elseif type(v) == "table" and v[1] == "inc" then v = (dataenv[key] or 0) + v[2] + end + v = math.max(v, dataenv[key] or 0) + dataenv[key] = v + data[dkey] = v + end, + }) + f(data, dataenv) - local pop = self:mountProfile(true) - local f = fs.open("/generic/"..name..".profile", "w") - f:write(data) - f:close() - self:umountProfile(true, pop) + if not nowrite then + local pop = self:mountProfile(true) + local f = fs.open("/generic/"..name..".profile", "w") + f:write(serialize(dataenv)) + f:close() + self:umountProfile(true, pop) + end - if not nosync then self:setConfigs("generic", name, data) end + if not nosync and not generic_profile_defs[name].no_sync then self:setConfigs("generic", name, data) end end --- Saves a module profile data -function _M:saveModuleProfile(name, data, module, nosync) +function _M:saveModuleProfile(name, data, nosync, nowrite) if module == "boot" then return end + if not game or not game.__mod_info.profile_defs then return end -- Delay when we are currently saving - if savefile_pipe and savefile_pipe.saving then savefile_pipe:pushGeneric("saveModuleProfile", function() self:saveModuleProfile(name, data, module, nosync) end) return end + if savefile_pipe and savefile_pipe.saving then savefile_pipe:pushGeneric("saveModuleProfile", function() self:saveModuleProfile(name, data, nosync) end) return end - data = serialize(data) - module = module or self.mod_name + local module = self.mod_name -- Check for readability - local f, err = loadstring(data) - if not f then print("[PROFILE] cannot save module data ", name, data, "it does not parse:") print(err) return end - setfenv(f, {}) - local ok, err = pcall(f) - if not ok and err then print("[PROFILE] cannot save module data", name, data, "it does not parse") print(err) return end - - local online = self:filterSaveData(name) - local pop = self:mountProfile(online, module) ---[[ - local path = "current-profile/modules/"..module.."/"..name..".profile" - local f, msg = fs.open(path, "w") - print("[PROFILE] search path: ") - table.foreach(fs.getSearchPath(), print) - print("[PROFILE] path: ", path) - print("[PROFILE] real path: ", fs.getRealPath(path), "::", fs.exists(path) and "exists" or "does not exist") - print("[PROFILE] write path is: ", fs.getWritePath()) - if f then - f:write(data) + profile.mod[name] = profile.mod[name] or {} + local dataenv = profile.mod[name] + local f = game.__mod_info.profile_defs[name].receive + setfenv(f, { + inc_set=function(dataenv, key, data, dkey) + local v = data[dkey] + if type(v) == "number" then + elseif type(v) == "table" and v[1] == "inc" then v = (dataenv[key] or 0) + v[2] + end + dataenv[key] = v + data[dkey] = v + end, + max_set=function(dataenv, key, data, dkey) + local v = data[dkey] + if type(v) == "number" then + elseif type(v) == "table" and v[1] == "inc" then v = (dataenv[key] or 0) + v[2] + end + v = math.max(v, dataenv[key] or 0) + dataenv[key] = v + data[dkey] = v + end, + }) + f(data, dataenv) + + if not nowrite then + local online = self:filterSaveData(name) + local pop = self:mountProfile(online, module) + local f = fs.open("/modules/"..module.."/"..name..".profile", "w") + f:write(serialize(dataenv)) f:close() - else - print("[PROFILE] physfs error:", msg) + self:umountProfile(online, pop) end -]] - local f = fs.open("/modules/"..module.."/"..name..".profile", "w") - f:write(data) - f:close() - - self:umountProfile(online, pop) - if not nosync then self:setConfigs(module, name, data) end + if not nosync and not game.__mod_info.profile_defs[name].no_sync then self:setConfigs(module, name, data) end end function _M:checkFirstRun() local result = self.generic.firstrun if not result then - firstrun = { When=os.time() } - self:saveGenericProfile("firstrun", firstrun, false) + self:saveGenericProfile("firstrun", {firstrun=os.time()}) end return result end @@ -328,8 +358,7 @@ function _M:performlogin(login, pass) self:tryAuth() self:waitFirstAuth() if (profile.auth) then - self.generic.online = { login=login, pass=pass } - self:saveGenericProfile("online", self.generic.online) + self:saveGenericProfile("online", {login=login, pass=pass}) self:getConfigs("generic") self:syncOnline("generic") end @@ -411,24 +440,13 @@ function _M:eventGetConfigs(e) local data = zlib.decompress(e.data):unserialize() local module = e.module if not data then print("[ONLINE PROFILE] get configs") return end - for name, val in pairs(data) do - print("[ONLINE PROFILE] config ", name) + for i = 1, #data do + local val = data[i] - local field = name - local f, err = loadstring(val) - if not f and err then - print("Error loading profile config: ", err) + if module == "generic" then + self:saveGenericProfile(e.kind, val, true, i < #data) else - 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 + self:saveModuleProfile(e.kind, val, true, i < #data) end end if self.evt_cbs.GetConfigs then self.evt_cbs.GetConfigs(e) self.evt_cbs.GetConfigs = nil end @@ -489,36 +507,81 @@ function _M:logOut() self:umountProfile(true, pop) end -function _M:getConfigs(module, cb) +function _M:getConfigs(module, cb, mod_def) self:waitFirstAuth() if not self.auth then return end self.evt_cbs.GetConfigs = cb - core.profile.pushOrder(table.serialize{o="GetConfigs", module=module}) + if module == "generic" then + for k, _ in pairs(generic_profile_defs) do + if not _.no_sync then + core.profile.pushOrder(table.serialize{o="GetConfigs", module=module, kind=k}) + end + end + else + for k, _ in pairs((mod_def or game.__mod_info).profile_defs) do + if not _.no_sync then + core.profile.pushOrder(table.serialize{o="GetConfigs", module=module, kind=k}) + end + end + end end -function _M:setConfigs(module, name, val) +function _M:setConfigsBatch(v) + core.profile.pushOrder(table.serialize{o="SetConfigsBatch", v=v and true or false}) +end + +function _M:setConfigs(module, name, data) self:waitFirstAuth() if not self.auth then return end if name == "online" then return end - if type(val) ~= "string" then val = serialize(val) end - core.profile.pushOrder(table.serialize{o="SetConfigs", module=module, data=zlib.compress(table.serialize{[name] = val})}) + if module ~= "generic" then + if not game.__mod_info.profile_defs then print("[PROFILE] saving config but no profile defs", module, name) return end + if not game.__mod_info.profile_defs[name] then print("[PROFILE] saving config but no profile def kind", module, name) return end + else + if not generic_profile_defs[name] then print("[PROFILE] saving config but no profile def kind", module, name) return end + end + core.profile.pushOrder(table.serialize{o="SetConfigs", module=module, kind=name, data=zlib.compress(table.serialize(data))}) end -function _M:syncOnline(module) +function _M:syncOnline(module, mod_def) self:waitFirstAuth() 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 - - core.profile.pushOrder(table.serialize{o="SetConfigs", module=module, data=zlib.compress(table.serialize(data))}) + self:setConfigsBatch(true) + if module == "generic" then + for k, def in pairs(generic_profile_defs) do + if not def.no_sync and def.export and sync[k] then + local f = def.export + local ret = {} + setfenv(f, setmetatable({add=function(d) ret[#ret+1] = d end}, {__index=_G})) + f(sync[k]) + for i, r in ipairs(ret) do + core.profile.pushOrder(table.serialize{o="SetConfigs", module=module, kind=k, data=zlib.compress(table.serialize(r))}) + end + end + end + else + for k, def in pairs((mod_def or game.__mod_info).profile_defs) do + if not def.no_sync and def.export and sync[k] then + local f = def.export + local ret = {} + setfenv(f, setmetatable({add=function(d) ret[#ret+1] = d end}, {__index=_G})) + f(sync[k]) + for i, r in ipairs(ret) do + core.profile.pushOrder(table.serialize{o="SetConfigs", module=module, kind=k, data=zlib.compress(table.serialize(r))}) + end + end + end + end + self:setConfigsBatch(false) end function _M:checkModuleHash(module, md5) self.hash_valid = false +do self.hash_valid = true return true end -- if not self.auth then return nil, "no online profile active" end if config.settings.cheat then return nil, "cheat mode active" end if game and game:isTainted() then return nil, "savefile tainted" end diff --git a/game/engines/default/engine/interface/WorldAchievements.lua b/game/engines/default/engine/interface/WorldAchievements.lua index 6fd3a68ae73d6e0df18ac361273d028ecc00bc54..1cb397e11763f77398b12607d115cf6e2ae65e95 100644 --- a/game/engines/default/engine/interface/WorldAchievements.lua +++ b/game/engines/default/engine/interface/WorldAchievements.lua @@ -131,7 +131,7 @@ function _M:gainAchievement(id, src, ...) self:gainPersonalAchievement(true, id, src, ...) self.achieved[id] = {turn=game.turn, who=self:achievementWho(src), when=os.date("%Y-%m-%d %H:%M:%S")} - profile:saveModuleProfile("achievement."..id, self.achieved[id]) + profile:saveModuleProfile("achievements", {id=id, turn=game.turn, who=self:achievementWho(src), gained_on=os.date("%Y-%m-%d %H:%M:%S")}) game.log("#LIGHT_GREEN#New Achievement: %s!", a.name) self:showAchievement("New Achievement: #LIGHT_GREEN#"..a.name, a) profile.chat:achievement(a.name) diff --git a/game/engines/default/engine/utils.lua b/game/engines/default/engine/utils.lua index ce8e002d3072bdd4587532b5ade96965820a1114..26a17fc6cf957445a0e0d7e1c2287ab9b1c896d4 100644 --- a/game/engines/default/engine/utils.lua +++ b/game/engines/default/engine/utils.lua @@ -1057,9 +1057,7 @@ function util.showMainMenu(no_reboot, reboot_engine, reboot_engine_version, rebo if game and type(game) == "table" and game.__session_time_played_start then if game.onDealloc then game:onDealloc() end - profile.generic.modules_played = profile.generic.modules_played or {} - profile.generic.modules_played[game.__mod_info.short_name] = (profile.generic.modules_played[game.__mod_info.short_name] or 0) + (os.time() - game.__session_time_played_start) - profile:saveGenericProfile("modules_played", profile.generic.modules_played) + profile:saveGenericProfile("modules_played", {name=game.__mod_info.short_name, time_played={"inc", os.time() - game.__session_time_played_start}}) end -- Join threads diff --git a/game/engines/default/modules/boot/init.lua b/game/engines/default/modules/boot/init.lua index 6c74d50df27023315a434860cecc5f8d82e4c5a0..1dd00d698bed5989e5d79c14020c6d03bd2c491c 100644 --- a/game/engines/default/modules/boot/init.lua +++ b/game/engines/default/modules/boot/init.lua @@ -23,8 +23,8 @@ short_name = "boot" author = { "DarkGod", "darkgod@te4.org" } homepage = "http://te4.org/" is_boot = true -version = {0,9,26} -engine = {0,9,26,"te4"} +version = {0,9,29} +engine = {0,9,29,"te4"} description = [[ Bootmenu! ]] diff --git a/game/modules/tome/class/Actor.lua b/game/modules/tome/class/Actor.lua index 255f9b02f7c1b1460f346dd72757696a171c02d4..1567c6a7e290dd88da16a47c9326471c30f4ca69 100644 --- a/game/modules/tome/class/Actor.lua +++ b/game/modules/tome/class/Actor.lua @@ -1835,7 +1835,7 @@ function _M:onAddObject(o) -- Achievement checks if self.player then - if o.unique then + if o.unique and not o.lore and not o.randart then game.player:registerArtifactsPicked(o) end world:gainAchievement("DEUS_EX_MACHINA", self, o) diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index f7beb011960833e1232449100a2dde37f0373859..a8370de221d5a026f6a9b9fb949d6d518f3fed9a 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -1438,12 +1438,11 @@ function _M:setAllowedBuild(what, notify) -- Do not unlock things in easy mode --if self.difficulty == self.DIFFICULTY_EASY then return end - profile.mod.allow_build = profile.mod.allow_build or {} + profile:saveModuleProfile("allow_build", {name=what}) + if profile.mod.allow_build[what] then return end profile.mod.allow_build[what] = true - profile:saveModuleProfile("allow_build", profile.mod.allow_build) - if notify then self.state:checkDonation() -- They gained someting nice, they could be more receptive self:registerDialog(require("mod.dialogs.UnlockDialog").new(what)) diff --git a/game/modules/tome/class/Object.lua b/game/modules/tome/class/Object.lua index 2c289dda291b02dc3fddf79508a6c61272d9c0c3..7788808e4081f3f21f89df5df00a52421c641de6 100644 --- a/game/modules/tome/class/Object.lua +++ b/game/modules/tome/class/Object.lua @@ -981,7 +981,7 @@ function _M:on_identify() game.player:learnLore(self.on_id_lore) end if self.unique and self.desc and not self.no_unique_lore then - game.player:additionalLore(self.unique, self:getName(), "artifacts", self.desc) + game.player:additionalLore(self.unique, self:getName{no_add_name=true, do_color=false, no_count=true}, "artifacts", self.desc) game.player:learnLore(self.unique) end end diff --git a/game/modules/tome/class/interface/PlayerStats.lua b/game/modules/tome/class/interface/PlayerStats.lua index 2ebf2ad857d8fdadbed4806d208c818e0556c67b..aedea4c56cfeaf526bfd1c2ccd4d236138986331 100644 --- a/game/modules/tome/class/interface/PlayerStats.lua +++ b/game/modules/tome/class/interface/PlayerStats.lua @@ -28,53 +28,34 @@ end function _M:registerDeath(src) local pid = self:playerStatGetCharacterIdentifier(game.party:findMember{main=true}) local name = src.name - - profile.mod.deaths = profile.mod.deaths or {} - profile.mod.deaths.count = (profile.mod.deaths.count or 0) + 1 - - profile.mod.deaths.sources = profile.mod.deaths.sources or {} - profile.mod.deaths.sources[pid] = profile.mod.deaths.sources[pid] or {} - profile.mod.deaths.sources[pid][name] = (profile.mod.deaths.sources[pid][name] or 0) + 1 - profile:saveModuleProfile("deaths", profile.mod.deaths) + profile:saveModuleProfile("deaths", {source=name, cid=pid, nb={"inc",1}}) end function _M:registerUniqueKilled(who) local pid = self:playerStatGetCharacterIdentifier(game.party:findMember{main=true}) - profile.mod.uniques = profile.mod.uniques or { uniques={} } - profile.mod.uniques.uniques[who.name] = profile.mod.uniques.uniques[who.name] or {} - profile.mod.uniques.uniques[who.name][pid] = (profile.mod.uniques.uniques[who.name][pid] or 0) + 1 - profile:saveModuleProfile("uniques", profile.mod.uniques) + profile:saveModuleProfile("uniques", {victim=who.name, cid=pid, nb={"inc",1}}) end function _M:registerArtifactsPicked(what) if what.stat_picked_up then return end what.stat_picked_up = true local pid = self:playerStatGetCharacterIdentifier(game.party:findMember{main=true}) - local name = what:getName{do_color=false, do_count=false, force_id=true} + local name = what:getName{do_color=false, do_count=false, force_id=true, no_add_name=true} - profile.mod.artifacts = profile.mod.artifacts or { artifacts={} } - profile.mod.artifacts.artifacts[name] = profile.mod.artifacts.artifacts[name] or {} - profile.mod.artifacts.artifacts[name][pid] = (profile.mod.artifacts.artifacts[name][pid] or 0) + 1 - profile:saveModuleProfile("artifacts", profile.mod.artifacts) + profile:saveModuleProfile("artifacts", {name=name, cid=pid, nb={"inc",1}}) end function _M:registerCharacterPlayed() local pid = self:playerStatGetCharacterIdentifier(game.party:findMember{main=true}) - profile.mod.characters = profile.mod.characters or { characters={} } - profile.mod.characters.characters[pid] = (profile.mod.characters.characters[pid] or 0) + 1 - profile:saveModuleProfile("characters", profile.mod.characters) + profile:saveModuleProfile("characters", {cid=pid, nb={"inc",1}}) end function _M:registerLoreFound(lore) - profile.mod.lore = profile.mod.lore or { lore={} } - profile.mod.lore.lore[lore] = true - profile:saveModuleProfile("lore", profile.mod.lore) + profile:saveModuleProfile("lore", {name=lore, nb={"inc",1}}) end function _M:registerEscorts(status) - profile.mod.escorts = profile.mod.escorts or { saved=0, lost=0, betrayed=0, zigur=0 } - profile.mod.escorts[status] = profile.mod.escorts[status] + 1 - profile:saveModuleProfile("escorts", profile.mod.escorts) + profile:saveModuleProfile("escorts", {fate=status, nb={"inc",1}}) end diff --git a/game/modules/tome/data/general/objects/egos/boots.lua b/game/modules/tome/data/general/objects/egos/boots.lua index dc29aec6014b69ca2d74c01ebd0a006dbf07279a..667a71de9fe61e3203a64b0851998c37254fc3fc 100644 --- a/game/modules/tome/data/general/objects/egos/boots.lua +++ b/game/modules/tome/data/general/objects/egos/boots.lua @@ -223,7 +223,6 @@ newEntity{ rarity = 35, cost = 80, max_power = 80, power_regen = 1, - use_talent = { id = Talents.T_DISENGAGE, level = 2, power = 80 }, wielder = { resists={ [DamageType.NATURE] = resolvers.mbonus_material(20, 10, function(e, v) return 0, -v end), @@ -234,7 +233,7 @@ newEntity{ }, pin_immune = resolvers.mbonus_material(50, 40, function(e, v) v=v/100 return 0, v end), combat_spellpower = resolvers.mbonus_material(7, 3), - }, + }, } newEntity{ @@ -251,7 +250,7 @@ newEntity{ blind_immune = resolvers.mbonus_material(15, 5, function(e, v) v=v/100 return 0, v end), confusion_immune = resolvers.mbonus_material(15, 5, function(e, v) v=v/100 return 0, v end), disease_immune = resolvers.mbonus_material(15, 5, function(e, v) v=v/100 return 0, v end), - }, + }, } newEntity{ @@ -269,7 +268,7 @@ newEntity{ [DamageType.COLD] = resolvers.mbonus_material(10, 5), }, combat_armor = resolvers.mbonus_material(7, 3), - }, + }, } newEntity{ @@ -283,7 +282,7 @@ newEntity{ max_mana = resolvers.mbonus_material(40, 20), mana_regen = resolvers.mbonus_material(50, 10, function(e, v) v=v/100 return 0, v end), combat_spellcrit = resolvers.mbonus_material(4, 1), - }, + }, } newEntity{ @@ -300,7 +299,7 @@ newEntity{ stun_immune = resolvers.mbonus_material(15, 10, function(e, v) v=v/100 return 0, v end), pin_immune = resolvers.mbonus_material(15, 10, function(e, v) v=v/100 return 0, v end), confusion_immune = resolvers.mbonus_material(15, 10, function(e, v) v=v/100 return 0, v end), - }, + }, } newEntity{ @@ -320,7 +319,7 @@ newEntity{ pin_immune = resolvers.mbonus_material(15, 10, function(e, v) v=v/100 return 0, v end), poison_immune = resolvers.mbonus_material(15, 10, function(e, v) v=v/100 return 0, -v end), disease_immune = resolvers.mbonus_material(15, 10, function(e, v) v=v/100 return 0, -v end), - }, + }, } newEntity{ @@ -334,7 +333,7 @@ newEntity{ resource_leech_chance = resolvers.mbonus_material(10, 5), resource_leech_value = resolvers.mbonus_material(1, 1), max_life = resolvers.mbonus_material(70, 40, function(e, v) return 0, -v end), - }, + }, } newEntity{ @@ -348,10 +347,10 @@ newEntity{ disarm_immune = resolvers.mbonus_material(15, 10, function(e, v) v=v/100 return 0, v end), combat_physcrit = resolvers.mbonus_material(4, 1), combat_dam = resolvers.mbonus_material(3, 3), - resists_pen = { + resists_pen = { [DamageType.PHYSICAL] = resolvers.mbonus_material(10, 5), }, - }, + }, } newEntity{ @@ -367,7 +366,7 @@ newEntity{ inc_stats = { [Stats.STAT_MAG] = resolvers.mbonus_material(5, 1), }, - }, + }, } newEntity{ @@ -383,5 +382,5 @@ newEntity{ combat_mentalresist = resolvers.mbonus_material(7, 1), combat_physresist = resolvers.mbonus_material(7, 1), combat_spellresist = resolvers.mbonus_material(7, 1), - }, + }, } \ No newline at end of file diff --git a/game/modules/tome/data/zones/town-angolwen/npcs.lua b/game/modules/tome/data/zones/town-angolwen/npcs.lua index 867a79d2f3b10b41816160e0f789dc3b8c35a10f..085646f185b6d27413157a7a6508205cc617f223 100644 --- a/game/modules/tome/data/zones/town-angolwen/npcs.lua +++ b/game/modules/tome/data/zones/town-angolwen/npcs.lua @@ -94,7 +94,6 @@ newEntity{ define_as = "TARELION", level_range = {30, nil}, exp_worth = 2, rank = 4, size_category = 3, - female = true, mana_regen = 120, max_mana = 2000, max_life = 350, life_rating = 24, fixed_rating = true, diff --git a/game/modules/tome/init.lua b/game/modules/tome/init.lua index 5b649c668b5b8f952b211da7f6692a3ecd7b3bd6..a8a3094a350a14bc0420ff234834b8bf698ac6db 100644 --- a/game/modules/tome/init.lua +++ b/game/modules/tome/init.lua @@ -48,3 +48,15 @@ starter = "mod.load" profile_stats_fields = {"artifacts", "characters", "deaths", "uniques", "lore", "escorts"} allow_userchat = true -- We can talk to the online community no_get_name = true -- Name setting for new characters is done by the module itself + +-- Define the fields that are sync'ed online, and how they are sync'ed +profile_defs = { + allow_build = { {name="index:string:30"}, receive=function(data, save) save[data.name] = true end, export=function(env) for k, _ in pairs(env) do add{name=k} end end }, + lore = { {name="index:string:30"}, receive=function(data, save) save.lore = save.lore or {} save.lore[data.name] = true end, export=function(env) for k, v in pairs(env.lore or {}) do add{name=k} end end }, + escorts = { {fate="index:enum(lost,betrayed,zigur,saved)"}, {nb="number"}, receive=function(data, save) inc_set(save, data.fate, data, "nb") end, export=function(env) for k, v in pairs(env) do add{fate=k, nb=v} end end }, + artifacts = { {cid="index:string:50"}, {name="index:string:40"}, {nb="number"}, receive=function(data, save) save.artifacts = save.artifacts or {} save.artifacts[data.cid] = save.artifacts[data.cid] or {} inc_set(save.artifacts[data.cid], data.name, data, "nb") end, export=function(env) for cid, d in pairs(env.artifacts or {}) do for name, v in pairs(d) do add{cid=cid, name=name, nb=v} end end end }, + characters = { {cid="index:string:50"}, {nb="number"}, receive=function(data, save) save.characters = save.characters or {} inc_set(save.characters, data.cid, data, "nb") end, export=function(env) for k, v in pairs(env.characters or {}) do add{cid=k, nb=v} end end }, + uniques = { {cid="index:string:50"}, {victim="index:string:50"}, {nb="number"}, receive=function(data, save) save.uniques = save.uniques or {} save.uniques[data.cid] = save.uniques[data.cid] or {} inc_set(save.uniques[data.cid], data.victim, data, "nb") end, export=function(env) for cid, d in pairs(env.uniques or {}) do for name, v in pairs(d) do add{cid=cid, victim=name, nb=v} end end end }, + deaths = { {cid="index:string:50"}, {source="index:string:50"}, {nb="number"}, receive=function(data, save) save.sources = save.sources or {} save.sources[data.cid] = save.sources[data.cid] or {} inc_set(save.sources[data.cid], data.source, data, "nb") end, export=function(env) for cid, d in pairs(env.sources or {}) do for name, v in pairs(d) do add{cid=cid, source=name, nb=v} end end end }, + achievements = { {id="index:string:40"}, {gained_on="timestamp"}, {who="string:50"}, {turn="number"}, receive=function(data, save) save[data.id] = {who=data.who, when=data.gained_on, turn=data.turn} end, export=function(env) for id, v in pairs(env) do add{id=id, who=v.who, gained_on=v.when, turn=v.turn} end end }, +} diff --git a/game/profile-thread/Client.lua b/game/profile-thread/Client.lua index ed2d011cf4f1517721599fde09f6cafbb12ca269..a79d0987efb139232308e8add67ea59546ecb5b6 100644 --- a/game/profile-thread/Client.lua +++ b/game/profile-thread/Client.lua @@ -30,7 +30,7 @@ end function _M:connected() if self.sock then return true end - self.sock = socket.connect("te4.org", 2257) + self.sock = socket.connect("te4.org", 2259) if not self.sock then return false end -- self.sock:settimeout(10) print("[PROFILE] Thread connected to te4.org") @@ -43,7 +43,7 @@ end --- Connects the second tcp channel to receive data function _M:connectedPull() if self.psock then return true end - self.psock = socket.connect("te4.org", 2258) + self.psock = socket.connect("te4.org", 2260) if not self.psock then return false end -- self.psock:settimeout(10) print("[PROFILE] Pull socket connected to te4.org") @@ -256,21 +256,43 @@ end function _M:orderGetConfigs(o) if not self.auth then return end - self:command("GCFS", o.module) + self:command("CGET", o.module, o.kind) if self:read("200") then local _, _, size = self.last_line:find("^([0-9]+)") size = tonumber(size) if not size or size < 1 then return end local body = self:receive(size) - cprofile.pushEvent(string.format("e='GetConfigs' module=%q data=%q", o.module, body)) + cprofile.pushEvent(string.format("e='GetConfigs' module=%q kind=%q data=%q", o.module, o.kind, body)) + end +end + +function _M:orderSetConfigsBatch(o) + if not o.v then + if not self.setConfigsBatching then return end + self.setConfigsBatchingLevel = self.setConfigsBatchingLevel - 1 + if self.setConfigsBatchingLevel > 0 then return end + + print("[PROFILE THREAD] flushing CSETs") + + local data = zlib.compress(table.serialize(self.setConfigsBatching)) + self:command("FSET", data:len()) + if self:read("200") then self.sock:send(data) end + + self.setConfigsBatching = nil + else + print("[PROFILE THREAD] batching CSETs") + self.setConfigsBatching = self.setConfigsBatching or {} + self.setConfigsBatchingLevel = (self.setConfigsBatchingLevel or 0) + 1 end end function _M:orderSetConfigs(o) if not self.auth then return end - self:command("SCFS", o.data:len(), o.module) - if self:read("200") then - self.sock:send(o.data) + if self.setConfigsBatching then + self.setConfigsBatching[#self.setConfigsBatching+1] = o + else + self:command("CSET", o.data:len(), o.module, o.kind) + if self:read("200") then self.sock:send(o.data) end end end diff --git a/ideas/quests.ods b/ideas/quests.ods index 267fd2f43118649e1707a2466a6528a062a536b1..9f355f20f4a250d07c5ab1717b42eb549c5008b2 100644 Binary files a/ideas/quests.ods and b/ideas/quests.ods differ diff --git a/src/profile.c b/src/profile.c index 3c796a1a95accb5be523aa3d0d5640d08e1df605..7afd2436183639002827882babdf8c378ecd3d30 100644 --- a/src/profile.c +++ b/src/profile.c @@ -68,6 +68,7 @@ int pop_order(lua_State *L) if (q) { lua_pushlstring(L, q->payload, q->payload_len); +// printf("[profile order POP] %s\n", lua_tostring(L,-1)); free(q->payload); free(q); } @@ -139,6 +140,7 @@ int thread_profile(void *data) luaopen_core(L); luaopen_socket_core(L); luaopen_mime_core(L); + luaopen_zlib(L); luaL_openlib(L, "cprofile", threadlib, 0); lua_pop(L, 1); // Override "print" if requested