-
Notifications
You must be signed in to change notification settings - Fork 0
Creating a Lua gamemode or filterscript
YALP doesn't provide any new system to load server scripts in addition to gamemodes and filterscripts. Instead, you can create a Lua instance in any Pawn script, and let it replace it in terms of all functionality. The loader script is very simple:
#include <a_samp>
#include <YALP>
public OnFilterScriptInit()
{
new Lua:l = lua_newstate(lua);
if(lua_loadfile(l, "script.lua") || !lua_bind(l))
{
lua_stackdump(l);
lua_close(l);
}
}
lua_newstate
creates a new Lua instance. lua_loadfile
loads a script (from the "scriptfiles" directory) and pushes it on the Lua stack as a function. lua_bind
consumes this function and "rewires" all callbacks and other operations to the Lua instance. From this point on, the Lua instance completely replaces the filterscript (or gamemode).
In case of an error, lua_stackdump
outputs the contents of the Lua stack, and lua_close
deletes the instance. If everything succeeds, the Lua instance will be destroyed when the calling Pawn script is unloaded.
Since there is no special entry/main function in the script that is called, the script itself is its entry function. In it, you should perform initialization of the script, load all modules, and register your public functions. The core module in YALP is the interop
package, which performs the communication between Lua and SA-MP.
local interop = require "interop"
local public = interop.public
local SendClientMessage, SetPlayerHealth
import(interop.native) -- assigns SendClientMessage and SetPlayerHealth from interop.native.SendClientMessage and interop.native.SetPlayerHealth
local COLOR_WHITE = 0xFFFFFFFF
local function tofloat(num) -- when calling native functions with a float parameter, a float value must be ensured
return 0.0 + num
end
function public.OnPlayerConnect(playerid) -- adding a function to the "public" table automatically registers the callback (based on the name)
SendClientMessage(playerid, COLOR_WHITE, "Hello from Lua!") -- calling a native function requires almost no modifications
end
local commands = {}
function commands.hp(playerid, params)
local hp = tonumber(params)
if not hp then
return SendClientMessage(playerid, COLOR_WHITE, "Usage: /hp [value]")
end
SetPlayerHealth(playerid, tofloat(hp))
SendClientMessage(playerid, COLOR_WHITE, "Your health was set to "..hp.."!")
return true
end
function public.OnPlayerCommandText(playerid, cmdtext)
playerid = interop.asinteger(playerid) -- YALP cannot guess the type of the arguments, so they must be explicitly converted
cmdtext = interop.asstring(cmdtext)
local ret
cmdtext:gsub("^/([^ ]+) ?(.*)$", function(cmd, params)
local handler = commands[string.lower(cmd)]
if handler then
ret = handler(playerid, params)
end
end)
return ret
end
print("Lua script initialized!")
lua_newstate
automatically opens standard Lua packages for use within scripts and new YALP modules as well. For safety, io
, os
, and debug
packages aren't loaded by default, but setting the parameters of lua_newstate
can allow them. It is also possible to limit the memory usage of the Lua instance this way.
YALP packages use the standard Lua package system. They are not exported to the global table, but can be loaded by using require
, like in the example script above. This is even necessary for the interop
module, if you want to use it together with lua_bind
, since loading it before the call to the native function creates a fake filterscript to interface with the server.
Most of the Lua C API is exported for use from Pawn. If you don't want to write SA-MP scripts in Lua or want to have greater control over the Lua instance, you can operate with the Lua stack, register custom functions, or run any Lua code from Pawn. However, using the Lua API is discouraged since it is more error-prone, takes longer to write and debug, and isn't faster than Lua code. You should only resolve to it when absolutely necessary.