csvdata.lua 4.05 KB
-- 对sharetable 的一层封装 实现热更新

local skynet = require "skynet"
local service = require "skynet.service"
local sharetable = require "skynet.sharetable"

local function sharetable_service()
	local skynet = require "skynet"
	local sharetable = require "skynet.sharetable"

	local files = {}	-- filename
	local csvdata = {}

	local initfile = {
		["src/csvdata/init.lua"] = {},
		["src/csvdata/init_adv.lua"] = {},
	}

	function csvdata.query(source, filename)
		files[filename] = files[filename] or {}
		files[filename][source] = 1
	end

	function csvdata.close(source)
		for filename, info in pairs(files) do
			info[source] = nil
		end
	end

	function csvdata.realName(file)
		if string.match(file, "Csv$") then
			if initfile["src/csvdata/init.lua"] and initfile["src/csvdata/init.lua"][file] then
				file = initfile["src/csvdata/init.lua"][file]
			elseif initfile["src/csvdata/init_adv.lua"] and initfile["src/csvdata/init_adv.lua"][file] then
				file = initfile["src/csvdata/init_adv.lua"][file]
			end
			file = "src/" .. file .. ".lua"
		end
		return file
	end

	function csvdata.hotfix(_, ...)
		local now = skynet.timex()

		local filenames = {...}

		local needHotfix = {}
		for _, filename in ipairs(filenames) do
			-- 更新下
			skynet.error(string.format("hotfix_csvdata time: %s, file: %s", now, filename))

			if initfile[filename] then
				sharetable.loadfile(filename)
				sharetable.update(filename)
			else
				sharetable.loadfile(csvdata.realName(filename))
			end

			if files[filename] then
				for source, _ in pairs(files[filename]) do
					needHotfix[source] = needHotfix[source] or {}
					table.insert(needHotfix[source], filename)
				end
			end
		end

		for source, files in pairs(needHotfix) do
			skynet.send(source, "csvdata", "hotfix", table.unpack(files))
		end
	end

	skynet.dispatch("lua", function(_,source,cmd,...)
		skynet.ignoreret()
		csvdata[cmd](source,...)
	end)

	skynet.register_protocol {
		name = "csvdata",
		id = 102,
		pack = skynet.pack,
		unpack = skynet.unpack,
	}

	-- 初始化csvdata
	skynet.start(function()
		for file, _ in pairs(initfile) do
			sharetable.loadfile(file)
			local init = sharetable.query(file)
			if not init then
				error("csvdata load init file error " .. file)
				return
			end
			initfile[file] = init
			for _, one in pairs(init) do
				sharetable.loadfile("src/" .. one .. ".lua")
			end
		end
	end)
end


local cache = {}
local csvdata

local function load_csvdata(t, key)
	if key == "address" then
		t.address = service.new("csvdata", sharetable_service)
		return t.address
	else
		if cache[key] then return cache[key] end

		local realName = csvdata.realName(key)

		local tab = sharetable.query(realName)
		if not tab then
			error("dont have csvdata :  " .. realName)
			return
		end
		cache[key] = tab
		-- 增加引用
		skynet.send(csvdata.address, "lua", "query", key)
		return tab
	end
end

local function close_hotfix(t)
	local addr = rawget(t, "address")
	if addr then
		skynet.send(addr, "lua", "close")
	end
end

csvdata = setmetatable ( {} , {
	__index = load_csvdata,
	__gc = close_hotfix,
})

function csvdata.init()
	return csvdata.address
end

function csvdata.hotfix(...)
	skynet.send(csvdata.address, "lua", "hotfix", ...)
end

function csvdata.realName(file)
	if string.match(file, "Csv$") then
		if csvdata["src/csvdata/init.lua"] and csvdata["src/csvdata/init.lua"][file] then
			file = csvdata["src/csvdata/init.lua"][file]
		elseif csvdata["src/csvdata/init_adv.lua"] and csvdata["src/csvdata/init_adv.lua"][file] then
			file = csvdata["src/csvdata/init_adv.lua"][file]
		end
		file = "src/" .. file .. ".lua"
	end
	return file
end

local function update(file)
	-- 更新一下
	if cache[file] then
		sharetable.update(csvdata.realName(file))
	end
end

skynet.register_protocol {
	name = "csvdata",
	id = 102,
	pack = skynet.pack,
	unpack = skynet.unpack,
	dispatch = function(_, _, cmd, ...)
		skynet.ignoreret()
		if cmd == "hotfix" then
			-- skynet.error("csvdara hotfix update", ...)
			local files = {...}
			for _, file in ipairs(files) do
				update(file)
			end
			return
		end
	end,
}

return csvdata