Writing Modules

Writing a C/C++ Binary Module

The most simple binary module would be one that is wholly independent of BWAPI-Lua and BWAPI, as it would only need to link to the Lua library. If using the CMake script below, your Lua link library should be named lua51.lib and placed in dependecies/lua/5.1/lib within the module’s project folder; the necessary Lua header files should be placed in dependecies/lua/5.1/include.

CMakeLists.txt
cmake_minimum_required (VERSION 2.6)
project (HelloWorldModule)

# Lua
set(LUA_5_1_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/dependencies/lua/5.1/include)
set(LUA_5_1_LIB_DIR ${PROJECT_SOURCE_DIR}/dependencies/lua/5.1/lib)
find_library(LUA_LIB lua51 ${LUA_5_1_LIB_DIR} NO_DEFAULT_PATH)

# Our Module
add_library( helloworld SHARED helloworld.cpp helloworld.def )
set_target_properties( helloworld PROPERTIES LINK_FLAGS "${CMAKE_LINK_DEF_FILE_FLAG}${PROJECT_SOURCE_DIR}/helloworld.def /SUBSYSTEM:WINDOWS" )
target_link_libraries ( helloworld ${LUA_LIB} )
helloworld.cpp
#define LUA_LIB
#include "lauxlib.h"

LUALIB_API int luaopen_helloworld(lua_State *L)
{
   // Loading this module will return the string "Hello World!"
   lua_pushstring(L, "Hello World!");
   return 1;
}
helloworld.def
EXPORTS
luaopen_helloworld

Once built and placed next to the BWAPI-Lua dll (in the same directory), the following Lua code would work:

init.lua
local helloWorld = require('helloworld')

assert(helloWorld == "Hello World!")
print(helloWorld)

Interacting with BWAPI

To write a binary module that interacts with BWAPI, we will need to link against BWAPILIB and make sure its headers are in our include path. If using the CMake script below:

  • Your BWAPILIB release library should be named BWAPILIB.lib and placed in dependencies/bwapi/lib within the module’s project folder
  • Your BWAPILIB debug library should be named BWAPILIBd.lib and placed in dependencies/bwapi/lib
  • Your BWAPI header files should be placed in dependencies/bwapi/include
  • Lua library/headers should be in the same places as in the previous module

Important

When linking BWAPILIB, your module will contain a separate instance of BWAPI::Broodwar and a NULL BWAPI::BroodwarPtr. To use BWAPI::Broodwar in your extension, BWAPI::BroodwarPtr will need to be set to the value of BWAPI.Broodwar from the Lua VM, which is a BWAPI::Game* pushed to Lua as userdata by the Sol2 binding library. The code below shows how to retrieve that value using the Lua C API (see also the Sol2 docs on this subject).

CMakeLists.txt
cmake_minimum_required (VERSION 2.6)
project (HelloWorldModule)

# BWAPILIB
set(BWAPI_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/dependencies/bwapi/include)
set(BWAPI_LIB_DIR ${PROJECT_SOURCE_DIR}/dependencies/bwapi/lib)
find_library(BWAPILIB_RELEASE_LIB BWAPILIB ${BWAPI_LIB_DIR} NO_DEFAULT_PATH)
find_library(BWAPILIB_DEBUG_LIB BWAPILIBd ${BWAPI_LIB_DIR} NO_DEFAULT_PATH)

# Lua
set(LUA_5_1_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/dependencies/lua/5.1/include)
set(LUA_5_1_LIB_DIR ${PROJECT_SOURCE_DIR}/dependencies/lua/5.1/lib)
find_library(LUA_LIB lua51 ${LUA_5_1_LIB_DIR} NO_DEFAULT_PATH)

# Our Module
add_library( helloworld SHARED helloworld.cpp helloworld.def )
set_target_properties( helloworld PROPERTIES LINK_FLAGS "${CMAKE_LINK_DEF_FILE_FLAG}${PROJECT_SOURCE_DIR}/helloworld.def /SUBSYSTEM:WINDOWS" )
target_link_libraries ( helloworld ${LUA_LIB} optimized ${BWAPILIB_RELEASE_LIB} debug ${BWAPILIB_DEBUG_LIB} )
#define LUA_LIB
#include "lauxlib.h"
#include "BWAPI.h"

static int helloworld_print(lua_State *L)
{
	const char* msg = luaL_checkstring(L, 1);
	BWAPI::Broodwar << msg << std::endl;
	return 0;
}

LUALIB_API int luaopen_helloworld(lua_State *L)
{
	// If BWAPI::Broodwar is used, we need to set it to the same pointer that BWAPI-Lua uses,
	// so we need to grab that from BWAPI.Broodwar
	lua_getglobal(L, "BWAPI");
	lua_getfield(L, -1, "Broodwar");
	void* data = lua_touserdata(L, -1);
	BWAPI::Game* bw = *static_cast<BWAPI::Game**>(data);
	BWAPI::BroodwarPtr = bw;
	lua_settop(L, 0); // clear the stack

	// Create a table with a 'print' function that prints to the BW chat
	lua_newtable(L);
	int tableIndex = lua_gettop(L);
	lua_pushcfunction(L, &helloworld_print);
	lua_setfield(L, tableIndex, "print");

	// Return the table since it's still on the stack
	return 1;
}
EXPORTS
luaopen_helloworld

Once built and placed next to the BWAPI-Lua dll (in the same directory), the following Lua code would work:

init.lua
local helloWorld = require('helloworld')

assert(type(helloWorld) == "table")
assert(type(helloWorld.print) == "function")
helloWorld.print("Hello World!")