diff --git a/game/engine/Game.lua b/game/engine/Game.lua
index 9ee202e6da308c5590e8b51a332ba541c002bc66..85a9ae52fda13e0d8b842afee499fc705392cde3 100644
--- a/game/engine/Game.lua
+++ b/game/engine/Game.lua
@@ -42,6 +42,8 @@ function _M:init(keyhandler)
 	self.mouse:setCurrent()
 
 	self.uniques = {}
+
+	self.__threads = {}
 end
 
 function _M:loaded()
@@ -208,3 +210,27 @@ end
 --- Requests the game to save
 function _M:saveGame()
 end
+
+--- Save a thread into the thread pool
+-- Threads will be auto join'ed when the module exits or when it can<br/>
+-- ALL THREADS registered *MUST* return true when they exit
+function _M:registerThread(th, linda)
+	print("[THREAD] registering", th, linda, #self.__threads+1)
+	self.__threads[#self.__threads+1] = {th=th, linda=linda}
+	return #self.__threads
+end
+
+--- Try to join all registered threads
+-- @param timeout the time in seconds to wait for each thread
+function _M:joinThreads(timeout)
+	for i = #self.__threads, 1, -1 do
+		local th = self.__threads[i].th
+		print("[THREAD] Thread join", i, th)
+		local v, err = th:join(timeout)
+		if err then print("[THREAD] error", th) error(err) end
+		if v then
+			print("[THREAD] Thread result", i, th, "=>", v)
+			table.remove(self.__threads, i)
+		end
+	end
+end
diff --git a/game/engine/GameTurnBased.lua b/game/engine/GameTurnBased.lua
index ffe5200e0b0f671418e8d4f63680531f0a5a0480..27c30e187e5404e49a974d709eb191aa99bd3afd 100644
--- a/game/engine/GameTurnBased.lua
+++ b/game/engine/GameTurnBased.lua
@@ -45,6 +45,11 @@ function _M:tick()
 		engine.GameEnergyBased.tick(self)
 		self.turn = self.turn + 1
 		self:onTurn()
+
+		-- Try to join threads if any, every hundred turns
+		if self.turn % 100 == 0 then
+			self:joinThreads(0)
+		end
 	end
 end
 
diff --git a/game/engine/Module.lua b/game/engine/Module.lua
index 29d0fae576e1a1f3015bc38585db61c2257299a7..d14100cf08df305506144cddde5d8eb4672b846e 100644
--- a/game/engine/Module.lua
+++ b/game/engine/Module.lua
@@ -95,6 +95,8 @@ function _M:loadDefinition(dir, team)
 			end
 			profile:loadModuleProfile(mod.short_name)
 			local m = require(mod.starter)
+			m[1].__session_time_played_start = os.time()
+			m[1].__mod_info = mod
 			print("[MODULE LOADER] loading module", mod.long_name, "["..mod.starter.."]", "::", m[1] and m[1].__CLASSNAME, m[2] and m[2].__CLASSNAME)
 			return m[1], m[2]
 		end
diff --git a/game/engine/PlayerProfile.lua b/game/engine/PlayerProfile.lua
index 35091d88f740a4062560591753052faef459deb6..c3a250425ae3eb8186224ae5c8075941ae0bde8f 100644
--- a/game/engine/PlayerProfile.lua
+++ b/game/engine/PlayerProfile.lua
@@ -21,6 +21,7 @@ require "engine.class"
 local http = require "socket.http"
 local url = require "socket.url"
 local ltn12 = require "ltn12"
+local lanes = require "lanes"
 require "Json2"
 
 ------------------------------------------------------------
@@ -187,10 +188,33 @@ end
 -----------------------------------------------------------------------
 
 function _M:rpc(data)
-	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)
+	-- 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:tryAuth()
@@ -229,7 +253,7 @@ function _M:setConfigs(module, name, val)
 
 	if type(val) ~= "string" then val = serialize(val) end
 
-	local data = self:rpc{action="SetConfigs", login=self.login, hash=self.auth.hash, module=module, data={[name] = val}}
+	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
@@ -243,7 +267,7 @@ function _M:syncOnline(module)
 	local data = {}
 	for k, v in pairs(sync) do if k ~= "online" then data[k] = serialize(v) end end
 
-	local data = self:rpc{action="SetConfigs", login=self.login, hash=self.auth.hash, module=module, data=data}
+	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
diff --git a/game/engine/utils.lua b/game/engine/utils.lua
index 4535aa63626789ba00914dfb634e36ab5fe7620f..69bc2f099b492a1d179c78d193cfceca90020064 100644
--- a/game/engine/utils.lua
+++ b/game/engine/utils.lua
@@ -489,6 +489,15 @@ function util.findFreeGrid(sx, sy, radius, block, what)
 end
 
 function util.showMainMenu(no_reboot)
+	if game and type(game) == "table" and game.__session_time_played_start then
+		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)
+	end
+
+	-- Join threads
+	if game and type(game) == "table" then game:joinThreads(30) end
+
 	if no_reboot then
 		local Menu = require("special.mainmenu.class.Game")
 		game = Menu.new()
diff --git a/game/special/mainmenu/class/Game.lua b/game/special/mainmenu/class/Game.lua
index 261c88bce136a722009e7ccf16de3f8a623da4d8..2e669a19261f88773187844814dd05d9965d96a8 100644
--- a/game/special/mainmenu/class/Game.lua
+++ b/game/special/mainmenu/class/Game.lua
@@ -35,6 +35,7 @@ function _M:init()
 	engine.Game.init(self, engine.KeyBind.new())
 
 	self.background = core.display.loadImage("/data/gfx/mainmenu/background.png")
+	self.refuse_threads = true
 end
 
 function _M:run()