Lots of weird incompatibilities - proposed fix for 1.2.0!

Where bugs go to lie down and rest

Moderator: Moderator

Post Reply
Message
Author
Nagyhal
Wyrmic
Posts: 282
Joined: Tue Feb 15, 2011 12:01 am

Lots of weird incompatibilities - proposed fix for 1.2.0!

#1 Post by Nagyhal »

There's been a bug going around, so to speak...

These mysterious addon incompatibilities have been appearing for a while now, generally involving what seemed like well-sanitized and thoughtfully crafted addons. The other addon owners I've seen to fall afoul of them have realized that changing the weights of their addons was enough to fix them, blamed the fault on some obscure feature of superloading and then moved on.

A mere few days after uploading my latest big update to the Beholder, there've been two incompatibility reports with two separate addons that have been mysterious in the same way. I was desperate to find out what was happening, but all I could do was comment lines out in hooks/load.lua files until I came across the culprit. And here it is: The humble require call.

(Warning! Dense chunk of technical mumbo-jumbo incoming...)

When T-Engine uses the loadAddons function, it goes through each addon in the list, mounting superloaded files to the virtual directory structure, mounting overloaded files to the virtual directory structure, and then running files in the "hooks" folder so as to execute the class:bindHook function. The distinction, however, with superloads and overloads being mounted and hook files being run causes a difficulty. When libraries are loaded with the require function, they are kept in memory permanently in their current state. If require loads files before T-Engine builds the references to all superloaded versions of those files, then any superloads coming after are disregarded.

(Warning! Frustatingly simple solution incoming...)

All you have to do is: Load hook files after all superloading and overloading.

Here's my proposed fix to the loadAddons function in default/engine/Module.lua. I have tested it and it works beautifully!

Code: Select all

function _M:loadAddons(mod, saveuse)
	local hashlist = {}
	local adds = self:listAddons(mod, true)

	if saveuse then saveuse = table.reverse(saveuse) end

	-- Filter based on settings
	for i = #adds, 1, -1 do
		local add = adds[i]
		local removed = false
		if saveuse then
			if not saveuse[add.short_name] then
				print("Removing addon "..add.short_name..": not allowed by savefile")
				table.remove(adds, i) removed = true
			end
		else
			if add.cheat_only and not config.settings.cheat then
				print("Removing addon "..add.short_name..": cheat mode required")
				table.remove(adds, i) removed = true
			elseif add.dlc == "no" then
				print("Removing addon "..add.short_name..": donator required")
				table.remove(adds, i) removed = true
			elseif add.id_dlc == "no" then
				print("Removing addon "..add.short_name..": DLC not granted")
				table.remove(adds, i) removed = true
			elseif config.settings.addons[add.for_module] and config.settings.addons[add.for_module][add.short_name] ~= nil then
				-- Forbidden by config
				if config.settings.addons[add.for_module][add.short_name] == false then
					print("Removing addon "..add.short_name..": not allowed by config")
					table.remove(adds, i) removed = true
				end
			else
				-- Forbidden by version
				if not add.natural_compatible then
					table.remove(adds, i) removed = true
				end
			end
		end

		if add.dlc and add.dlc_files then
			if not removed and add.dlc_files.classes then
				for _, name in ipairs(add.dlc_files.classes) do
					print("Preloading DLC class", name)
					local data = profile:getDLCD(add.for_module.."-"..add.short_name, ("%d.%d.%d"):format(add.version[1],add.version[2],add.version[3]), name:gsub("%.", "/")..".lua")
					if data and data ~= '' then
						profile.dlc_files.classes[name] = data
					elseif not __module_extra_info.ignore_addons_not_loading then
						print("Removing addon "..add.short_name..": DLC class not received")
						table.remove(adds, i) removed = true
						if saveuse then
							-- The savefile requires it, but we couldnt activate it, abord
							core.game.setRebootMessage(([[The savefile requires the #YELLOW#%s#WHITE# addon.
Some of its features require being online and could not be enabled. To prevent damaging the savefile loading was aborded.

You may try to force loading if you are sure the savefile does not use that addon, at your own risk, by checking the "Ignore unloadable addons" checkbox on the load game screen..]]):format(add.long_name))
							util.showMainMenu(nil, nil, nil, nil, nil, nil, "show_ignore_addons_not_loading=true")
						end
						break
					else
						add.dlc = "no"
						print("Removing addon "..add.short_name..": dlc file required not found")
						table.remove(adds, i) removed = true
					end
				end
			end
		end
	end

	mod.addons = {}
	local hooks_list = {}

	_G.__addons_superload_order = {}
	for i, add in ipairs(adds) do
		add.version_name = ("%s-%s-%d.%d.%d"):format(mod.short_name, add.short_name, add.version[1], add.version[2], add.version[3])

		print("Binding addon", add.long_name, add.teaa, add.version_name)
		local base
		if add.teaa then
			base = fs.getRealPath(add.teaa)
		else
			base = fs.getRealPath(add.dir)
		end

		if add.data then
			print(" * with data")
			if add.teaa then fs.mount("subdir:/data/|"..fs.getRealPath(add.teaa), "/data-"..add.short_name, true)
			else fs.mount(base.."/data", "/data-"..add.short_name, true)
			end
		end
		if add.superload then 
			print(" * with superload")
			if add.teaa then fs.mount("subdir:/superload/|"..fs.getRealPath(add.teaa), "/mod/addons/"..add.short_name.."/superload", true)
			else fs.mount(base.."/superload", "/mod/addons/"..add.short_name.."/superload", true)
			end
			
			table.insert(_G.__addons_superload_order, add.short_name)
		end
		if add.overload then
			print(" * with overload")
			if add.teaa then fs.mount("subdir:/overload/|"..fs.getRealPath(add.teaa), "/", false)
			else fs.mount(base.."/overload", "/", false)
			end
		end
		if add.hooks then
			if add.teaa then fs.mount("subdir:/hooks/|"..fs.getRealPath(add.teaa), "/hooks/"..add.short_name, true)
			else fs.mount(base.."/hooks", "/hooks/"..add.short_name, true)
			end

			hooks_list[#hooks_list+1] = "/hooks/"..add.short_name

			print(" * with hooks (passed to post-processing))")
		end


		-- Compute addon md5
		local hash_valid, hash_err
		if config.settings.cheat then
			hash_valid, hash_err = false, "cheat mode skipping addon validation"
		else
			local fmd5 = self:addonMD5(add)
			hashlist[#hashlist+1] = {module=mod.short_name, addon=add.version_name, md5=fmd5}
--			hash_valid, hash_err = profile:checkAddonHash(mod.short_name, add.version_name, fmd5)
		end

--		if hash_err then hash_err = hash_err .. " [addon: "..add.short_name.."]" end
--		add.hash_valid, add.hash_err = hash_valid, hash_err

		mod.addons[add.short_name] = add
	end

	--We load hooks at the end of all superloads and overloads
	--If the code in /hooks/ is run earlier, it will load early versions of the dependent files and subsequent superloads and overloads will be disregarded.
	print("Post-processing hooks.")
	for i, dir in ipairs(hooks_list) do
		self:setCurrentHookDir(dir.."/")
		dofile(dir.."/load.lua")
		self:setCurrentHookDir(nil)
	end

	return hashlist
end

Marson
Uruivellas
Posts: 645
Joined: Thu Jan 16, 2014 4:56 am

Re: Lots of weird incompatibilities - proposed fix for 1.2.0

#2 Post by Marson »

Nice work. I know how frustrating dealing with that can be.

darkgod
Master of Eyal
Posts: 10750
Joined: Wed Jul 24, 2002 9:26 pm
Location: Angolwen
Contact:

Re: Lots of weird incompatibilities - proposed fix for 1.2.0

#3 Post by darkgod »

nice catch thanks
[tome] joylove: You can't just release an expansion like one would release a Kraken XD
--
[tome] phantomfrettchen: your ability not to tease anyone is simply stunning ;)

Post Reply