diff --git a/game/engines/default/engine/Module.lua b/game/engines/default/engine/Module.lua
index 808d25e845b1930ab71dfd1cdb929d363f436b54..edd55a82f23d6be70fd610206b36fb9a1ddeca85 100644
--- a/game/engines/default/engine/Module.lua
+++ b/game/engines/default/engine/Module.lua
@@ -1016,6 +1016,7 @@ function _M:instanciate(mod, name, new_game, no_reboot, extra_module_info)
 	profile:addStatFields(unpack(mod.profile_stats_fields or {}))
 	profile:setConfigsBatch(true)
 	profile:loadModuleProfile(mod.short_name, mod)
+	profile:incrLoadProfile(mod)
 	profile:currentCharacter(mod.full_version_string, "game did not tell us")
 
 	UIBase:clearCache()
diff --git a/game/engines/default/engine/PlayerProfile.lua b/game/engines/default/engine/PlayerProfile.lua
index 3a5ca60d17d01608b715b1db9719348489c7b142..2b97f37207052de5d0f77260c3ecd475ecbb525e 100644
--- a/game/engines/default/engine/PlayerProfile.lua
+++ b/game/engines/default/engine/PlayerProfile.lua
@@ -79,6 +79,7 @@ function _M:init()
 	self.generic = {}
 	self.modules = {}
 	self.evt_cbs = {}
+	self.data_log = {log={}}
 	self.stats_fields = {}
 	local checkstats = function(self, field) return self.stats_fields[field] end
 	self.config_settings =
@@ -236,6 +237,7 @@ function _M:loadModuleProfile(short_name, mod_def)
 	-- Delay when we are currently saving
 	if savefile_pipe and savefile_pipe.saving then savefile_pipe:pushGeneric("loadModuleProfile", function() self:loadModuleProfile(short_name) end) return end
 
+	local def = mod_def.profile_defs or {}
 	local function load(online)
 		local pop = self:mountProfile(online, short_name)
 		local d = "/current-profile/modules/"..short_name.."/"
@@ -251,6 +253,12 @@ function _M:loadModuleProfile(short_name, mod_def)
 					else
 						self.modules[short_name][field] = self.modules[short_name][field] or {}
 						self:loadData(f, self.modules[short_name][field])
+						if def[field].incr_only then
+							if not self.modules[short_name][field].incr_only then
+								print("[PROFILE] Old non incremental data for "..field..": discarding")
+								self.modules[short_name][field] = {}
+							end
+						end
 					end
 				end
 			end
@@ -319,6 +327,7 @@ function _M:saveModuleProfile(name, data, nosync, nowrite)
 	if module == "boot" then return end
 	core.game.resetLocale()
 	if not game or not game.__mod_info.profile_defs then return end
+	if game.__mod_info.profile_defs[name].incr_only then print("[PROFILE] data in incr only mode but called with saveModuleProfile", name) 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, nosync) end) return end
@@ -364,6 +373,49 @@ function _M:saveModuleProfile(name, data, nosync, nowrite)
 	if not nosync and not game.__mod_info.profile_defs[name].no_sync then self:setConfigs(module, name, data) end
 end
 
+--- Loads the incremental log data
+function _M:incrLoadProfile(mod_def)
+	if not mod_def or not mod_def.short_name then return end
+	local pop = self:mountProfile(true)
+	local file = "/current-profile/modules/"..mod_def.short_name.."/incr.log"
+	if fs.exists(file) then
+		local f, err = loadfile(file)
+		if not f and err then
+			print("Error loading incr log", file, err)
+		else
+			self:loadData(f, self.data_log)
+		end
+	end
+	self:umountProfile(true, pop)
+end
+
+--- Saves a incr profile data
+function _M:incrDataProfile(name, data)
+	if module == "boot" then return end
+	core.game.resetLocale()
+	if not game or not game.__mod_info.profile_defs then return end
+	if not game.__mod_info.profile_defs[name].incr_only then print("[PROFILE] data in non-incr only mode but called with incrDataProfile", name) return end
+
+	-- Delay when we are currently saving
+	if savefile_pipe and savefile_pipe.saving then savefile_pipe:pushGeneric("incrDataProfile", function() self:incrDataProfile(name, data) end) return end
+
+	local module = self.mod_name
+
+	-- Check for readability
+	local dataenv = self.data_log.log
+	dataenv[#dataenv+1] = {module=game.__mod_info.short_name, kind=name, data=data}
+
+	local pop = self:mountProfile(true, module)
+	local f = fs.open("/modules/"..module.."/incr.log", "w")
+	if f then
+		f:write(serialize(self.data_log))
+		f:close()
+	end
+	self:umountProfile(true, pop)
+
+	self:syncIncrData()
+end
+
 function _M:checkFirstRun()
 	local result = self.generic.firstrun
 	if not result then
@@ -503,6 +555,16 @@ function _M:eventGetNews(e)
 	end
 end
 
+function _M:eventIncrLogConsume(e)
+	local module = game.__mod_info.short_name
+	if not module then return end
+	print("[PROFILE] Server accepted our incr log, deleting")
+	local pop = self:mountProfile(true, module)
+	fs.delete("/modules/"..module.."/incr.log")
+	self:umountProfile(true, pop)
+	self.data_log.log = {}
+end
+
 function _M:eventGetConfigs(e)
 	local data = zlib.decompress(e.data):unserialize()
 	local module = e.module
@@ -607,14 +669,14 @@ function _M:getConfigs(module, cb, mod_def)
 	if not self.auth then return end
 	self.evt_cbs.GetConfigs = cb
 	if module == "generic" then
-		for k, _ in pairs(generic_profile_defs) do
-			if not _.no_sync then
+		for k, def in pairs(generic_profile_defs) do
+			if not def.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 or {}) do
-			if not _.no_sync then
+		for k, def in pairs((mod_def or game.__mod_info).profile_defs or {}) do
+			if not def.no_sync and not def.incr_only then
 				core.profile.pushOrder(table.serialize{o="GetConfigs", module=module, kind=k})
 			end
 		end
@@ -625,6 +687,15 @@ function _M:setConfigsBatch(v)
 	core.profile.pushOrder(table.serialize{o="SetConfigsBatch", v=v and true or false})
 end
 
+function _M:syncIncrData()
+	self:waitFirstAuth()
+	if not self.auth then return end
+	local module = game and game.__mod_info.short_name
+	if not module then return end
+	
+	core.profile.pushOrder(table.serialize{o="SendIncrLog", data=zlib.compress(table.serialize(self.data_log.log))})
+end
+
 function _M:setConfigs(module, name, data)
 	self:waitFirstAuth()
 	if not self.auth then return end
@@ -660,7 +731,7 @@ function _M:syncOnline(module, mod_def)
 		end
 	else
 		for k, def in pairs((mod_def or game.__mod_info).profile_defs or {}) do
-			if not def.no_sync and def.export and sync[k] then
+			if not def.no_sync and not def.incr_only 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}))
diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua
index 8d048a6ea8b4e01f5c9f6371d98afd7475ea3a8a..911ec5c7a3f7c4cb5fda4464a13b49949eb2d602 100644
--- a/game/modules/tome/class/Game.lua
+++ b/game/modules/tome/class/Game.lua
@@ -1857,8 +1857,6 @@ do return end
 			print(f, err)
 			setfenv(f, setmetatable({level=self.level, zone=self.zone}, {__index=_G}))
 			print(pcall(f))
-do return end
-			self:registerDialog(require("mod.dialogs.DownloadCharball").new())
 		end end,
 		[{"_f","ctrl"}] = function() if config.settings.cheat then
 			self.player.quests["love-melinda"] = nil
diff --git a/game/modules/tome/class/interface/PlayerStats.lua b/game/modules/tome/class/interface/PlayerStats.lua
index 9508c7f4536a5050fb575a2a3d2e37121a63851f..a020464f064e3d1179a48bfc5ce8faf570749ed3 100644
--- a/game/modules/tome/class/interface/PlayerStats.lua
+++ b/game/modules/tome/class/interface/PlayerStats.lua
@@ -29,13 +29,13 @@ end
 function _M:registerDeath(src)
 	local pid = self:playerStatGetCharacterIdentifier(game.party:findMember{main=true})
 	local name = src.name
-	profile:saveModuleProfile("deaths", {source=name, cid=pid, nb={"inc",1}})
+	profile:incrDataProfile("deaths", {source=name, cid=pid, nb=1})
 end
 
 function _M:registerUniqueKilled(who)
 	local pid = self:playerStatGetCharacterIdentifier(game.party:findMember{main=true})
 
-	profile:saveModuleProfile("uniques", {victim=who.name, cid=pid, nb={"inc",1}})
+	profile:incrDataProfile("uniques", {victim=who.name, cid=pid, nb=1})
 end
 
 function _M:registerArtifactsPicked(what)
@@ -44,13 +44,13 @@ function _M:registerArtifactsPicked(what)
 	local pid = self:playerStatGetCharacterIdentifier(game.party:findMember{main=true})
 	local name = what:getName{do_color=false, do_count=false, force_id=true, no_add_name=true}
 
-	profile:saveModuleProfile("artifacts", {name=name, cid=pid, nb={"inc",1}})
+	profile:incrDataProfile("artifacts", {name=name, cid=pid, nb=1})
 end
 
 function _M:registerCharacterPlayed()
 	local pid = self:playerStatGetCharacterIdentifier(game.party:findMember{main=true})
 
-	profile:saveModuleProfile("characters", {cid=pid, nb={"inc",1}})
+	profile:incrDataProfile("characters", {cid=pid, nb=1})
 end
 
 function _M:registerLoreFound(lore)
diff --git a/game/modules/tome/dialogs/DownloadCharball.lua b/game/modules/tome/dialogs/DownloadCharball.lua
deleted file mode 100644
index 99932141acfc0ad7fb9f83d25d4f1d396a9fc319..0000000000000000000000000000000000000000
--- a/game/modules/tome/dialogs/DownloadCharball.lua
+++ /dev/null
@@ -1,99 +0,0 @@
--- ToME - Tales of Maj'Eyal
--- Copyright (C) 2009 - 2017 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"
-local ListColumns = require "engine.ui.ListColumns"
-local TextzoneList = require "engine.ui.TextzoneList"
-local Separator = require "engine.ui.Separator"
-local Image = require "engine.ui.Image"
-
-module(..., package.seeall, class.inherit(Dialog))
-
-function _M:init()
-	Dialog.init(self, "Download charball", game.w * 0.8, game.h * 0.8)
-
-	self:generateList()
-
-	self.c_list = ListColumns.new{width=math.floor(self.iw - 10), height=self.ih - 10, scrollbar=true, sortable=true, columns={
-		{name="Player", width=30, display_prop="player", sort="player"},
-		{name="Character", width=70, display_prop="character", sort="character"},
-	}, list=self.list, fct=function(item) self:importCharball(item) end, select=function(item, sel) self:select(item) end}
-
-	self:loadUI{
-		{left=0, top=0, ui=self.c_list},
-	}
-	self:setFocus(self.c_list)
-	self:setupUI()
-	self:select(self.list[1])
-
-	self.key:addBinds{
-		EXIT = function() game:unregisterDialog(self) end,
-	}
-end
-
-function _M:generateList()
-	profile.chat:selectChannel("tome")
-
-	-- Makes up the list
-	local list = {}
-	for login, user in pairs(profile.chat.channels.tome.users) do
-		if user.valid == "validate" and user.current_char_data and user.current_char_data.uuid then
-			list[#list+1] = { player=user.name, character=user.current_char, id=user.id, uuid=user.current_char_data.uuid }
-		end
-	end
-	-- Add known artifacts
-	table.sort(list, function(a, b) return a.character < b.character end)
-	self.list = list
-end
-
-function _M:select(item)
-	if item then
-	end
-end
-
-function _M:importCharball(item)
-	if not item or not item.uuid then return end
-
-	local data = profile:getCharball(item.id, item.uuid)
-	local f = fs.open("/charballs/__import.charball", "w")
-	f:write(data)
-	f:close()
-
-	savefile_pipe:ignoreSaveToken(true)
-	local ep = savefile_pipe:doLoad("__import", "entity", "engine.CharacterBallSave", "__import")
-	savefile_pipe:ignoreSaveToken(false)
-	for a, _ in pairs(ep.members) do
-		if a.__CLASSNAME == "mod.class.Player" then
-			mod.class.NPC.castAs(a)
-			engine.interface.ActorAI.init(a, a)
-			a.quests = {}
-			a.ai = "tactical"
-			a.ai_state = {talent_in=1}
-			a.no_drops = true
-			a.keep_inven_on_death = false
-			a.energy.value = 0
-			a.player = nil
-			a.faction = "enemies"
-			game.zone:addEntity(game.level, a, "actor", game.player.x, game.player.y-1)
-
-			game:unregisterDialog(self)
-		end
-	end
-end
diff --git a/game/modules/tome/init.lua b/game/modules/tome/init.lua
index cf3cd115a8cd8c3200f7603d240ae2487c18d238..a4dd4110e08c01f3b6fc23b190ce9debc0c5d9fc 100644
--- a/game/modules/tome/init.lua
+++ b/game/modules/tome/init.lua
@@ -148,15 +148,15 @@ load_tips = {
 profile_defs = {
 	allow_build = { {name="index:string:50"}, 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 },
+	escorts = { incr_only=true, {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 = { incr_only=true, {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 = { incr_only=true, {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 = { incr_only=true, {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 = { incr_only=true, {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 },
 	donations = { no_sync=true, {last_ask="timestamp"}, receive=function(data, save) save.last_ask = data.last_ask end, export=function(env) add{last_ask=env.last_ask} end },
 	scores = {
-		nosync=true,
+		no_sync=true,
 		receive=function(data,save)
 			save.sc = save.sc or {}
 			save.sc[data.world] = save.sc[data.world] or {}
diff --git a/game/profile-thread/Client.lua b/game/profile-thread/Client.lua
index dea4d4ac1f21f12b9769d3cb4b795675473d3a1e..c3ab20f1b1bd923ee511475e1075850d6d367c92 100644
--- a/game/profile-thread/Client.lua
+++ b/game/profile-thread/Client.lua
@@ -392,6 +392,12 @@ function _M:orderSetConfigs(o)
 	end
 end
 
+function _M:orderSendIncrLog(o)
+	if not self.auth then cprofile.pushEvent("e='IncrLogConsume' ok=false") return end
+	self:command("CINC", o.data:len())
+	if self:read("200") then self.sock:send(o.data) cprofile.pushEvent("e='IncrLogConsume' ok=true") end
+end
+
 function _M:orderSendError(o)
 	o = table.serialize(o)
 	self:command("ERR_", o:len())