Mod Compatibility Tool [LÖVE 0.8.0 - 0.9.x at the same time]

Mods, guides how to use and install mods go right in here.
Post Reply
User avatar
HugoBDesigner
Posts: 2188
Joined: 19 Sep 2012, 02:23
Contact:

Post » 28 May 2014, 02:25

I've seen people asking a lot for porting Mari0 mods to LÖVE 0.9.0. I made it myself with Mari0 +Portal, but since I'm: 1- VERY used to LÖVE 0.8.0 and 2- Not patient enough to rename every function, I made the following code to help you guys play your mods in LÖVE 0.8.0, LÖVE 0.9.0 AND 0.9.1! At the same time!

Many thanks to Automatik for the music thread code for LÖVE 0.9.0. It helped A LOT making it compatible with both LÖVE versions!


How to update a mod: If you're a modder, then it should be easy for you to understand. Anyway, open the ".love" file of the mod with WinRAR or similar. Find the following files and do what I ask in the following instructions. ".lua" files can be opened with any text editor:

Here's the file "musicloader.lua". Replace all of it's code and put this:

Code: Select all

music = {
	thread = false,
	toload = {},
	loaded = {},
	list = {},
	list_fast = {},
	pitch = 1,
}

if love._version == "0.8.0" then
	music.thread = love.thread.newThread("musicthread", "musicloader_thread.lua")
else
	music.thread = love.thread.newThread("musicloader_thread.lua")
end

music.stringlist = table.concat(music.toload, ";")

function music:init()
	self.thread:start()
end

function music:load(musicfile) -- can take a single file string or an array of file strings
	if type(musicfile) == "table" then
		for i,v in ipairs(musicfile) do
			self:preload(v)
		end
	else
		self:preload(musicfile)
	end
	self.stringlist = table.concat(self.toload, ";")
	if love._version == "0.8.0" then
		self.thread:set("musiclist", self.stringlist)
	else
		love.thread.getChannel("musiclist"):push(self.stringlist)
	end
end

function music:preload(musicfile)
	if self.loaded[musicfile] == nil then
		self.loaded[musicfile] = false
		table.insert(self.toload, musicfile)
	end
end

function music:play(name)
	if name and soundenabled then
		if self.loaded[name] == false then
			local source
			if love._version == "0.8.0" then
				source = self.thread:demand(name)
			else
				source = love.thread.getChannel(name):demand()
			end
			self:onLoad(name, source)
		end
		if self.loaded[name] then
			playsound(self.loaded[name])
		end
	end
end

function music:playIndex(index, isfast)
	local name = isfast and self.list_fast[index] or self.list[index]
	self:play(name)
end

function music:stop(name)
	if name and self.loaded[name] then
		love.audio.stop(self.loaded[name])
	end
end

function music:stopIndex(index, isfast)
	local name = isfast and self.list_fast[index] or self.list[index]
	self:stop(name)
end

function music:update()
	for i,v in ipairs(self.toload) do
		if love._version == "0.8.0" then
			local source = self.thread:get(v)
			if source then
				self:onLoad(v, source)
			end
		else
			if love.thread.getChannel(v):getCount()>0 then
				self:onLoad(v, love.thread.getChannel(v):pop())
			end
		end
	end
	for name, source in pairs(self.loaded) do
		if source ~= false then
			source:setPitch(self.pitch)
		end
	end
	local err
	if love._version == "0.8.0" then
		err = self.thread:get("error")
	else
		err = love.thread.getChannel("error"):pop()
	end
	if err then print(err) end
end

function music:onLoad(name, source)
	self.loaded[name] = source
	source:setLooping(true)
	source:setPitch(self.pitch)
end


music:load{
	"overworld",
	"overworld-fast",
	"underground",
	"underground-fast",
	"castle",
	"castle-fast",
	"underwater",
	"underwater-fast",
	"starmusic",
	"starmusic-fast",
	"princessmusic",
}

-- the original/default music needs to be put in the correct lists
for i,v in ipairs(music.toload) do
	if v:match("fast") then
		table.insert(music.list_fast, v)
	elseif not v:match("princessmusic") then
		table.insert(music.list, v)
	end
end

music:init()

Now find the file "musicloader_thread.lua" and replace it's code by this:

Code: Select all

local this = love.thread.getThread()

require("love.filesystem")
require("love.sound")
require("love.audio")
require("love.timer")

love.filesystem.setIdentity("mari0")

local musicpath = "sounds/%s.ogg"

local musiclist = {}
local musictoload = {} -- waiting to be loaded into memory

local function getmusiclist()
	-- the music string should have names separated by the ";" character
	-- music will be loaded in in the same order as they appear in the string
	local musicliststr
	if love._version == "0.8.0" then
		musicliststr = this:get("musiclist")
	else
		musicliststr = love.thread.getChannel("musiclist"):pop()
	end
	if musicliststr then
		for musicname in musicliststr:gmatch("[^;]+") do
			if not musiclist[musicname] then
				musiclist[musicname] = true
				table.insert(musictoload, musicname)
			end
		end
	end
end

local function getfilename(name)
	local filename = name:match("%.[mo][pg][3g]$") and name or musicpath:format(name) -- mp3 or ogg
	if love.filesystem.exists(filename) and love.filesystem.isFile(filename) then
		return filename
	else
		print(string.format("thread can't load \"%s\": not a file!", filename))
	end
end

local function loadmusic()
	if #musictoload > 0 then
		local name = table.remove(musictoload, 1)
		local filename = getfilename(name)
		if filename then
			local source = love.audio.newSource(love.sound.newDecoder(filename, 512 * 1024), "static")
			--print("thread loaded music", name)
			if love._version == "0.8.0" then
				this:set(name, source)
			else
				love.thread.getChannel(name):push(source)
			end
		end
	end
end

while true do
	getmusiclist()
	loadmusic()
	love.timer.sleep(1/60)
end

Go to the file "main.lua" and put this on the VERY TOP of the text. DO NOT CHANGE THE ORIGINAL CODE:

Code: Select all

love.graphics.drawq = love.graphics.drawq or love.graphics.draw
love.filesystem.enumerate = love.filesystem.enumerate or love.filesystem.getDirectoryItems
love.filesystem.mkdir = love.filesystem.mkdir or love.filesystem.createDirectory
love.graphics.setMode = love.graphics.setMode or love.window.setMode
lgSetMode = love.graphics.setMode
love.graphics.setCaption = love.graphics.setCaption or love.window.setTitle
love.graphics.defaultSetLine = love.graphics.setLine
love.graphics.setDefaultImageFilter = love.graphics.setDefaultImageFilter or love.graphics.setDefaultFilter
love.graphics.setIcon = love.graphics.setIcon or love.window.setIcon
lgSetIcon = love.graphics.setIcon
love.graphics.setCaption = love.graphics.setCaption or love.window.setTitle
love.mouse.setGrab = love.mouse.setGrab or love.mouse.setGrabbed
SpriteBatch.addq = SpriteBatch.addq or SpriteBatch.add

function love.graphics.setMode(w, h, f, v, fsaa)
	local fullscreen = f or false
	local v = v
	local fsaa = fsaa
	if love._version == "0.8.0" then
		lgSetMode(w, h, f, v, fsaa)
	else
		lgSetMode(w, h, {fullscreen = f, vsync = v, fsaa = fsaa})
	end
end

function love.graphics.setIcon(i)
	local i = i
	if tostring(i) == "ImageData" then
		i = love.graphics.newImage(i)
	end
	if love._version == "0.8.0" then
		lgSetIcon(i)
	else
		local n = 1
		while love.filesystem.exists("tempFile" .. n .. ".png") do --you never know when someone has this file there
			n = n+1
		end
		lgSetIcon(i:getData())
	end
end

Replace the VERY FIRST LINE of init.lua (inside the "shaders" folder) by this:

Code: Select all

local supported = love.graphics.isSupported and love.graphics.isSupported("canvas") and ((love._version == "0.8.0" and love.graphics.isSupported("pixeleffect")) or true)

At last, if you don't want the annoying version message showing up when you load your mod, replace the file "conf.lua" with this:

Code: Select all

function love.conf(t)
	t.author = "Maurice"
	t.identity = "mari0"
	t.console = false
	--t.screen = false
	t.modules.physics = false
	t.version = "0.8.0"
	if love._version == "0.9.0" or love._version == "0.9.1" then
		t.version = love._version
	end
end
VERY IMPORTANT: Some modders, just like me, may have changed some things in this file. If you are unsure, replace only the line t.version = "0.8.0" with this:

Code: Select all

	t.version = "0.8.0"
	if love._version == "0.9.0" or love._version == "0.9.1" then
		t.version = love._version
	end

I hope I helped you guys. I don't need credits for anything because it wasn't a big deal and I am already using this technique in my Mari0 +Portal mod. Bye!
Last edited by HugoBDesigner on 03 Jun 2014, 09:38, edited 5 times in total.

User avatar
I LÖVE LUA
Posts: 222
Joined: 12 Aug 2013, 13:19

Post » 30 May 2014, 10:03

Thanks Hugo (and Automatik by the way). I was wondering how to port my mod into 0.9.x.

User avatar
HugoBDesigner
Posts: 2188
Joined: 19 Sep 2012, 02:23
Contact:

Post » 31 May 2014, 07:51

Updated the main post with some small things I had to change to make it work on LÖVE 0.8.0, 0.9.0 and 0.9.1. Check that out if anyone's using this code!

User avatar
Automatik
Posts: 1073
Joined: 20 Jul 2012, 17:54
Contact:

Post » 31 May 2014, 13:09

Nice!
Four things:
- You made a typo, it's "main.lua", not "mail.lua".
- Why do you have to put l.g.setMode & l.g.setIcon at the bottom of main.lua(and not at the top)?
- In love.graphics.setMode, why do you put "local v = v or nil", since it doesn't do anything? Same thing for "local i = i" in love.graphics.setIcon.
-What's the point of this (in love.graphics.setIcon):

Code: Select all

      while love.filesystem.exists("tempFile" .. n .. ".png") do --you never know when someone has this file there
         n = n+1
      end

User avatar
HugoBDesigner
Posts: 2188
Joined: 19 Sep 2012, 02:23
Contact:

Post » 31 May 2014, 21:23

Automatik wrote:Nice!
Four things:
- You made a typo, it's "main.lua", not "mail.lua".
Thanks, fixed!

- Why do you have to put l.g.setMode & l.g.setIcon at the bottom of main.lua(and not at the top)?
It's not "necessary", but just easier...

- In love.graphics.setMode, why do you put "local v = v or nil", since it doesn't do anything? Same thing for "local i = i" in love.graphics.setIcon.
I was afraid of getting an error, like "attempted to index field "v" (an nil value)" or something like this.

-What's the point of this (in love.graphics.setIcon):

Code: Select all

      while love.filesystem.exists("tempFile" .. n .. ".png") do --you never know when someone has this file there
         n = n+1
      end
I didn't want to mess up with anyone else's code, so I made a temporary file. Since I don't know whether or not someone else's mod uses a "tempFile.png" (they probably don't), I made this.
By the way, guys, I still found some problems running vanilla Mari0/Mari0 mod with this compatibility code. I'll keep trying to fix it, but any help is appreciated!

User avatar
Automatik
Posts: 1073
Joined: 20 Jul 2012, 17:54
Contact:

Post » 31 May 2014, 21:46

HugoBDesigner wrote:It's not "necessary", but just easier...
-Well actually, it's easier to do just one copy-paste instead of two.
I was afraid of getting an error, like "attempted to index field "v" (an nil value)" or something like this.
-Yeah, but here if v was originally nil, well it would get replaced with nil so it's useless.
You can give a function a variable that doesn't exist(or nothing), and it will think you gave him nil.

I didn't want to mess up with anyone else's code, so I made a temporary file. Since I don't know whether or not someone else's mod uses a "tempFile.png" (they probably don't), I made this.
-I find absolutely no trace of creating a "tempFile.png"(or "tempNFile.png") file in your code.

User avatar
HugoBDesigner
Posts: 2188
Joined: 19 Sep 2012, 02:23
Contact:

Post » 31 May 2014, 21:58

Automatik wrote:It's not "necessary", but just easier...
-Well actually, it's easier to do just one copy-paste instead of two.
True, true... Fixed it :D

I was afraid of getting an error, like "attempted to index field "v" (an nil value)" or something like this.
-Yeah, but here if v was originally nil, well it would get replaced with nil so it's useless.
You can give a function a variable that doesn't exist(or nothing), and it will think you gave him nil.

Oh, didn't knew it. Thanks, fixed!

I didn't want to mess up with anyone else's code, so I made a temporary file. Since I don't know whether or not someone else's mod uses a "tempFile.png" (they probably don't), I made this.
-I find absolutely no trace of creating a "tempFile.png"(or "tempNFile.png") file in your code.
Not in my code, but in someone else's mod code. Just for safety. In my mod, for example, although I wasn't creating any "tempFileN.png", I was creating "temp.lua"...

User avatar
I LÖVE LUA
Posts: 222
Joined: 12 Aug 2013, 13:19

Post » 03 Jun 2014, 09:04

In order to not make the game crash when somebody attempts to "lock the mouse with f12", you should add this line :

Code: Select all

love.mouse.setGrab = love.mouse.setGrab or love.mouse.setGrabbed
because setGrab has been renamed in 0.9.x.

User avatar
HugoBDesigner
Posts: 2188
Joined: 19 Sep 2012, 02:23
Contact:

Post » 03 Jun 2014, 09:40

Yeah, I completely forgot that. Thank you, I LÖVE LUA! Updated the main post.

Also, if someone could help me, I'm having problems with music playing in 0.9.0

One last thing: I added this, that I also forgot, but I'm not sure if it'll work. I need to have the music fixed before:

Code: Select all

SpriteBatch.addq = SpriteBatch.addq or SpriteBatch.add

Post Reply