Extending LOVE2D
Why
If you are making a game that you want to publish, it is very likely that you will be required to implement an SDK that is not part of love’s standard library. Example of such libraries are Steamworks and EOS (Epic Online Services). While none of them is required in order to publish your game on their respective platforms, they both provide loads of useful functionality that you can take advantage of in your game. They both provide SDKs implemented in the C language, making it an easy task to wrap it with some glue code and make it a usable module in your Lua Love game code. Here is a step-by-step guide on how to do it.
Disclaimer
This has only been tested on a 64bit Windows 10 machine, I have no experience on how these steps work on a MacOS or Linux operating systems.
How
- Pull
megasource
building tool source code from here. - Pull
love2d
source code intomegasource
’slibs
directory from here using the following command:git clone https://github.com/love2d/love libs/love
. - Change the directory to
libs/love/src/modules
and create a new directory with the name of a module you want to create.
In this example I’ll be creating module named misc
, so replace it everywhere you see it with a module name of your choice. New directory, in my case, is src/modules/misc
.
- Open
CMakeLists.txt
file belonging tolove2d
library (lib/love/CMakeLists.txt
). - Somewhere between modules that are already defined, add a new variable and assign all the source files from your module.
set(LOVE_SRC_MODULE_MISC
src/modules/misc/Misc.cpp
src/modules/misc/Misc.h
src/modules/misc/wrap_Misc.cpp
src/modules/misc/wrap_Misc.h)
source_group("modules\\misc" FILES ${LOVE_SRC_MODULE_MISC}
Don’t worry about source files at the moment, we will create them later.
- Scroll down to the
LOVE_LIB_SRC
variable and insert your module’s sources there.
set(LOVE_LIB_SRC
${LOVE_SRC_COMMON}
# Modules
.
.
.
${LOVE_SRC_MODULE_MISC}
)
- Save and close
CMakeLists.txt
file. - Open
src/common/Module.h
header file and find an enum with the nameModuleType
. - Put
M_MISC
enum entry right beforeM_MAX_ENUM
. - Save and close
Module.h
file. - Open
src/common/config.h
header file. - Find defines for all the modules (should be right below
Autotools config.h
comment) and add a new define somewhere among others
# define LOVE_ENABLE_FONT
# define LOVE_ENABLE_GRAPHICS
.
.
.
# define LOVE_ENABLE_MISC
- Save and close the file.
- Open
src/modules/love/love.cpp
source file. - Find defines for enabling other modules and put a new entry for your module somewhere in between.
#ifdef LOVE_ENABLE_MISC
# include "misc/Misc.h"
#endif
Again, this file doesn’t exist yet, but this is fine, we will get to that later.
- Scroll down a bit within the same file where there are declarations of luaopen functions and add an entry for your own module.
#if defined(LOVE_ENABLE_MISC)
extern int luaopen_love_misc(lua_State*);
#endif
- Scroll even further to find a
modules
variable definition with an array full of all the modules and add an entry again.
#if defined(LOVE_ENABLE_MISC)
{ "love.misc", luaopen_love_misc },
#endif
- Finally, time to create source code for our module. Go to your module directory and create 4 files:
Misc.h
Misc.cpp
wrap_Misc.h
wrap_Misc.cpp
- Inside your
Misc.h
file, create a class that inherits fromModule
class and override these two functions:
ModuleType getModuleType() const
- this function must return module enum type we created in step 9.const char* getName() const
- this must return a name for your module, in my caselove.misc
.
- Declare functions you would like to implement as part of your module, in my case its
double customAdd(double a, double b)
.
Full code for the Misc
class header file:
#ifndef LOVE_MISC_H
#define LOVE_MISC_H
#include "common/Module.h"
namespace love
{
namespace misc
{
class Misc : public Module
{
public:
static love::Type type;
ModuleType getModuleType() const override { return M_MISC; }
const char* getName() const override
{
return "love.misc";
}
double customAdd(double a, double b);
};
}
}
#endif
- Inside your
Misc.cpp
file implement functions you declared for your module and define alove::Type type
static variable.
Full code for the Misc
class source file:
#include "Misc.h"
namespace love
{
namespace misc
{
love::Type Misc::type("misc", &Module::type);
double Misc::customAdd(double a, double b)
{
return a + b;
}
}
}
- Inside your
wrap_Misc.h
file declare an extern function that we used in a step 16 and 17.
#ifndef LOVE_WRAP_MISC_H
#define LOVE_WRAP_MISC_H
#include "common/runtime.h"
namespace love
{
namespace misc
{
extern "C" LOVE_EXPORT int luaopen_love_misc(lua_State *L);
}
}
#endif
Make sure you include common/runtime.h
header. Oterwise you will receive error C2065: 'lua_State': undeclared identifier
error during compilation.
23. Finally, inside wrap_Misc.cpp
file define Lua wrappers for all your functions implemented in Misc
class.
int w_customAdd(lua_State* L)
{
double a = lua_tonumber(L, 1);
double b = lua_tonumber(L, 2);
double c = instance()->customAdd(a, b);
lua_pushnumber(L, c);
return 1;
}
- Add all functions to an array together with their names.
// List of functions to wrap.
static const luaL_Reg functions[] =
{
{ "customAdd", w_customAdd },
{ 0, 0 }
};
- At the end, implement the extern function we decleared in a header file effectively registering our module.
extern "C" int luaopen_love_misc(lua_State* L)
{
Misc* instance = instance();
if(instance == nullptr)
{
luax_catchexcept(L, [&]() { instance = new love::misc::Misc(); });
}
else
{
instance()->retain();
}
WrappedModule w;
w.module = instance;
w.name = "misc";
w.type = &Misc::type;
w.functions = functions;
w.types = 0;
int n = luax_register_module(L, w);
return n;
}
Full code for wrap_Misc.cpp
source file:
#include "wrap_Misc.h"
#include "Misc.h"
#include "common/Module.h"
namespace love
{
namespace misc
{
#define instance() (Module::getInstance<Misc>(Module::M_MISC))
int w_customAdd(lua_State* L)
{
double a = lua_tonumber(L, 1);
double b = lua_tonumber(L, 2);
double c = instance()->customAdd(a, b);
lua_pushnumber(L, c);
return 1;
}
// List of functions to wrap.
static const luaL_Reg functions[] =
{
{ "customAdd", w_customAdd },
{ 0, 0 }
};
extern "C" int luaopen_love_misc(lua_State* L)
{
Misc* instance = instance();
if(instance == nullptr)
{
luax_catchexcept(L, [&]() { instance = new love::misc::Misc(); });
}
else
{
instance()->retain();
}
WrappedModule w;
w.module = instance;
w.name = "misc";
w.type = &Misc::type;
w.functions = functions;
w.types = 0;
int n = luax_register_module(L, w);
return n;
}
}
}
- Go to the root directory of the
megasource
project and call following commands:
cmake -G "Visual Studio 16 2019" -A Win32 -H. -Bbuild
cmake --build build --target love/love --config Release
- Your module is now compiled and accessible from Lua code executed using
love
executable. - In order to use it you must first require the module by calling
require "love.misc"
. - Use functions from your module like any other function:
result = love.misc.customAdd(10, 15)
.