Jump to content

» «

MoonLoader — Development

8 replies to this topic
BH Team
  • BH Team


  • Members
  • Joined: 08 Aug 2011
  • Russia


Posted 02 July 2017 - 07:28 PM Edited by BH Team, 6 days ago.

Disclaimer: The text may contain mistakes because English is not my native language, and I'd be very grateful if someone sent me a PM to help me correct some of these mistakes.


Development Guide

I highly recommend reading this topic before, if you don't familiar with MoonLoader yet.
Note: All links to the official MoonLoader wiki pages are translated via Google Translate.

First of all, you need to have basic programming skills and some Lua language knowledge. If you are already familiar with programming, but don't know Lua, you can start from "Learn Lua in 15 Minutes" and official documentation.
In the case if you have no experience in programming at all, you may want to start with any book or online course of your choice, I personally can't recommend anything in this case, but any material recommended by the Lua community should be good enough.
This article is better for understanding if you already know Lua, but anyway it won't hurt even if you don't.

Before we start talking directly about scripting, first we need to talk about standards. MoonLoader like any other good framework has its own standards that makes development more specific and efficient. So the standard is one of the things that you would like to know about development for MoonLoader.

Global Symbols
Global variables
All functions including opcodes
All events
If you are already familiar with SCM (CLEO), these lists can also be useful:
Functions based on opcodes that have difference from original opcodes
Removed opcodes that have alternatives
Missing opcodes that have no purpose in Lua

Directory Hierarchy
These directories are located in the game's root directory.
  • 'moonloader' - The main directory. It contains .lua and .luac scripts, the log file 'moonloader.log' and other subdirectories related to Lua scripts
  • 'moonloader\lib' - for libraries and modules
  • 'moonloader\config' - for configs of any kind, including configs in the form of Lua scripts
  • 'moonloader\resource' - for all the resources used by scripts (textures, models, etc)
  • 'moonloader\resource\txd' - .txd files loads from this directory using 'loadTextureDictionary' script function
MoonLoader distro includes several lua libraries to help making some common tasks easier and standardized.
  • 'moonloader' - things related to MoonLoader
  • 'vkeys' - virtual keyboard key identifiers with helpful functions
  • 'bitex' - the standard module 'bit' extension
  • 'vector3d' - 3D vector class
  • 'matrix3x3' - 3D rotation matrix class
  • 'sampfuncs' - SAMPFUNCS plugin constants
  • 'game.models' - common game model identifiers (weapons, vehicles, skins, etc)
  • 'game.globals' - global SCM variables list
  • 'game.keys' - game control key identifiers
  • 'game.weapons' - weapon names and identifiers
Script Directives
Directives are functions that specifies various script properties. Every directive value of any script can be obtained via LuaScript object.
Directives must be set once in the global script scope, usually at the very beginning of the script. Though, using them is not necessary, but desirable.
  • script_name(string name) - specifies script name
  • script_author(string author) - specifies script author
  • script_authors(string author, …) - specifies multiple script authors
  • script_description(string description) - specifies description
  • script_version(string version) - specifies script version string
  • script_version_number(int version) - specifies script version number (eg, release number)
  • script_url(string url) - specifies script URL (can be not only a direct download link)
  • script_dependencies(string name, …) - specifies script external dependencies (eg, 'SA:MP', 'CLEO')
  • script_moonloader(int version) - specifies minimum required version of MoonLoader. Doesn't cause the script to terminate if the version doesn't match, but prints an error into the log
  • script_properties(string property, …) - specifies script properties that affect its execution
Code Style
All MoonLoader's global variables and constants are named in the UPPERCASE, all built-in functions and events are named in the lowerCamelCase, every event name starts with prefix 'on', types (classes) named in CamelCase, and built-in modules in lowercase (lua_thread is an exception). So, if you don't know what naming style you wish to use, the snake_case style might be your choice.

print("Hello, World!")
That's how the "Hello world" would look like in regular Lua.
In MoonLoader global script area is not intended for main script code. Instead, all main code must be inside the 'main' function.
So "Hello world" should look like this:
-- will print out string "Hello, World!" and script will be terminated
function main()
  print("Hello, World!")
But... Why?
Global script area is intended for defining script information, loading libraries and configs. All code within the global area executes right after script loads, but the 'main' function calls when the game gets loaded.
Function 'main' can be suspended for certain time interval using function 'wait' with value in milliseconds. Delay in script won't pause the whole game, only the delayed script thread will be paused.
And you can use infinite loops inside 'main' to implement actively-working script, here is an example:
-- specify script information
script_name("Restore health")
-- load virtual key list
local vk = require "lib.vkeys"

function main()
  while true do -- infinite loop
    wait(0) -- delay is necessary in infinite loop, even zero
    if wasKeyPressed(vk.VK_1) and isPlayerPlaying(PLAYER_HANDLE) then -- if key '1' was pressed and player is available
      setCharHealth(PLAYER_PED, 100) -- set 100 health points to the player's character
The 'wait' delay may be used only inside the 'main' function (of course in nested calls too) and scripting threads. Delays can be used in any way, not only inside loops.
Script stops execution and completely unloads when exiting the 'main' function.
Next example shows how you can use sequence of delays and then stop script:
local vk = require "lib.vkeys"

function main()
  while true do
    if wasKeyPressed(vk.VK_1) and isPlayerPlaying(PLAYER_HANDLE) then
      print('Restoring health... Please, wait.')
      setCharHealth(PLAYER_PED, 100)
      print('Restore health: done.')
      print('Restoring armor...')
      setCharHealth(PLAYER_PED, 100)
      print('Restore armor: done.')
      return -- exit from 'main'. Script will be stopped and unloaded
Broadly speaking, events are functions that performed by themselves when some action happens. For example, when user starts a new game or new script gets loaded.
This example uses event 'onScriptMessage' to intercept 'print' messages from all scripts:
-- calls when any script prints out message with function 'print'
function onScriptMessage(msg, script)
  -- it is advisable to check that game is loaded
  if isPlayerPlaying(PLAYER_HANDLE) then
    printStringNow(script.name .. ": " .. msg, 2000) -- display message
Script won't stop automatically if it has some events and doesn't have the 'main' function. But if you have both in your script and your function 'main' doesn't have infinite loop, you can use 'wait(-1)' to prevent script from automatical termination.
Also, delays cannot be used inside events, but you can use scripting threads to get over it.
In this way you can use delays inside events and prevent script termination using 'wait(-1)':
function onScriptMessage(msg, script)
  -- run a new scripting thread, passing message and script name
  lua_thread.create(process_script_message, msg, script.name) -- function 'process_script_message' will be called instantly in this line
  -- and will run until delay, then execution will be passed back to the event
  -- but the thread will continue to get processed and will continue exististence until it is exited

function process_script_message(msg, name)
  printStringNow(name, 1000)
  printStringNow(msg, 'Message:~n~' .. msg)
  -- function exits out = the thread is destroyed

function main()
  if getMoonloaderVersion() < 21 then return end
  -- if script is no longer performs any actions, but needs to handle events
  -- you can get over it in that way
  wait(-1) -- infinite delay will prevent script from automatically unloading
You can learn more about scripting threads on wiki.

Theoretically, this information is enough to start developing Lua-scripts for MoonLoader. Things described here is just a basic guide to get started, MoonLoader has a lot more concepts which you have to learn yourself.
I highly recommend install scripts "ML AutoReload" and "ML ReloadAll", they are very helpful in development, you might also want to install "SF Integration" and "ScriptManager" if you have SAMPFUNCS installed, they are very useful too.

Lua scripts don't require compilation and it is better to keep scripts open source, but if you need, you can compile them using LuaJIT interpreter.
Download this archive and extract it anywhere, then just drag and drop your .lua script file onto compile.bat, the compiled script with extension .luac will appear next to original file. MoonLoader loads the .luac files in the same way as normal .lua.

Atom was selected as an official IDE for MoonLoader, Notepad++ is also supported officially, but has less functionality. The best option is to choose one of these source code editors for effective development, but you can use any text editor that you like.

Extension for Atom includes Lua syntax highlighting, smart autocompletion, real-time error checking. There is also fuzzy search for functions and opcodes - activated with hotkey Ctrl+Alt+A, to find function by opcode add prefix '@' before search query.
Note: if you already have package 'language-lua' or 'linter-lua' installed in your Atom, disable or remove it to avoid conflicts.
1. Install and run Atom
2. Go to the settings window (menu File -> Settings or hotkey Ctr + ,)
3. Go to the "Install" tab
4. Enter "moonloader" in the search box and install first module from the results list
Note: if an error occurred during autoinstallation of "autocomplete-lua" just install it yourself by the same method.
1U5YKAtt.png ujghizst.png Af0Hyxyt.png VqQTP4Ut.png 6u0qCiMt.png

Extension for Notepad++ has less functionality - it's includes basic autocompletion suggestions, additional keywords highlighting and tooltips for functions.
1. Install Notepad++ and run it at least once
2. Download the MoonLoader installer, run and install it with selected "Notepad++ extension" checkbox
3. Open Notepad++ and select the Lua language (menu Language -> L -> Lua)
lt22Uq1t.png iYsu2n1t.png

Main Topic
MoonLoader Wiki Pages
Download Script Examples
  • Wesser, Blackbird88, Apu889 and 9 others like this

  • Blue

    The sun is the same in a relative way but you're older

  • Facade Corporation
  • Joined: 09 Apr 2014
  • Portugal
  • Best Total Overhaul 2016 [Shine o' Vice] [Contribution]
    Best Workshop 2016 [Runner-up]


Posted 02 July 2017 - 08:11 PM

You sir are a God.
  • deltaCJ likes this

  • Delatom

    I enjoy Gaming & Nature, seeking for some insperation.

  • Members
  • Joined: 2 weeks ago
  • None


Posted A week ago


  • Crspy


  • Members
  • Joined: 14 May 2015
  • None


Posted 6 days ago

there's a little problem with atom , everything was installed just fine except this :




i am running atom as administrator anyway.

BH Team
  • BH Team


  • Members
  • Joined: 08 Aug 2011
  • Russia


Posted 5 days ago

Yes, this problem has been reported but devs haven't fixed it yet. Just install autocomplete-lua separately via Settings -> Install.

  • Crspy likes this

  • Crspy


  • Members
  • Joined: 14 May 2015
  • None


Posted 4 days ago Edited by Crspy, 4 days ago.

It worked thanks. i like how the syntax is almost similar to C++  without the complexity.


I have a question though , is declaring my own functions and using them in the main function when i want is possible ?  something like this for example :  

function myrequest( models, address)
while not hasModelLoaded(models) do
writeMemory(address, 4, models, false)

function main()
local random
local model
random = 0
while true do
	if isPlayerPlaying(PLAYER_HANDLE) then
		if isCharInZone(PLAYER_PED, "SF") then
			printHelpString("player is in SF")
			random = math.random(0,5)
			if random == 0 then model = 281
			elseif random == 1 then model = 340
			elseif random == 2 then model = 668
			elseif random == 3 then model = 699
			else model = 86

PS : indentation gets messy when the code is copy-pasted. :3

BH Team
  • BH Team


  • Members
  • Joined: 08 Aug 2011
  • Russia


Posted 4 days ago

Yes, you can do everything with functions that you want. There is no limitations to any Lua possibilities.

  • Crspy likes this

  • BigHomie


  • Members
  • Joined: 03 May 2016
  • Germany


Posted 3 days ago

As I understand, thread saving isn't available, but would there be some way to save the script's status?

BH Team
  • BH Team


  • Members
  • Joined: 08 Aug 2011
  • Russia


Posted 3 days ago Edited by BH Team, 3 days ago.

There is embedded savegame system built on events, if you need it for saving mission progress or something like that, that should be enough. For configs you can use built-in module 'inicfg', for other purposes there are functions encodeJson and decodeJson, and also serialization libraries by community.

  • Crspy likes this

1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users