0% found this document useful (0 votes)
335 views102 pages

MTA SA Lua Scripting Course

The document provides a comprehensive guide to mastering Lua for Multi Theft Auto: San Andreas (MTA:SA), covering its functionality as a multiplayer modification and the advantages of using Lua as a scripting language. It outlines the necessary setup for MTA:SA development, including installation requirements, server configuration, and recommended text editors. Additionally, it introduces Lua basics such as variables, data types, operators, and control structures essential for scripting in MTA:SA.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
335 views102 pages

MTA SA Lua Scripting Course

The document provides a comprehensive guide to mastering Lua for Multi Theft Auto: San Andreas (MTA:SA), covering its functionality as a multiplayer modification and the advantages of using Lua as a scripting language. It outlines the necessary setup for MTA:SA development, including installation requirements, server configuration, and recommended text editors. Additionally, it introduces Lua basics such as variables, data types, operators, and control structures essential for scripting in MTA:SA.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 102

Mastering Lua for MTA:SA: From Beginner to Advanced

📘 Introduction

What is MTA:SA?

Multi Theft Auto: San Andreas (MTA:SA) is a sophisticated multiplayer modification for
Grand Theft Auto: San Andreas (GTA:SA) on Microsoft Windows.1 Unlike traditional
modifications that alter original game files, MTA:SA operates as a derivative game
engine, employing code injection and hooking techniques to extend the original
game's functionality without direct modification.1 This approach significantly
enhances stability and speed while improving file management.

MTA:SA effectively transforms GTA:SA into a versatile platform for online multiplayer
experiences. It integrates core functionalities such as networking and GUI rendering,
and critically, exposes a substantial portion of the original game's engine capabilities
through a Lua scripting interface.1 This allows developers to create and host a vast
array of custom game modes and maps, ranging from free-roam experiences to
competitive deathmatches, role-playing games, and unique minigames like poker
matches. The entire gameplay functionality within MTA:SA is driven by this scripting
language, providing users with the tools to customize nearly every aspect of the
game. The platform supports a modular system where custom content is bundled into
"resources" – packages containing scripts, custom assets (images, 3D models), and
metadata, which are hosted server-side and selectively downloaded by connected
clients. This design enables a thriving community, with thousands of active servers
and hundreds of thousands of monthly players.
Why use Lua for MTA:SA scripting?

Lua is the chosen scripting language for MTA:SA due to its inherent strengths as an
embedded language.1 It excels at "gluing together" code written in other languages,
such as C and C++, by exposing their functions and objects for manipulation.3 This
characteristic makes Lua an ideal fit for game engines like MTA:SA, where a C++ core
handles high-performance tasks while Lua provides a flexible layer for game logic,
configuration, and dynamic content creation.3

The design of Lua as a lightweight, embeddable scripting language with no


dependencies means it can operate efficiently even in resource-constrained
environments.4 Its small footprint (around 200 KB–400 KB for the interpreter and
code) further reinforces its suitability for integration into larger applications.3 For
developers, Lua offers a "friendly look" with a simpler syntax compared to languages
with extensive braces and parentheses, making it accessible for beginners and game
logic designers.3 This simplicity, combined with its flexibility, allows for rapid
development and iteration of game mechanics without requiring recompilation of the
core engine.3 The language's dynamic typing also offers flexibility, as variables can
change their types during execution. This adaptability is particularly beneficial in a
dynamic game environment where rapid prototyping and iteration are common.3

Basic requirements (server setup, text editor, etc.)

To begin developing for MTA:SA, several foundational components are required:


1. GTA: San Andreas Installation: MTA:SA functions as an extension of the
original Grand Theft Auto: San Andreas game. Therefore, a legitimate installation
of GTA:SA is a mandatory prerequisite.2 The typical installation path is
C:\Program Files (x86)\Rockstar Games\GTA San Andreas.
2. MTA:SA Client and Server: The MTA:SA installer, downloadable from the official
website, includes both the game client (for playing on servers) and the dedicated
server components (for hosting your own server).2 During installation, it is crucial
to select a separate directory for MTA:SA, distinct from the GTA:SA installation
folder, and to avoid cloud-synced directories like OneDrive.2
3. Server Setup:
○ Installation: The MTA:SA server installation on Windows is straightforward,
typically involving downloading and running an installer.7
○ Configuration: The server can be configured via its console, in-game
commands, or a web interface.7 Key configuration files include
mods/deathmatch/mtaserver.conf (for ports and general settings) and
mods/deathmatch/acl.xml (for access control and administrator accounts).7
Port forwarding (UDP for main server/ASE, TCP for HTTP) is often necessary
for external access.7
○ Resource Management: Custom content, organized as "resources" (ZIP or
folder format), must be placed in the <SERVER>\mods\deathmatch\resources
directory.7 After adding or updating resources, the
refresh command in the server console re-scans the folder, and start
[resourceName] loads the resource.7
4. Text Editor: While any plain text editor can be used to write Lua scripts, a
specialized code editor or Integrated Development Environment (IDE) is highly
recommended.4 Editors like Visual Studio Code (VS Code) with Lua extensions
(e.g., MTA:SA Lua, MTA DevKit) offer features such as syntax highlighting, code
completion, and debugging capabilities, which significantly streamline the
development process.9 Other popular choices include Sublime Text and
ZeroBrane Studio.10
5. Internet Connection: An active internet connection is necessary for
downloading MTA:SA, connecting to online servers, and accessing
documentation.2

📗 Module 1: Lua Basics

1. What is Lua?

Lua is a powerful, lightweight, and fast programming language designed primarily for
embedding within applications.4 It is not a standalone language meant for building
entire operating systems or complex desktop applications from scratch, but rather
excels at providing a flexible scripting layer for larger systems.3 Its primary use cases
include game development (as seen in MTA:SA, Roblox, Factorio, World of Warcraft),
web applications, image processing, and even embedded systems with limited
resources.3

The language is known for its simplicity, minimalist design, and ease of learning.3
Lua's core philosophy emphasizes being small and having no external dependencies,
making it highly portable and suitable for a wide range of integration scenarios.3 This
design choice allows Lua to be seamlessly integrated into C and C++ applications,
acting as a "glue" language that enables dynamic configuration and interaction with
the underlying native code without requiring recompilation.3 For game developers,
this means rapid prototyping and modification of game logic, AI, and user interfaces.3

2. Variables and Data Types

In Lua, variables are dynamically typed, meaning a variable's type is determined by


the value assigned to it, not by an explicit declaration.5 This provides significant
flexibility, allowing a single variable to hold different types of values throughout a
program's execution.

Lua features eight fundamental data types 13:


● nil: Represents the absence of a value or a non-existent variable. Uninitialized
global variables default to nil. Assigning nil to a variable effectively removes its
value.
Lua
local myVar -- myVar is nil by default
print(myVar) -- Output: nil

myVar = 10
print(myVar) -- Output: 10

myVar = nil
print(myVar) -- Output: nil

● boolean: Represents logical values, either true or false.5 These are crucial for
conditional logic and control flow.14
Lua
local isActive = true
local isFinished = false
print(isActive) -- Output: true
print(isFinished) -- Output: false
print(not isActive) -- Output: false

● number: Represents numerical values.5 Since Lua 5.3, it internally supports both
floating-point (fractional) and integer (non-fractional) representations, but both
are considered the
number type.5
Lua
local integerNum = 10
local floatNum = 3.14
print(integerNum + floatNum) -- Output: 13.14
print(type(integerNum)) -- Output: number

● string: Represents sequences of characters, used for text data. Strings are
immutable in Lua, meaning any modification creates a new string. They can be
enclosed in single quotes ('), double quotes ("), or double square brackets ([[ ]])
for multiline strings.
Lua
local greeting = "Hello, MTA:SA!"
local multilineText =]
print(greeting)
print(multilineText)
print(type(greeting)) -- Output: string

● table: Lua's general-purpose aggregate data type, used for storing collections
like lists, arrays, and dictionaries (associative arrays).13 Tables are explained in
detail in a later section.
● function: Functions are first-class values in Lua, meaning they can be assigned
to variables, passed as arguments, and returned from other functions.13
● userdata: Represents objects foreign to Lua, typically implemented in C or C++
and exposed to Lua.13 In MTA:SA, game elements like players, vehicles, and
objects are often represented as userdata.
● thread: Primarily used for coroutines, enabling cooperative multitasking within
Lua programs.
Tips for Beginners:
● Always declare variables with local if they are only needed within a specific scope
(e.g., a function or block). This prevents accidental global variable creation and
potential conflicts with other scripts.
● Use print(type(variable)) to quickly check the data type of a variable during
development.

Common Mistakes:
● Forgetting that global variables, if not explicitly assigned, default to nil, which can
lead to runtime errors when operations are attempted on them.
● Assuming 0 or an empty string "" evaluates to false in conditional statements. In
Lua, only nil and false evaluate to false; all other values (including 0, empty
strings, and empty tables) evaluate to true.

3. Operators in Lua

Lua supports standard arithmetic, relational, logical, and other operators.


● Arithmetic Operators: Perform mathematical calculations.
○ + (addition)
○ - (subtraction)
○ * (multiplication)
○ / (division)
○ % (modulo - remainder of division)
○ ^ (exponentiation)
○ - (unary negation)
Lua
print(10 + 5) -- 15
print(10 - 5) -- 5
print(10 * 5) -- 50
print(10 / 4) -- 2.5
print(10 % 3) -- 1
print(2 ^ 3) -- 8
print(-5) -- -5

● Relational Operators: Compare two values and return a boolean (true or false).13
○ == (equality) - Note: single = is for assignment.13
○ ~= (inequality)
○ > (greater than)
○ < (less than)
○ >= (greater than or equal to)
○ <= (less than or equal to)
Lua
print(5 == 5) -- true
print(5 ~= 10) -- true
print(10 > 5) -- true
print(5 < 10) -- true
print(5 >= 5) -- true
print(5 <= 10) -- true

● Logical Operators: Combine or modify boolean expressions.14


○ and (logical AND)
○ or (logical OR)
○ not (logical NOT) 13
Lua
local isSunny = true
local isWarm = false
print(isSunny and isWarm) -- false
print(isSunny or isWarm) -- true
print(not isSunny) -- false

○ Short-circuit evaluation: and returns the first false value or the second
operand if both are true. or returns the first true value or the second operand
if both are false.
Lua
print(nil and 10) -- nil
print(true and 10) -- 10
print(false or "hello") -- "hello"

● Concatenation Operator:
○ .. (string concatenation)
Lua
local firstName = "John"
local lastName = "Doe"
print(firstName.. " ".. lastName) -- "John Doe"
● Length Operator:
○ # (length of strings or array-like tables) 13
Lua
local myString = "MTA"
print(#myString) -- 3
local myArray = {"a", "b", "c"}
print(#myArray) -- 3

Tips for Beginners:


● Pay close attention to the difference between = (assignment) and ==
(comparison). This is a common source of errors for newcomers from other
languages.13
● Understand short-circuit evaluation for and and or as it can affect the return
value and prevent errors (e.g., if myTable and myTable.key then).

Common Mistakes:
● Using + or other arithmetic operators on nil values or strings that are not valid
numbers, leading to runtime errors.10
● Misunderstanding the behavior of the # operator for tables, especially with
sparse arrays, where it may not return the total number of elements.13

4. Control Structures (if, loops)

Control structures dictate the flow of execution in a Lua program, allowing code
blocks to run conditionally or repeatedly.14
● Conditionals (if, elseif, else): Execute code blocks based on whether a
condition is true.14
○ if condition then... end: Basic conditional execution.14
○ if condition then... else... end: Provides an alternative block for when the if
condition is false.14
○ if condition then... elseif condition then... else... end: Allows testing multiple
conditions sequentially.14
Lua
local score = 85
ifscore >= 90 then
print("Grade: A")
elseif score >= 80 then
print("Grade: B") -- This will be executed
else
print("Grade: C or lower")
end

● Loops (while, for, repeat): Used for repeatedly executing a block of code.14
○ while condition do... end: Continues executing as long as the condition
remains true.14
Lua
localcount = 1
while count <= 3 do
print("Count is: ".. count)
count = count + 1
end
-- Output:
-- Count is: 1
-- Count is: 2
-- Count is: 3

○ for i = start, end, [delta] do... end: Numerical for loop, iterates a specific
number of times.14
delta (step value) defaults to 1.14
Lua
for i = 1, 5 do
print("Iteration: ".. i)
end
-- Output:
-- Iteration: 1
-- Iteration: 2
-- Iteration: 3
-- Iteration: 4
-- Iteration: 5

for i = 10, 1, -2 do -- Counting down by 2


print("Countdown: ".. i)
end
-- Output:
-- Countdown: 10
-- Countdown: 8
-- Countdown: 6
-- Countdown: 4
-- Countdown: 2

○ for key, value in pairs(table) do... end: Generic for loop, iterates over all
key-value pairs in a table.14 The order of iteration is not guaranteed.13
Lua
local playerStats = {health = 100, armor = 50, weapon = "Pistol"}
for key, value in pairs(playerStats) do
print(key.. ": ".. value)
end
-- Example Output (order may vary):
-- health: 100
-- armor: 50
-- weapon: Pistol

○ for index, value in ipairs(array_like_table) do... end: Iterates over array-


like tables with guaranteed sequential order for consecutive integer keys
starting from 1.13
Lua
local items = {"Sword", "Shield", "Potion"}
for index, value in ipairs(items) do
print(index.. ": ".. value)
end
-- Output:
-- 1: Sword
-- 2: Shield
-- 3: Potion

○ repeat... until condition: Executes the code block at least once, then
repeats until the condition becomes true.14
Lua
local counter =0
repeat
print("Counter is: "..
counter)
counter = counter + 1
until counter > 2
-- Output:
-- Counter is: 0
-- Counter is: 1
-- Counter is: 2
○ break: Exits the innermost loop immediately.14
Lua
for i = 1, 10 do
if i == 5 then
break -- Exit loop when i is 5
end
print(i)
end
-- Output: 1, 2, 3, 4

Tips for Beginners:


● Use if/elseif/else for clear, mutually exclusive conditions.
● Choose the right loop type: numerical for for fixed iterations, ipairs for arrays,
pairs for dictionaries.
● Be cautious with while loops to avoid infinite loops; always ensure the condition
will eventually become false.

Common Mistakes:
● Forgetting the then keyword after if or elseif conditions, or do after while or for
loops, leading to syntax errors.10
● Incorrectly using pairs when ipairs is needed for guaranteed array order, or vice-
versa, leading to unexpected iteration behavior.13
● Creating infinite loops, which can freeze the script or server.15

5. Functions and Scope

Functions in Lua are reusable blocks of code that perform specific tasks.14 They are a
fundamental concept for organizing code, promoting reusability, and creating
modular programs.14
● Defining a Function: Functions are defined using the function keyword.13
Lua
-- Global function
function greet(name)
print("Hello, ".. name.. "!")
end
-- Local function (recommended)
local function add(a, b)
return a+b
end

A function definition includes an optional local keyword for scope, a function


name, a list of arguments (parameters), a function body, and an optional return
statement.16
● Function Arguments (Parameters): Arguments are placeholders for values
passed to the function when it is called.16 These are local variables within the
function's scope.16
Lua
function calculateArea(length, width)
localarea = length * width
return area
end

● Return Values: Functions can return one or more values using the return
statement.14 If no
return statement is used, the function implicitly returns nil.14
Lua
function getPlayerCoordinates(player)
localx, y, z = getElementPosition(player) -- MTA:SA function
return x, y, z
end

local px, py, pz = getPlayerCoordinates(localPlayer)


print("Player position:", px, py, pz)

● Calling a Function: To execute a function, use its name followed by


parentheses, passing any required arguments.16
Lua
greet("World") -- Calls the global greet function
local sum = add(5, 3) -- Calls the local add function
print("Sum:", sum)

● Anonymous Functions: Functions can be defined without a name and assigned


to variables.14 This is common for event handlers or callbacks.
Lua
localmultiply = function(a, b)
return a * b
end
print("Product:", multiply(4, 2))

● Variable Arguments (...): Functions can accept a variable number of arguments


using ....16 These arguments can be accessed as a table using
local arg = {...}.16
Lua
function average(...)
local sum = 0
local arg = {...} -- Collects all variable arguments into a table
for i, v in ipairs(arg) do
sum = sum + v
end
return sum / #arg
end

print("Average:", average(10, 20, 30, 40)) -- Output: Average: 25

● Scope: Scope determines where a variable or function is accessible.


○ Global Scope: Variables and functions declared without local are global and
accessible from anywhere in the script or even other scripts (if not explicitly
restricted).16
○ Local Scope: Variables and functions declared with local are confined to the
block (e.g., if statement, for loop, function body) in which they are defined.14
This prevents naming conflicts and improves code organization.
○ Closures: Lua supports closures, meaning a function can "capture" and
access variables from its surrounding (lexical) scope, even after the outer
function has finished executing.14

Tips for Beginners:


● Use local function for most of your functions to keep them encapsulated and
avoid polluting the global namespace. This is crucial for modularity and
preventing conflicts in a multi-resource MTA:SA environment.
● Understand that return not only specifies values but also immediately exits the
function.
● Embrace the concept of variable scope early. Variables defined inside a function
are not accessible outside it unless explicitly returned.

Common Mistakes:
● Not using local for functions or variables, leading to global functions that might
conflict with other scripts or unexpected side effects across different parts of the
codebase.
● Assuming the result from a function call is always a single value; remember
functions can return multiple values, and if you only assign to one variable, the
others are discarded.
● Misunderstanding variable scope, leading to nil values when trying to access
variables outside their defined scope.

Lua's treatment of functions as first-class values 13 is a powerful feature that enables


advanced programming paradigms like functional programming and event-driven
architectures, which are fundamental to MTA:SA scripting. The ability to assign
functions to variables, pass them as arguments (e.g., to

addEventHandler), and return them from other functions is not merely a syntactic
detail; it is a core language feature.13 This "first-class citizen" status, combined with
closures that allow functions to "remember" their environment 14, forms the bedrock
of event-driven programming and callback mechanisms, which are central to how
MTA:SA scripts interact with the game world. For MTA:SA developers, this means that
event handlers (which are functions) can be dynamically attached and detached, and
custom functions can be easily integrated into the game's event system, enabling
highly modular and reactive code essential for dynamic game environments.

6. Tables in Lua (arrays and dictionaries)

Tables are Lua's sole and most versatile data structuring mechanism.13 They serve as
both arrays (indexed by numbers) and dictionaries (associative arrays, indexed by any
value except

nil or NaN).13 This unified approach simplifies the language's core by providing a
single, powerful tool for various data collection needs.
● Creation: Tables are created using a pair of curly brackets {}.13
Lua
local myTable = {} -- An empty table
local playerStats = {name = "CJ", health = 100} -- Table as a dictionary
local inventory = {"Weapon", "Armor", "Health Pack"} -- Table as an array

● As Dictionaries (Associative Arrays): Tables store key-value pairs, where keys


can be numbers, strings, or even other tables or functions.13
○ Accessing Values: Values are accessed using table[key] syntax.13 For string
keys, a shortcut
table.key is available, provided the key name follows Lua's identifier rules
(underscores, letters, numbers, not starting with a number).13
Lua
local playerInfo = {
name = "Tommy Vercetti",
age = 35,
city = "Vice City"
}
print("Player Name:", playerInfo.name)
print("Player Age:", playerInfo["age"])

○ Adding/Modifying Pairs: Assign a value to a key: playerInfo.money =


100000.13
○ Removing Pairs: Assign nil to a key's value: playerInfo.city = nil.13 If a key has
no associated value, accessing it returns
nil without an error.13
● As Arrays: Tables can simulate arrays by using consecutive integer keys,
typically starting from 1 (unlike many other languages that start from 0).13
Lua
local weapons = {"Pistol", "Shotgun", "AK47"}
print("First Weapon:", weapons) -- Output: Pistol
print("Second Weapon:", weapons) -- Output: Shotgun

● Length Operator (#): The # operator returns the "length" of an array-like table
by finding the last integer (non-fractional number) key.13 However, its results are
undefined for sparse arrays (arrays with gaps in their integer keys), making it
unreliable in such cases.13
Lua
local items = {"Apple", "Banana", "Cherry"}
print("Number of items:", #items) -- Output: 3

local sparseTable = {}
sparseTable = "First"
sparseTable = "Fifth"
print("Length of sparse table:", #sparseTable) -- Output: 1 (unreliable for sparse arrays)

● Table Manipulation Functions: Lua's standard library provides functions for


common table operations.
○ table.insert(table, [pos,] value): Adds an item to a table. If pos is omitted, it
adds to the end.13
○ table.remove(table, [pos]): Removes an item from a table. If pos is omitted, it
removes the last item.13
○ table.concat(table, [separator,][i,][j]): Joins string elements of a table into a
single string.13
Lua
local fruits = {"Apple", "Banana"}
table.insert(fruits, "Orange") -- Adds to end
table.insert(fruits, 2, "Grape") -- Inserts at index 2, shifting others
print("Fruits:", table.concat(fruits, ", ")) -- Output: Fruits: Apple, Grape, Banana, Orange

table.remove(fruits, 3) -- Removes "Banana"


print("Fruits after removal:", table.concat(fruits, ", ")) -- Output: Fruits after removal: Apple,
Grape, Orange

● Iteration:
○ for key, value in pairs(table) do... end: Iterates over all key-value pairs in a
table. The order of iteration is not defined.13
○ for index, value in ipairs(array_like_table) do... end: Iterates over array-like
tables with guaranteed sequential order for consecutive integer keys starting
from 1.13
● Tables as References: When a table is assigned to a new variable or passed to a
function, a new copy is not created.13 Instead, the new variable or function
receives a
reference to the original table, similar to a pointer in C.13 This means modifying
the table through one reference will affect all other references to the same
table.13 Tables are freed by the garbage collector when no references to them
remain.13
Lua
local originalTable = {value = 10}
local anotherReference = originalTable
anotherReference.value = 20 -- Modifies the originalTable

print(originalTable.value) -- Output: 20

function modifyTable(t)
t.newValue = true
end
modifyTable(originalTable)
print(originalTable.newValue) -- Output: true

Tips for Beginners:


● Embrace tables! They are incredibly versatile and fundamental to Lua
programming, serving almost all data structuring needs.
● Be mindful that tables are passed by reference. If a function modifies a table
passed to it, the original table outside the function will also be changed. If a true
copy is needed, it must be manually created.

Common Mistakes:
● Assuming the # operator always counts all items in a table, especially if it's a
mixed (array and dictionary parts) or sparse table.13 It only works reliably for
sequential integer keys starting from 1.
● Forgetting that arrays are 1-indexed in Lua, not 0-indexed like many other
common programming languages.
● Attempting to use nil or NaN as table keys, which will cause a runtime error.13

Lua's unified table type 13 simplifies data structure management, but its pass-by-
reference behavior 13 is a critical concept that can lead to subtle bugs if not fully
understood. The fact that tables are the

only complex data structure means all other common structures (lists, sets, objects,
arrays) are built upon them.13 This design choice streamlines the language's core but
places the responsibility on developers to grasp how tables behave under the hood,
particularly their reference semantics. This means that any changes made to a table
through one reference are immediately visible through all other references. For
MTA:SA scripting, this is especially crucial when dealing with game elements (players,
vehicles, objects), which are often represented as Lua tables or userdata that behave
like tables. Modifying an element's properties through one script will affect its state
globally if not handled carefully. This also implies that passing large tables between
client and server via events can be efficient as it is passing references, but careful
consideration of synchronization is required.

Practice Exercises (Module 1)

1. Variable Swapper: Write a Lua script that declares two variables, a and b, with
initial numerical values. Then, without using a third temporary variable, swap their
values. Print the values of a and b before and after the swap.
2. Simple Calculator: Create a function calculate(num1, operator, num2) that takes
two numbers and a string operator (+, -, *, /). The function should return the
result of the operation. Include error handling for invalid operators or division by
zero.
3. Player Inventory Manager (Table Practice):
○ Create a table named playerInventory to represent a player's items. Initialize
it with a few items (e.g., {"Sword", "Shield", "Potion"}).
○ Add a new item to the end of the inventory.
○ Remove an item from a specific position (e.g., the second item).
○ Print the entire inventory using table.concat.
○ Add a new entry to the playerInventory table to store the player's money
(e.g., playerInventory.money = 500). Print the player's money.

📕 Module 2: MTA:SA Scripting Basics

1. Setting up an MTA:SA resource

Custom content and gameplay modifications in MTA:SA are organized into self-
contained units called "resources". A resource is essentially a package, which can be
a single archive (like a ZIP file) or a directory, containing all the necessary script files,
custom assets (such as images, 3D models, textures, and collision files), and a crucial
metadata file named meta.xml. These resources are hosted on the MTA:SA game
server.

To set up a new resource, follow these steps:


1. Create a Resource Directory: Navigate to your MTA:SA server installation
directory, specifically to <SERVER>\mods\deathmatch\resources.7 Inside this
resources folder, create a new sub-directory. The name of this directory will be
the name of your resource (e.g., my_first_resource).
2. Add Script Files: Inside your newly created resource directory, place your Lua
script files. Common conventions include:
○ server.lua: For code that runs exclusively on the server.
○ client.lua: For code that runs exclusively on the player's client.
○ shared.lua: For code that runs on both the server and client (separately).
3. Create meta.xml: In the root of your resource directory (the same level as your
script files), create a file named meta.xml.17 This XML file acts as the manifest for
your resource, telling MTA:SA what files to load, where they should run, and other
essential metadata.
4. Load the Resource on Server: Once your resource folder is structured and
contains the meta.xml and script files, you need to inform the MTA:SA server
about its presence.
○ Open your MTA:SA server console window.
○ Type refresh and press Enter. This command instructs the server to re-scan
its resources folder and update its internal list of available resources.7
○ To start your resource, type start [resourceName] (e.g., start
my_first_resource) and press Enter. The server will then load and execute the
scripts defined in your meta.xml file.20

Code Example (Basic Resource Structure):

my_first_resource/
├── meta.xml
├── server.lua
└── client.lua

Tips for Beginners:


● Keep your resource names descriptive and unique to avoid conflicts with other
resources.
● Start with a minimal meta.xml file and add entries for scripts and files as your
resource develops. This helps in understanding each component's role.

Common Mistakes:
● Incorrect Folder Structure: Placing script files or meta.xml in the wrong
subdirectories within the resource folder, preventing MTA:SA from finding them.
● Typos: Simple spelling mistakes in resource names, script filenames, or the
meta.xml file can prevent the resource from loading or functioning correctly.
● Forgetting refresh: Not running the refresh command after adding or updating a
resource means the server won't recognize the changes.

The "resource" system is not just a file organization method; it is MTA:SA's core
modularity and distribution mechanism. This implies a strong emphasis on reusability
and ease of sharing custom content within the MTA:SA ecosystem. The design choice
to package all related assets and code into self-contained "resources" is critical for a
multiplayer environment where custom content needs to be reliably transferred to
clients and managed by the server. The mention of "package dependency and
inheritance of functions between different packages" further indicates a
sophisticated system for building complex gamemodes from smaller, reusable
components. Developers should therefore design their scripts with modularity in
mind, carefully considering what functionality belongs in a specific resource and how
it might interact with others. This also has implications for performance (loading only
necessary resources) and security (resource permissions via Access Control List).

2. The meta.xml file explained

The meta.xml file is an XML-formatted document that serves as the manifest for
every MTA:SA resource.18 It provides essential metadata and configuration
instructions to the MTA:SA engine, dictating how the resource should be loaded, what
files it contains, and how it interacts with other parts of the game.18 This file is written
in XML, a textual data format widely used for data representation, similar in structure
to HTML.18

Key tags within the meta.xml file include:


● <info>: This tag provides general information about the resource.
○ author: The creator of the resource.
○ version: The resource's version number.
○ name: The display name of the resource.
○ description: A brief summary of what the resource does.
○ type: Specifies the resource's category (e.g., "gamemode", "script", "map", or
"misc").18
XML
<info author="YourName" version="1.0.0" name="MyFirstResource" description="A simple
example resource" type="script" />

● <script>: This crucial tag defines the Lua source code files to be loaded.
○ src: The file path to the Lua script (e.g., server.lua, scripts/my_logic.lua).18
○ type: Determines where the script will execute:
■ "server": Runs only on the game server.
■ "client": Runs only on the player's client.
■ "shared": Runs separately on both the client and the server.18
○ cache: For client-side scripts, cache="false" prevents the file from being
saved on the client's hard drive, which can be a security consideration.18
Default is
true.
○ validate: If false, compatibility checks are skipped.18
XML
<script src="server.lua" type="server" />

<script src="client.lua" type="client" cache="false" />

<script src="shared.lua" type="shared" />

● <file>: Specifies client-side files (e.g., images, 3D models, textures, XML files)
that need to be downloaded by clients when the resource starts.18
○ src: The file path to the asset (e.g., images/my_image.png).18
○ download: If false, the file is not automatically sent on resource start but can
be downloaded later using downloadFile.18 Default is
true.
XML
<file src="images/my_image.png" />
<file src="models/my_custom_car.dff" />

● <map>: Used to include .map files, which define game world elements for
gamemodes.19
● <include>: Allows a resource to declare dependencies on other resources. The
specified resources will be started along with the current one.18
● <config>: Defines XML configuration files (.xml) that the resource can access.18
● <export>: Exports functions from the resource, making them callable by other
resources using the call function.18
○ function: The name of the function to export.
○ type: Specifies if the function is exported server-side, client-side, or shared.18
XML
<export function="myExportedServerFunction" type="server" />
<export function="myExportedClientFunction" type="client" />

Code Example (Complete meta.xml):

XML

<meta>
<info author="YourName" version="1.0.0" name="MyFirstResource" description="A simple example
resource" type="script" />

<script src="server.lua" type="server" />

<script src="client.lua" type="client" />

<script src="shared.lua" type="shared" />

<file src="images/my_image.png" />

<export function="getExampleData" type="server" />


</meta>
Tips for Beginners:
● Always start with a basic meta.xml structure and gradually add tags as your
resource's complexity increases. This helps in understanding the purpose of each
entry.
● The type attribute for <script> tags is fundamental; correctly defining it ensures
your Lua code runs in the intended environment (client or server).

Common Mistakes:
● Missing meta.xml: The resource will not be recognized or loaded by the server.
● Incorrect XML Syntax: Even a small typo (e.g., missing closing tag, incorrect
attribute quoting) can prevent the entire meta.xml from being parsed, leading to
resource loading failures.
● Forgetting to list files: If a script or asset is used but not declared in meta.xml, it
won't be loaded, causing errors or missing content in-game.
● Incorrect type for scripts: Accidentally marking a server-side script as
type="client" can lead to security vulnerabilities or unexpected behavior, as
sensitive logic might be exposed.

The meta.xml file acts as the manifest and dependency manager for MTA:SA
resources.18 Its structured XML format enforces a clear contract for how resources
are loaded and interact, which is vital for complex multiplayer environments. This file
is more than just a configuration; it is the central point of control for a resource's
lifecycle and its interactions within the MTA ecosystem. It dictates what assets are
sent to clients, what code runs where, and how modularity (through imports/exports)
is achieved. The XML format provides a standardized, machine-readable way to
define these complex relationships. Proper

meta.xml configuration is therefore critical for resource functionality, performance


(client precaching), and security (controlling script visibility and access).
Misconfigurations can lead to broken resources, client-side vulnerabilities, or
unexpected behavior.

3. Event-driven programming in MTA:SA


MTA:SA scripting is fundamentally built upon an event-driven programming model.8
This means that instead of a linear execution flow, scripts primarily react to "events"
– signals triggered by various occurrences within the game world or by player
actions.8 This reactive paradigm is essential for dynamic and interactive multiplayer
environments.

Core Concepts:
● Events: Events are notifications that something has happened. MTA:SA provides
a wide range of built-in events, such as onPlayerJoin (when a player connects),
onVehicleExplode (when a vehicle is destroyed), or onClientGUIClick (when a GUI
element is clicked).8
● Event Handlers: These are Lua functions that are "attached" to specific events.22
When an event occurs, all attached handlers for that event are executed. Event
handlers are registered using the
addEventHandler function.22
● addEventHandler(eventName, element, handlerFunction, [propagate=true],
[priority="normal"]): This function links a handlerFunction to an eventName on a
specific element.22
○ eventName: The name of the event to listen for (e.g., "onPlayerJoin").
○ element: The element to which the handler is attached. This is crucial for
performance and specificity. For global events, root (server-side) or
getRootElement() (client-side) is often used, but it is generally more efficient
to attach to a more specific element if possible.22
○ handlerFunction: The Lua function to execute when the event fires.
● source Element: Within an event handler function, the global source variable
refers to the element that originated or triggered the event.8 For example, in
onPlayerJoin, source is the player who joined.26
● this Element: The this variable (also available within handlers) refers to the
element the event handler is attached to.22 This can be useful for handlers
attached to specific elements like markers or vehicles.
● Event Parameters: Most events pass specific parameters to their handler
functions, providing additional context about what happened.22 For instance,
onClientGUIClick provides parameters like button, state, absoluteX, and
absoluteY.22 Developers must consult the MTA:SA Wiki for the parameters of each
specific event.22
● Custom Events: Developers can define and trigger their own custom events to
facilitate communication between different parts of their code or even between
different resources.22
○ addEvent(eventName,): Registers a new custom event.22 Set
remoteTriggerable to true if the event needs to be triggered across the
client-server boundary (e.g., a client event triggering a server event).28
○ triggerEvent(eventName, sourceElement, [arguments...]): Triggers an event.22
● Event Propagation: Events in MTA:SA follow the element tree hierarchy.22 An
event triggered on a specific element will also propagate to its parent elements
(and their parents recursively) and its child elements (and their children
recursively).22 Handlers attached to the
root element will catch every event of that type in the entire element tree.22
● Canceling Events: Some events can be "canceled" using cancelEvent() within
their handler function.22 This prevents the default game action associated with
that event from occurring. For example, canceling
onPickupUse stops a player from picking up an item.22 It is important to note that
canceling an event does not stop other event handlers from being triggered.22

Code Example (Event Handling):

Lua

-- server.lua
-- Server-side: Welcome message on player join
addEventHandler("onPlayerJoin", root, function()
local playerName = getPlayerName(source)
outputChatBox("Welcome ".. playerName.. " to our server!", source, 0, 255, 0, true) --
Green welcome message
outputConsole(playerName.. " has joined the server.") -- Log to server console
end)

-- client.lua
-- Client-side: Display message when a vehicle explodes
addEventHandler("onClientVehicleExplode", root, function()
local vehicleName = getVehicleName(source)
outputChatBox(vehicleName.. " just exploded nearby!", 255, 0, 0) -- Red message
end)
Tips for Beginners:
● Understanding the source element is key to knowing what triggered the event
and how to interact with it.
● Use addEventHandler with the most specific element possible (e.g., a specific
vehicle, a player, or resourceRoot for resource-specific events) rather than root
to optimize performance and avoid unnecessary triggers.22 This minimizes the
number of times your handler function is called.

Common Mistakes:
● Over-attaching to root: Attaching event handlers to root when a more specific
element would suffice can lead to significant performance overhead, especially
for frequently occurring events.22
● Ignoring event parameters: Not checking the parameters passed to an event
handler, or assuming source will always be a specific type, can lead to runtime
errors when events are triggered by unexpected elements or contexts.
● Forgetting to register custom events: Custom events must be registered with
addEvent before they can be triggered with triggerEvent.
● Misunderstanding cancelEvent(): Not all events can be canceled.24 Also,
canceling an event only prevents its default action, not other handlers from
running.22

The event system 22 is MTA:SA's primary mechanism for

inter-script and client-server communication, enabling dynamic and reactive


gameplay. Its hierarchical nature (element tree) provides a powerful, yet sometimes
complex, model for event propagation. This system is not just a feature; it is the
paradigm for MTA:SA scripting. It defines how the game engine communicates with
scripts, how scripts communicate with each other, and how client and server
components interact. The element tree hierarchy for event propagation is a
sophisticated design that allows for both broad and highly specific event handling.
Custom events extend this power to user-defined interactions, enabling complex,
interconnected systems. Mastery of the event system is therefore paramount for any
MTA:SA developer. Understanding event parameters, source, this, and the
propagation model is crucial for writing efficient, robust, and secure scripts. It also
highlights the importance of addEvent and triggerEvent for building complex,
interconnected systems.
4. Client-side vs Server-side scripts

MTA:SA scripts operate within two distinct and fundamental environments: client-side
and server-side.8 This architectural division is crucial for performance, security, and
the overall design of game modes.
● Server-side Scripts:
○ Execution Environment: These scripts run exclusively on the game server.31
○ Purpose and Security: Server-side scripts handle the core game logic,
manage persistent player data (like money, inventory, and experience),
control vehicle spawning and persistence, and interact with databases.8 A key
advantage is security: the code for server-side scripts is never sent to the
client, making it inaccessible and less prone to tampering by malicious
players.21 This is where sensitive operations, such as giving money or banning
players, must occur.8
○ Examples: Implementing admin commands, managing player accounts,
saving game state, synchronizing world changes, and handling anti-cheat
logic.8
● Client-side Scripts:
○ Execution Environment: These scripts run directly on each player's MTA
client (their computer).8
○ Purpose and Limitations: Client-side scripts are primarily responsible for
graphical user interfaces (GUIs), local player-specific visual effects, handling
player input, and rendering custom elements.8 Since client-side code is
executed on the player's machine, it is inherently less secure; it can be
accessed and potentially modified by the client.21 Therefore, no sensitive
game logic or data that needs to be secure or synchronized across all players
should reside client-side.21
○ Examples: Drawing custom HUDs, creating interactive GUI menus, playing
local sounds, handling custom player animations, and processing key
bindings.1
● Shared Scripts:
○ Scripts declared as type="shared" in meta.xml are executed separately on
both the client and the server.18
○ They are typically used for functions, variables, or data structures that are
common to both environments and do not contain sensitive logic (e.g., utility
functions, shared configuration tables, or common constants).18
It is crucial to remember that shared scripts, when running client-side, are still

subject to client-side security risks.21
● Communication Between Client and Server:
○ Client and server scripts cannot directly call functions on the other side. They
communicate exclusively through the event system.
○ Server to Client: The server triggers an event on one or more clients using
triggerClientEvent(player/tableOfPlayers, eventName, sourceElement,
[arguments...]).28 This is the primary way the server sends information or
commands to players.
○ Client to Server: A client triggers an event on the server using
triggerServerEvent(eventName, sourceElement, [arguments...]).28 This is how
clients send input or requests to the server.
○ Event Registration for Remote Calls: For an event to be triggered across
the client-server boundary, it must be explicitly registered as
remoteTriggerable using addEvent(eventName, true).28
○ Identifying Players in Remote Events:
■ On the client-side, localPlayer refers to the player running the script.
■ On the server-side, when an event is triggered from a client, the global
client variable within the event handler refers to the player who triggered
that event.29 This is a critical security measure to prevent "event faking".29

Code Example (Client-Server Communication):


● meta.xml
XML
<meta>
<info author="YourName" version="1.0.0" name="ClientServerExample"
description="Demonstrates client-server communication" type="script" />
<script src="server.lua" type="server" />
<script src="client.lua" type="client" />
</meta>

● server.lua
Lua
-- Server-side: Registers an event that clients can trigger
addEvent("requestServerTime", true) -- 'true' makes it remotely triggerable
addEventHandler("requestServerTime", root, function()
-- 'client' global variable refers to the player who triggered this event
currentTime = getRealTime()
local
outputConsole("Player ".. getPlayerName(client).. " requested server time.")
-- Trigger a client event to send time back to the requesting player
triggerClientEvent(client, "receiveServerTime", root, currentTime.hour,
currentTime.minute, currentTime.second)
end)

-- Server-side: Registers an event to receive messages from clients


addEvent("sendClientMessage", true)
addEventHandler("sendClientMessage", root, function(message)
-- Always validate data coming from the client!
if type(message) == "string" and string.len(message) > 0 and string.len(message) <=
128 then
outputChatBox("Message from ".. getPlayerName(client).. ": ".. message, root,
255, 255, 0)
else
outputChatBox("Invalid message received from ".. getPlayerName(client), client,
255, 0, 0)
end
end)

● client.lua
Lua
-- Client-side: Registers an event that the server can trigger
addEvent("receiveServerTime", true)
addEventHandler("receiveServerTime", localPlayer, function(hour, minute, second)
outputChatBox(string.format("Server time: %02d:%02d:%02d", hour, minute, second),
0, 255, 255)
end)

-- Client-side: Command to request server time


addCommandHandler("gettime", function()
-- Trigger a server event to request time from the server
triggerServerEvent("requestServerTime", localPlayer)
end)

-- Client-side: Command to send a message to the server


addCommandHandler("saytoserver", function(cmd,...)
local message = table.concat({...}, " ")
if message ~= "" then
-- Trigger a server event to send the message
triggerServerEvent("sendClientMessage", localPlayer, message)
else
outputChatBox("Usage: /saytoserver <your message>", 255, 0, 0)
end
end)

Client-Side vs. Server-Side Scripting Comparison

Aspect Client-Side Characteristics

Execution Environment Player's local computer 8

Visibility Code is accessible to the client; less secure 21

Graphical User Interfaces (GUIs), local visual


Typical Use Cases
effects, input processing, custom HUDs,
animations 1

Communication triggerServerEvent to send data to server 28

Player Identification localPlayer global variable

Tips for Beginners:


● Always decide early whether a script's logic should be client-side, server-side, or
shared. A general rule is: if it affects all players, needs to be secure, or involves
persistent data, it belongs on the server. If it's purely visual or local to one
player's experience, it's client-side.
● For events that need to cross the client-server boundary, always use
addEvent(eventName, true).
● Crucially, always validate any data coming from the client on the server-side, as
client data can be easily faked or manipulated by malicious players.21

Common Mistakes:
● Security Vulnerabilities: Placing sensitive game logic (e.g., giving money,
modifying player stats without checks) on the client-side, which allows players to
easily exploit the system.
● Direct Function Calls: Attempting to directly call a server function from the
client or vice-versa without using the event-driven communication model. This
will simply not work.
● Forgetting remoteTriggerable: Not setting the second argument of addEvent to
true for events intended to be triggered remotely, leading to events that cannot
be called across the network.
● Trusting Client Data: Failing to validate inputs received from the client on the
server-side, opening doors for cheating and exploits.21

The strict client-server separation and event-driven communication 28 are


foundational to MTA:SA's

security model and synchronization capabilities. Understanding this division is


paramount for preventing exploits and ensuring consistent gameplay across all
players. The client-server model is not just about where code executes; it establishes
a critical security boundary. Any code or data residing on the client can be tampered
with. Therefore, all critical game logic, such as managing player economy, updating
player statistics, or implementing anti-cheat mechanisms, must reside and be
processed exclusively on the server-side. Communication between these two
environments must be explicit via events, and server-side validation of any data sent
from the client is non-negotiable to prevent cheating.21 The use of

addEvent(..., true) to explicitly mark events as remotely triggerable and the reliance
on the client global variable on the server-side for identifying the originating player
are direct mechanisms to enforce this security. Developers must adopt a "never trust
the client" mentality. Any data received from the client should be validated and
sanitized on the server before being used in critical game logic. This also means that
complex GUI interactions often require a client-server "dance" of events to update
game state securely and reliably.

5. Debugging scripts

Debugging is an essential part of the development process, involving the


identification, analysis, and resolution of errors in code. In MTA:SA scripting, various
tools and techniques are available to help developers efficiently debug their Lua
scripts.

Common Error Types:


● Syntax Errors: These occur when the Lua parser encounters code that violates
the language's grammatical rules (e.g., missing end keywords, misplaced
operators, or incorrect variable declarations).10 These errors are usually caught
early by the interpreter when the script loads, and often provide informative
messages indicating the file and line number of the issue.15
● Runtime Errors: These errors manifest during the execution of a script when an
operation cannot be performed (e.g., attempting arithmetic on a nil value,
accessing an invalid table index, or calling a non-existent function).10 They
typically result in a script crash or unexpected behavior.
● Logical Errors: These are the most challenging errors to detect because the
script runs without any syntax or runtime errors but produces incorrect or
unintended results due to flaws in the program's logic.10

Debugging Tools & Techniques:


● outputChatBox() and outputConsole(): These are fundamental for basic
tracing.
○ outputChatBox(text, [player], [r, g, b], [colorCoded]): Displays messages
directly in the in-game chat box.35 It can be sent to all players (
root) or a specific player.
○ outputConsole(text): Displays messages in the server's console window.14
○ outputDebugString(text): Displays messages in the client's F8 console (client-
side) or server console (server-side). This is often preferred over
outputChatBox for frequent debug messages to avoid spamming the chat.36

These functions are invaluable for tracking variable values, confirming code
execution paths, and identifying where unexpected behavior originates.10
● print(): The standard Lua print() function typically outputs to the server console
or debug log, similar to outputConsole.13
● Visual Studio Code Extensions: Modern code editors like VS Code offer
specialized extensions for MTA:SA Lua development.9 These extensions provide:
○ Syntax Highlighting: Visually identifies different parts of the code, making
syntax errors easier to spot.10
○ Code Completion: Suggests function names, parameters, and variables,
reducing typos.11
○ Debugging Features: Advanced extensions like the "MTA:SA Lua Debugger
and Test Framework" allow for setting breakpoints, stepping through code
line-by-line (step into, step over), and inspecting variable values (locals,
globals, upvalues).9
● addDebugHook(): This MTA:SA function allows for tracing specific MTA
functions and events.37 However, it can degrade script performance and should
only be used temporarily during active debugging sessions.37
● Server Console and Client Console (F8): These windows display script errors,
warnings, and messages from outputConsole/outputDebugString.6 Learning to
interpret these messages, including file names and line numbers, is crucial for
pinpointing issues.15

Best Practices for Debugging:


● Modularize Your Code: Divide large, complex scripts into smaller, more
manageable functions or modules.10 This makes it easier to isolate problematic
sections and test them independently.
● Comment Your Code: Add clear and concise comments, especially for complex
logic or non-obvious sections.10 This helps both the original developer and others
understand the script's intent and identify where logic might be flawed.
● Consistent Formatting: Maintain a uniform indentation style and naming
conventions.10 Well-formatted code is easier to read and helps visually identify
structural issues.
● Input Validation: Implement robust input validation, especially for data received
from clients or user commands.38 This prevents many common runtime errors
caused by unexpected or malicious input (e.g., trying to perform arithmetic on a
string).10
● Check for nil Values: Before attempting to access properties of a table or
perform operations on a variable, always verify that it is not nil.15 Using
conditional checks (
if variable then) or default values (local value = myTable.key or defaultValue) can
prevent runtime errors.
● Version Control: Utilize version control systems like Git. This allows developers
to track changes, revert to previous working states if new errors are introduced,
and collaborate effectively.10
● Avoid Masking Warnings: Resist the temptation to use excessive isTimer or
isElement checks solely to hide debug warnings from deleted elements.27 While
seemingly harmless, this can mask underlying issues related to element re-use by
MTA, leading to strange and hard-to-diagnose behavior later.27
● Restart Server/Resource: For server-side changes, restarting the resource
(stop [resourceName], start [resourceName]) or the entire server is often
necessary to apply changes and can sometimes resolve transient issues or clear
memory.7

Code Example (Debugging with outputChatBox and outputDebugString):

Lua

-- server.lua
-- Server-side script demonstrating debug output for player health
addCommandHandler("debugplayerhealth", function(playerSource, commandName,
targetPlayerName)
if nottargetPlayerName then
outputChatBox("Usage: /debugplayerhealth <player name>", playerSource, 255, 255, 0)
return
end

local targetPlayer = getPlayerFromName(targetPlayerName)


if targetPlayer then
local health = getElementHealth(targetPlayer)
-- Output to the player who used the command
outputChatBox("DEBUG: ".. getPlayerName(targetPlayer).. "'s health is: ".. health,
playerSource, 0, 255, 255)
-- Output to the server console for logging
outputConsole("DEBUG: ".. getPlayerName(targetPlayer).. "'s health checked by "..
getPlayerName(playerSource))
else
outputChatBox("DEBUG: Player '".. targetPlayerName.. "' not found.", playerSource,
255, 0, 0)
end
end)

-- client.lua
-- Client-side script demonstrating frequent debug output (use outputDebugString)
addEventHandler("onClientRender", root, function()
-- Avoid putting heavy debug logic in onClientRender as it runs every frame [27]
-- This is just for demonstration of outputDebugString
local localPlayerHealth = getElementHealth(localPlayer)
-- Using outputDebugString is better for frequent messages than outputChatBox to avoid spam
outputDebugString("Local player health: ".. localPlayerHealth)
end)

Tips for Beginners:


● Start with basic tracing using outputChatBox (for occasional messages to
players) and outputDebugString (for frequent technical output to console).
● Develop the habit of checking the server console and client F8 console for error
messages. They are the primary source of information when things go wrong.
● Invest in a good IDE with Lua syntax highlighting and completion; it will catch
many syntax errors before you even run the script.

Common Mistakes:
● Ignoring Error Messages: Many beginners overlook or misinterpret the error
messages displayed in the console, which often provide precise locations (file
and line number) of the issue.
● Over-reliance on print()/outputChatBox() in high-frequency events: Placing
frequent print() or outputChatBox() calls within functions that run every frame
(like onClientRender) can severely impact client performance and flood the
chat/console.27 Use
outputDebugString for such cases or implement conditional checks to limit
output.
● Lack of Input Validation: Assuming user input or data from other sources will
always be in the expected format. This leads to many runtime errors when
unexpected data types or values are encountered.10

Effective debugging in MTA:SA is not just about fixing bugs; it is about understanding
script behavior and performance characteristics.15 The distinct client-server
environments mean that debugging strategies must adapt to each. Debugging in a
game environment like MTA:SA extends beyond simple error correction. It involves
performance profiling to identify bottlenecks (especially in frequently called client-
side events), understanding the flow of data across the client-server boundary, and
recognizing that many perceived "game bugs" are actually logical errors in custom
scripts.27 The tools like

outputChatBox, outputDebugString, and specialized IDE extensions are tailored for


this complex environment. Beginners need to develop a systematic approach to
debugging, starting with basic print statements, learning to interpret error messages,
and gradually adopting more advanced tools. They also need to be aware that client-
side performance issues can severely impact player experience, making careful
profiling essential.

Practice Exercises (Module 2)

1. "Hello World" Resource:


○ Create a new resource folder (e.g., hello_world_resource).
○ Inside, create meta.xml, server.lua, and client.lua.
○ In meta.xml, define the resource info and link both server.lua (type server)
and client.lua (type client).
○ In server.lua, add a server-side onPlayerJoin event handler that outputs
"Hello from server!" to the joining player's chat.
○ In client.lua, add a client-side onClientResourceStart event handler (attached
to resourceRoot) that outputs "Hello from client!" to the local player's chat.
○ Start the resource on your server and verify both messages appear when you
join.
2. Client-Server Echo:
○ Expand on the previous resource.
○ In client.lua, create a command /echo <message> that triggers a server event
(triggerServerEvent). Pass the typed message as an argument.
○ In server.lua, add addEvent for this new event (remember
remoteTriggerable=true). Create an addEventHandler for it that receives the
message.
○ The server-side handler should then outputChatBox the message back to the
client who sent it, prefixed with "Server received: ".
○ Test by typing /echo Your message here in-game.
3. Debugging Challenge:
○ Create a server-side script with a command /buggymath <number>.
○ The command handler should attempt to divide the given <number> by a
global variable divisor which is initially nil.
○ Observe the error in the server console.
○ Fix the error by:
■ Initializing divisor to a non-zero number.
■ Adding input validation to ensure <number> is actually a number.
■ Adding a check to prevent division by zero.
○ Use outputChatBox to inform the player of any errors or the successful result.

📙 Module 3: Player Functions

1. Handling player joins and quits

Managing player connections and disconnections is a fundamental aspect of any


multiplayer game mode. MTA:SA provides specific server-side events that allow
scripts to react precisely when a player joins or leaves the server.
● onPlayerJoin (Server-side Event):
○ This event is triggered every time a player successfully connects to the
server.26
○ It has no parameters, but the source element within the event handler refers
to the player who just joined.26
○ The onPlayerJoin event is not cancellable, meaning its default action (the
player joining) cannot be prevented by a script.26
○ Typical Use Cases: This event is ideal for initializing player-specific data
(e.g., loading their money, inventory, or position from a database), setting
their initial spawn point, displaying a welcome message, or applying default
game mode settings.26
● onPlayerQuit (Server-side Event):
○ This event is triggered when a player disconnects from the server.30
○ The source element within the event handler refers to the player who is
quitting.30
○ It provides three parameters: quitType (how the player left, e.g., "Quit",
"Kicked", "Banned", "Timed out"), reason (if kicked/banned), and
responsibleElement (who was responsible for a kick/ban).30
○ Like onPlayerJoin, this event is not cancellable.30
○ Typical Use Cases: onPlayerQuit is crucial for saving player data back to a
database, cleaning up any temporary data or elements associated with the
player (to prevent memory leaks), and broadcasting a departure message to
other players.26

Code Example (Player Join/Quit Messages and Data Cleanup):

Lua

-- server.lua

-- Server-side: onPlayerJoin handler


-- This function runs when a player connects to the server.
addEventHandler("onPlayerJoin", root, function()
local playerName = getPlayerName(source) -- Get the name of the joining player
local serverName = getServerName() -- Get the server's name

-- Send a green welcome message to the joining player


outputChatBox("Welcome ".. playerName.. " to ".. serverName.. "!", source, 0, 255, 0,
true)

-- Log the join event to the server console for administrative purposes
outputConsole(playerName.. " has joined the server.")

-- Initialize player data (conceptual, would involve database in a real RPG)


playerData[source] = { money = 500, level = 1, experience = 0 }
outputChatBox("You start with $".. playerData[source].money.. " and are Level "..
playerData[source].level.. ".", source, 255, 255, 0)
end)

-- Server-side: onPlayerQuit handler


-- This function runs when a player disconnects from the server.
addEventHandler("onPlayerQuit", root, function(quitType, reason, responsibleElement)
local playerName = getPlayerName(source) -- Get the name of the quitting player
local quitReason = reason or "No specific reason" -- Handle cases where 'reason' might be
false/nil

-- Broadcast an orange message to all players about the departure


outputChatBox(playerName.. " has left the server (".. quitType.. "). Reason: "..
quitReason, root, 255, 100, 0, true)

-- Log the quit event to the server console


outputConsole(playerName.. " has quit. Type: ".. quitType.. ", Reason: ".. quitReason)

-- IMPORTANT: Clean up player-specific data to prevent memory leaks


-- In a real system, player data would be saved to a database here
if playerData[source] then
-- savePlayerData(source, playerData[source]) -- Conceptual save function
playerData[source] = nil -- Remove data from memory
outputConsole("Cleaned up data for ".. playerName)
end
end)

Tips for Beginners:


● Use onPlayerJoin to initialize player-specific data (e.g., load from a database, set
default money/skin, assign to a team).
● Use onPlayerQuit to save player data (if using a database) and, critically, to clean
up any temporary data or elements associated with the player in memory to
prevent memory leaks over time.26

Common Mistakes:
● Memory Leaks: Forgetting to clear or remove player-specific data from tables or
other data structures when a player quits can lead to accumulated data in
memory, causing server performance degradation and eventual crashes.26
● Attempting to Cancel: Trying to use cancelEvent() on onPlayerJoin or
onPlayerQuit will have no effect, as these events are explicitly non-cancellable.26
● Client-side Logic: Placing core player data initialization or saving logic on the
client-side. This data would not be synchronized or persistent and would be
vulnerable to client-side manipulation.

onPlayerJoin and onPlayerQuit 26 are not just informational events; they are critical
lifecycle hooks for managing player data persistence and server resource allocation.
In a dynamic multiplayer environment, players are constantly joining and leaving.
These events provide the precise moments when a player element is created and
destroyed. This is the ideal time to load player-specific data (e.g., from a database)
when they join and save it back when they leave, ensuring data persistence. It is also
crucial for memory management, as unreferenced data can lead to leaks, as
demonstrated by the explicit cleanup in the onPlayerQuit example.26 Developers
should therefore always consider what data needs to be loaded/saved and what
resources need to be allocated/deallocated when a player connects/disconnects.
This is fundamental for stable and scalable game modes, especially complex ones like
RPGs.

2. Setting player health, money, and skins

MTA:SA provides a comprehensive set of functions to directly manipulate core player


attributes, which are essential for developing custom game mechanics, administration
tools, or RPG systems.
● Health:
○ setElementHealth(thePlayer, newHealth): This function is used to set the
health of a specified element, which can include a player, a ped (NPC), an
object, or a vehicle.39
○ newHealth: A floating-point number representing the desired health value.
○ Note: While setPedMaxHealth exists, setElementHealth is the more general
and commonly used function to set current health.40
○ Server-side Authority: Changes to player health made server-side are
synchronized across all clients, ensuring consistency.41
● Money:
○ setPlayerMoney(thePlayer, amount): This function sets a player's money to a
specific absolute value, regardless of their current amount.42 It can also be
used with
root as thePlayer to set money for all players on the server.42
○ givePlayerMoney(thePlayer, amount): Adds a specified amount of money to a
player's current balance.43
○ takePlayerMoney(thePlayer, amount): Subtracts a specified amount of money
from a player's current balance.44
Server-side Only: It is critical to use these functions server-side. Attempting

to modify player money client-side will only change the visual display for that
player and will not be synchronized with the server, leading to
desynchronization and potential exploits.42
● Skins:
○ setElementModel(thePlayer, skinID): This is the recommended function for
changing a player's (or ped's) visual model, i.e., their skin.45
○ skinID: An integer representing a valid GTA:SA player model ID (character skin
ID).45
○ Deprecation Warning: The older setPedSkin and getPlayerSkin functions are
deprecated; setElementModel and getElementModel should be used
instead.45

Code Example (Setting Player Attributes):

Lua

-- server.lua

-- Command to set player health


addCommandHandler("sethealth", function(playerSource, commandName, targetPlayerName,
amount)
-- Validate input: check if player name and amount are provided
if not targetPlayerName or not amount then
outputChatBox("Usage: /sethealth <player name> <amount>", playerSource, 255, 255, 0)
return
end

local targetPlayer = getPlayerFromName(targetPlayerName) -- Get the player element


by name
local healthAmount = tonumber(amount) -- Convert amount to a number

-- Check if player exists and amount is a valid number


if targetPlayer and healthAmount then
setElementHealth(targetPlayer, healthAmount) -- Set the player's health
outputChatBox(getPlayerName(targetPlayer).. "'s health set to ".. healthAmount.. ".",
playerSource, 0, 255, 0)
else
outputChatBox("Invalid player name or health amount specified.", playerSource, 255, 0, 0)
end
end)

-- Command to give player money


addCommandHandler("givemoney", function(playerSource, commandName, targetPlayerName,
amount)
if not targetPlayerName or not amount then
outputChatBox("Usage: /givemoney <player name> <amount>", playerSource, 255, 255, 0)
return
end

local targetPlayer = getPlayerFromName(targetPlayerName)


local moneyAmount = tonumber(amount)

iftargetPlayer and moneyAmount then


givePlayerMoney(targetPlayer, moneyAmount) -- Give money to the player
outputChatBox(getPlayerName(targetPlayer).. " received $".. moneyAmount.. ".",
playerSource, 0, 255, 0)
-- In a real RPG, you'd also update a database here
else
outputChatBox("Invalid player name or money amount specified.", playerSource, 255, 0, 0)
end
end)

-- Command to set player skin


addCommandHandler("setskin", function(playerSource, commandName, targetPlayerName,
skinID)
if not targetPlayerName or not skinID then
outputChatBox("Usage: /setskin <player name> <skin ID>", playerSource, 255, 255, 0)
return
end

local targetPlayer = getPlayerFromName(targetPlayerName)


local newSkinID = tonumber(skinID)
targetPlayer and newSkinID then
if
setElementModel(targetPlayer, newSkinID) -- Use setElementModel to change skin
outputChatBox(getPlayerName(targetPlayer).. "'s skin set to ID ".. newSkinID.. ".",
playerSource, 0, 255, 0)
else
outputChatBox("Invalid player name or skin ID specified.", playerSource, 255, 0, 0)
end
end)

Tips for Beginners:


● Always validate user input (e.g., using tonumber()) when dealing with commands
that modify player statistics. This prevents runtime errors if a player types text
instead of a number.
● Prioritize using setElementModel for changing player skins, as setPedSkin is
deprecated and may not be supported in future MTA:SA versions.
● Remember that any changes to core player attributes (health, money, skin)
should ideally be performed server-side to ensure synchronization and prevent
client-side exploits.

Common Mistakes:
● Invalid Input: Not converting string arguments from commands (like amount or
skinID) to numbers using tonumber(), leading to runtime errors when operations
are attempted on incorrect data types.
● Using Deprecated Functions: Relying on older, deprecated functions like
setPedSkin, which can lead to compatibility issues or unexpected behavior in
newer MTA:SA versions.
● Client-side Manipulation: Attempting to set player money or other critical
attributes client-side. This results in desynchronization between the client and
server, where the client sees a change that is not reflected or validated by the
server, creating a vulnerability for cheating.42
● Negative Money: Setting negative values with setPlayerMoney can sometimes
lead to unexpected results, including players receiving very large amounts of
money instead of having money deducted.42

The direct manipulation of player attributes (health, money, skin) via server-side
functions 39 underscores MTA:SA's

single source of truth for core game state, which is the server. The documentation
consistently indicates that functions modifying fundamental player attributes are
either server-side or, if client-side, explicitly state that they will not synchronize with
the server.42 This reinforces the server's role as the authoritative source for game
state. For instance, if a player's money is set client-side, it is only a visual change for
that player; the server's record remains unchanged, leading to desynchronization and
potential exploits. For reliable and cheat-proof game modes, all critical player data
manipulation

must occur on the server. Client-side functions should only be used for visual
feedback or local effects that do not impact global game state. This is a crucial
security and consistency principle that developers must adhere to.

3. Player controls and key bindings

MTA:SA offers robust capabilities to programmatically control player input and bind
custom actions to specific keys. This allows developers to fundamentally alter how
players interact with the game world, enabling unique gameplay mechanics and user
interfaces.
● toggleControl(thePlayer, control, enabled):
○ This function enables or disables the use of a specific default GTA control for
a given player.47
○ thePlayer: The player element whose control ability is being toggled.
○ control: A string representing the name of the GTA control (e.g., "fire",
"accelerate", "jump", "forwards", "brake_reverse").48
○ enabled: A boolean value (true to enable, false to disable).47
○ Note: If disabling weapon fire, it's recommended to disable both the fire
control and the corresponding control action.47
● setControlState(thePlayer, control, state):
○ This function forces a specific GTA control to be in a "pressed" (true) or
"released" (false) state for a player, as if the player were holding or releasing
the key.49
○ thePlayer: The player whose control state is being set.
○ control: The name of the GTA control.
○ state: A boolean (true for pressed, false for released).49
○ Deprecation Warning: Client-side usage of setControlState is deprecated.
Developers should use setPedControlState and getPedControlState for
client-side control manipulation.49
● Control Names: MTA:SA provides a comprehensive list of predefined GTA
control names that can be used with toggleControl and setControlState.48 These
include movement (
forwards, backwards, left, right), combat (fire, aim_weapon), vehicle controls
(accelerate, brake_reverse, handbrake), and more.48
● bindKey(thePlayer, key, keyState, handlerFunction,...):
○ This function allows developers to bind a custom Lua function
(handlerFunction) to a specific key press or release event for a player.
○ thePlayer: The player for whom the key binding is created.
○ key: The name of the key (e.g., "W", "mouse1", "enter").36
○ keyState: "down" for key press, "up" for key release.
○ handlerFunction: The Lua function to execute when the key is pressed or
released.
● onClientKey (Client-side Event):
○ This event is triggered on the client whenever a user presses or releases a
keyboard or mouse button.36
○ It provides parameters like button (the key pressed) and pressOrRelease (a
boolean: true for press, false for release).36
○ This event can be canceled using cancelEvent() when pressOrRelease is true,
preventing any default GTA or MTA binds associated with that key from
triggering.36 This is powerful for overriding native game behavior.

Code Example (Toggle Control & Key Binding):

Lua

-- server.lua

-- Server-side: Disable player movement when they join


-- This can be used for login screens or specific game states
addEventHandler("onPlayerJoin", root, function()
-- Disable basic movement controls for the joining player
toggleControl(source, "forwards", false)
toggleControl(source, "backwards", false)
toggleControl(source, "left", false)
toggleControl(source, "right", false)
outputChatBox("Your movement is currently disabled! Type /enablemove to enable.", source,
255, 0, 0)
end)

-- Server-side: Command to re-enable player movement


addCommandHandler("enablemove", function(playerSource)
toggleControl(playerSource, "forwards", true)
toggleControl(playerSource, "backwards", true)
toggleControl(playerSource, "left", true)
toggleControl(playerSource, "right", true)
outputChatBox("Your movement is now enabled!", playerSource, 0, 255, 0)
end)

-- client.lua

-- Client-side: Bind 'X' key to send a message to the server


-- This demonstrates a custom action triggered by a key press
bindKey("X", "down", function()
outputChatBox("You pressed X! Sending a signal to the server...", 255, 255, 0)
-- Trigger a server event for server-side action (e.g., opening a menu, using an ability)
triggerServerEvent("onClientPressedX", localPlayer)
end)

-- Server-side counterpart for onClientPressedX (in server.lua)


addEvent("onClientPressedX", true) -- Register as remotely triggerable
addEventHandler("onClientPressedX", root, function()
outputChatBox(getPlayerName(client).. " pressed X!", root, 200, 200, 200)
end)

-- Client-side: Prevent player from jumping when in a specific "no-jump zone"


-- This is a conceptual example; 'inNoJumpZone' would be set by other game logic (e.g., colshapes)
localinNoJumpZone = false -- Assume this is managed by other client-side logic
addEventHandler("onClientKey", root, function(button, pressOrRelease)
if button == "jump" and pressOrRelease and inNoJumpZone then
outputChatBox("You cannot jump in this area!", 255, 0, 0)
cancelEvent() -- Prevent the default jump action associated with the 'jump' key
end
end)

Tips for Beginners:


● Use toggleControl to temporarily disable or enable default GTA controls for
specific gameplay scenarios (e.g., during a cutscene, while in a menu). Always
remember to re-enable them when appropriate.
● bindKey is powerful for creating custom player actions and integrating them
seamlessly with your scripts.
● Be cautious with onClientKey. While versatile, it fires very frequently (every key
press/release). Avoid placing complex or performance-heavy logic directly within
its handler, especially if attached to root.27 If complex logic is needed, trigger a
server event or use a timer to defer execution.

Common Mistakes:
● Forgetting to Re-enable Controls: Disabling controls with toggleControl(...,
false) without a corresponding toggleControl(..., true) can leave players stuck or
unable to perform basic actions, leading to frustration.
● Client-side setControlState: Using the deprecated client-side setControlState
instead of setPedControlState can lead to compatibility issues.
● Inefficient onClientKey Usage: Placing too much logic inside an onClientKey
handler, especially if attached to root, can cause significant client-side
performance issues (lag) due due to its high call frequency.27
● Not Using cancelEvent(): If the goal is to completely override a default key
action (e.g., prevent jumping), cancelEvent() must be called within the
onClientKey handler.36 Without it, both your custom logic and the default game
action might occur.

The control manipulation functions (toggleControl, setControlState, bindKey) 47 offer


deep programmatic control over player interaction, allowing developers to

fundamentally alter the gameplay experience beyond what GTA:SA natively provides.
These functions are not just minor tweaks; they enable scripters to create entirely
new gameplay mechanics. The ability to disable default movement, force
acceleration, or override native key behaviors means the game's fundamental input
system can be repurposed.36 This is how custom gamemodes can create unique
player experiences, such as a "no-jump zone," a "stuck in car" scenario, or custom
key-activated abilities. This level of control is crucial for designing custom
gamemodes where player actions need to be restricted or augmented to fit specific
gameplay rules. It also highlights the importance of careful design to avoid frustrating
players by unexpectedly overriding their controls without clear feedback.

4. Chat commands (addCommandHandler)

Chat commands provide a direct and intuitive way for players to interact with server-
side scripts by typing commands into the in-game chat box or console. This
mechanism is fundamental for implementing administrative tools, player abilities, and
various game mode features.
● addCommandHandler(commandName, handlerFunction,, [restricted=true]):
○ This function registers a Lua function (handlerFunction) to be executed when
a specific commandName is typed by a player.50
○ commandName: The string that players will type (e.g., "hello", "spawncar").
○ handlerFunction: The Lua function that will be called when the command is
used.
○ caseSensitive: An optional boolean (default false). If true, the command must
be typed with exact casing (e.g., /Hello is different from /hello).50
○ restricted: An optional boolean (default true). If true, the command's usage is
subject to MTA's Access Control List (ACL) permissions.50 This is crucial for
securing administrative commands.
● Handler Function Parameters: The function assigned as a command handler
receives specific parameters automatically 50:
○ playerSource: The player element who triggered the command. If the
command was issued from the server console (e.g., by an administrator), this
parameter will be false.50
○ commandName: The actual name of the command that was triggered (useful
if one function handles multiple commands).50
○ arg1, arg2,...: Each word typed after the commandName is passed as a
separate string argument.50 If no value is provided for an argument, its
variable will contain
nil.50
○ Variable Arguments (...): To capture all words after the command into a
single table, the ... (vararg) expression can be used as the last parameter in
the function definition.50 This is particularly useful for commands that take a
sentence as input (e.g.,
/sayall This is a message). The arguments can then be accessed as a table:
local args = {...}.50
● ACL Integration: For commands that perform sensitive actions (e.g., kicking
players, giving large amounts of money), it is vital to configure ACL permissions.50
This ensures that only authorized users (e.g., administrators) can use them.
Permissions are typically defined in
acl.xml under specific groups (e.g., <right name="command.killEveryone"
access="true"></right>).7

Code Example (Basic Chat Command and Variable Arguments):

Lua

-- server.lua

-- Server-side: Simple /hello command


-- This command will respond to the player who types it.
addCommandHandler("hello", function(playerSource, commandName)
if playerSource then -- Check if the command was triggered by a player (not the server console)
outputChatBox("Hello, ".. getPlayerName(playerSource).. "!", playerSource, 0, 255,
0) -- Green message to the player
else
outputConsole("The 'hello' command was used from the server console.")
end
end)

-- Server-side: /sayall command with variable arguments


-- This command allows a player to broadcast a message to everyone on the server.
addCommandHandler("sayall", function(playerSource, commandName,...)
if playerSource then -- Ensure it's a player using the command
local messageArgs = {...} -- Collect all arguments after the command into a table
if #messageArgs > 0 then -- Check if any message was provided
local fullMessage = table.concat(messageArgs, " ") -- Join the arguments into a single
string
outputChatBox(" ".. getPlayerName(playerSource).. ": ".. fullMessage, root, 255,
255, 0) -- Yellow announcement to all
else
outputChatBox("Usage: /sayall <your message>", playerSource, 255, 0, 0) -- Red usage
message to the player
end
else
outputConsole("The 'sayall' command can only be used by players, not from the console.")
end
end)

-- Server-side: /kickplayer command (conceptual, would require ACL setup)


-- This command demonstrates handling multiple specific arguments and permission checks.
addCommandHandler("kickplayer", function(playerSource, commandName, targetPlayerName,...)
-- First, check if the playerSource has permission to use this command (ACL check)
-- This is a placeholder; real ACL check would use hasObjectPermissionTo or isObjectInACLGroup
if not playerSource or not hasObjectPermissionTo(playerSource, "command.kickplayer",
false) then
outputChatBox("You do not have permission to use this command.", playerSource, 255, 0,
0)
return
end

targetPlayerName then
if not
outputChatBox("Usage: /kickplayer <player name> [reason]", playerSource, 255, 255, 0)
return
end

local targetPlayer = getPlayerFromName(targetPlayerName)


if targetPlayer then
local reasonArgs = {...}
local kickReason = #reasonArgs > 0 and table.concat(reasonArgs, " ") or "No reason
specified."

kickPlayer(targetPlayer, kickReason) -- MTA:SA function to kick a player


outputChatBox(getPlayerName(targetPlayer).. " has been kicked. Reason: "..
kickReason, root, 255, 100, 0)
outputConsole(getPlayerName(playerSource).. " kicked "..
getPlayerName(targetPlayer).. " for: ".. kickReason)
else
outputChatBox("Player '".. targetPlayerName.. "' not found.", playerSource, 255, 0, 0)
end
end, false, true) -- 'false' for case-insensitive, 'true' for restricted (ACL check)

Tips for Beginners:


● Always include clear usage instructions for your commands, especially if they
require arguments. This improves user experience.
● Validate arguments received from commands (e.g., using tonumber() for
numerical inputs, checking for nil or empty strings) to prevent script errors if
players type unexpected input.
● For administrative or sensitive commands, always integrate ACL permissions to
ensure only authorized users can execute them. This is critical for server security.

Common Mistakes:
● Ignoring playerSource: Not checking if playerSource is false when a command
can be triggered from the server console. This can lead to errors if console-only
actions are attempted on a non-existent player.
● Lack of Argument Validation: Assuming arguments will always be in the correct
format or type. This is a common source of runtime errors if a player provides
invalid input (e.g., typing text where a number is expected).
● Hardcoding Permissions: Instead of using MTA's built-in ACL system, some
developers might try to implement custom permission checks, which are often
less secure and harder to manage than ACL.
● Command Conflicts: Using command names that are already hardcoded in
MTA:SA (e.g., /login) or used by other resources can lead to unexpected
behavior.

addCommandHandler 50 is the primary interface for

player-initiated server-side interactions, enabling extensible and user-friendly


gameplay commands. Its design, with parameters like playerSource and the ability to
handle variable arguments (...), reflects the need for flexible input and precise user
identification. This function effectively serves as a user-facing API for server-side
scripts. The capability to capture multiple arguments into a table (...) is crucial for
commands that take sentences or multiple parameters (e.g., /kick PlayerName Reason
for kick), allowing for rich and detailed player input. The playerSource parameter is
vital for identifying who issued the command, which is fundamental for applying
effects to the correct player and for performing permission checks via ACL. Chat
commands are a cornerstone of interactive MTA:SA gamemodes. Developers should
design commands that are intuitive, robust against invalid input, and properly secured
with ACL for administrative functions. This is how players feel connected to and can
influence the game world, making it a dynamic and responsive environment.

Practice Exercises (Module 3)

1. Player Spawner Command:


○ Create a server-side command /spawnme.
○ When a player types this, get their current position and then use spawnPlayer
to respawn them at that location, giving them a random skin ID (use
math.random(0, 288) for a valid range of skins).
○ Output a chat message to the player confirming their spawn and new skin.
2. Money Transfer Command:
○ Create a server-side command /pay <targetPlayerName> <amount>.
○ Validate that both targetPlayerName and amount are provided and that
amount is a positive number.
○ Check if the playerSource has enough money (you can store player money in
a simple Lua table for this exercise, or use getPlayerMoney).
○ If valid, takePlayerMoney from playerSource and givePlayerMoney to
targetPlayer.
○ Output chat messages to both players confirming the transaction.
3. Toggle Controls Command:
○ Create a server-side command /togglejump.
○ This command should toggle the jump control state for the player who uses
it.
○ Output a message to the player indicating if jumping is now enabled or
disabled.
○ Advanced: Create a client-side command /togglehud that uses
toggleControl to hide/show the entire HUD (e.g., radar, health_armor).
Remember to use localPlayer for client-side controls.

📒 Module 4: Vehicle Functions


1. Spawning vehicles with Lua

Vehicles are central to the Grand Theft Auto: San Andreas experience, and MTA:SA
provides robust Lua functions to dynamically create and manage them within the
game world.
● createVehicle(modelID, x, y, z, [rx, ry, rz, color, upgrades,...]):
○ This is the primary function for spawning a vehicle element at a specified
location.51
○ modelID: A required integer representing the GTA:SA vehicle model ID (e.g.,
411 for Infernus, 432 for Rhino).51
○ x, y, z: The required floating-point coordinates for the vehicle's position.51 The
z value (vertical axis) should be set slightly above the ground to prevent the
vehicle from spawning underground or sinking.51 A value of
z + 1 or z + 2 relative to the ground is often sufficient.
○ rx, ry, rz: Optional floating-point values for the vehicle's rotation around the X,
Y, and Z axes, respectively.51
rz typically controls the heading (yaw).
○ color: Optional integer(s) or RGB values for the vehicle's color.51
○ Other optional parameters allow setting upgrades, paintjobs, license plate
text, health, siren state, and more.51
● Client-side vs. Server-side Vehicle Creation:
○ Server-side createVehicle: When createVehicle is called from a server-side
script, the vehicle is spawned in the authoritative game world. This means it is
visible to all connected players, synchronized across the network, and fully
interactive (players can enter and drive it).51 This is the standard and
recommended method for creating vehicles that are part of the shared
gameplay experience.
○ Client-side createVehicle: If createVehicle is called from a client-side
script, the vehicle is only spawned locally on that specific client's machine.51 It
will not be visible to other players, will not be synchronized, and cannot be
entered or interacted with by other players.51 Client-side vehicles are
primarily for local visual effects or testing purposes and are generally not
suitable for shared gameplay elements.51
● Performance Considerations: Creating a large number of vehicles, especially in
the same location, can cause performance issues (lag) for the server and
clients.51 Developers should manage vehicle counts efficiently.

Code Example (Spawning a Vehicle):

Lua

-- server.lua

-- Server-side: Command to spawn a vehicle in front of the player


addCommandHandler("spawncar", function(playerSource, commandName, vehicleNameOrID)
-- Input validation
if not vehicleNameOrID then
outputChatBox("Usage: /spawncar <vehicle name or ID>", playerSource, 255, 255, 0)
return
end

-- Attempt to convert input to a vehicle ID. Try as number first, then as name.
vehicleID = tonumber(vehicleNameOrID) or
local
getVehicleModelFromName(vehicleNameOrID)

if vehicleID then
-- Get player's current position and rotation
local x, y, z = getElementPosition(playerSource)
local rotZ = getElementRotation(playerSource)

-- Calculate spawn position 5 units in front of the player


-- math.rad converts degrees to radians for trigonometric functions
local spawnX = x + (math.cos(math.rad(rotZ)) * 5)
local spawnY = y + (math.sin(math.rad(rotZ)) * 5)
local spawnZ = z + 1 -- Spawn slightly above ground to prevent sinking

-- Create the vehicle


local newVehicle = createVehicle(vehicleID, spawnX, spawnY, spawnZ, 0, 0, rotZ)
newVehicle then
if
outputChatBox("Spawned ".. getVehicleNameFromModel(vehicleID).. "!",
playerSource, 0, 255, 0)
warpPedIntoVehicle(playerSource, newVehicle) -- Warp player into the new vehicle
else
outputChatBox("Failed to spawn vehicle. (Invalid ID, server limit reached, or other issue)",
playerSource, 255, 0, 0)
end
else
outputChatBox("Invalid vehicle name or ID specified.", playerSource, 255, 0, 0)
end
end)

Tips for Beginners:


● Always spawn vehicles server-side if you intend for them to be interactive,
synchronized, and visible to all players. Client-side vehicle creation is generally
for very specific local visual effects.
● Carefully consider the z coordinate when spawning vehicles. A common practice
is to add a small offset (e.g., +1 or +2) to the ground z coordinate to ensure the
vehicle spawns correctly on the surface.
● Validate the modelID from user input to ensure it corresponds to a valid vehicle.
getVehicleModelFromName can convert a vehicle name string to its ID.

Common Mistakes:
● Client-side Spawning for Shared Gameplay: A frequent mistake for beginners
is to create vehicles client-side and then wonder why other players cannot see or
interact with them. This is a direct consequence of the client-server architecture.
● Invalid Model IDs: Providing an incorrect or non-existent modelID to
createVehicle will cause the function to return false, and no vehicle will be
created.
● Spawning Too Many Vehicles: Continuously spawning vehicles without proper
cleanup or limits can quickly lead to server and client-side performance
degradation (lag).51

The distinction between client-side and server-side createVehicle 51 highlights the

critical importance of server-side authority for persistent and synchronized game


elements. Client-side vehicle creation is primarily for local visual effects, not shared
gameplay. This is a direct consequence of the client-server architecture, where the
server manages the authoritative game state, including the existence and properties
of vehicles. A client can simulate a vehicle for its own display, but without server
synchronization, that vehicle does not "exist" for other players in the shared game
world. This is a fundamental concept for multiplayer game development. Developers
must always use the server-side createVehicle for any vehicle intended to be part of
the shared game world. Client-side vehicle creation is a niche tool reserved for very
specific visual effects that do not impact shared gameplay. This understanding
directly influences how features like car dealerships, dynamic vehicle spawns, or even
vehicle persistence systems are implemented.

2. Controlling vehicle properties (speed, color, health)

MTA:SA provides an extensive set of functions to dynamically manipulate various


properties of vehicle elements, allowing for rich customization, damage systems, and
unique vehicle behaviors.
● Speed (Indirect Control):
○ There isn't a direct setVehicleSpeed function in MTA:SA. Vehicle speed is
primarily influenced by its engine state and player input.
○ setVehicleEngineState(theVehicle, state): This function is used to turn a
vehicle's engine on (true) or off (false).52 When the engine is off, the vehicle
cannot accelerate.
○ Player controls like accelerate and brake_reverse can also be manipulated
using toggleControl or setControlState to influence vehicle movement.49
● Color:
○ setVehicleColor(theVehicle, r1, g1, b1, [r2, g2, b2, r3, g3, b3, r4, g4, b4]): Sets
the vehicle's color using RGB values (0-255).51 Vehicles can have up to four
color components, though most only use two.51
○ setVehicleColor(theVehicle, p1, p2, p3, p4): Sets the vehicle's color using
standard San Andreas color palette IDs.51 Even if a vehicle has fewer than four
colors, all four palette IDs must be provided.51
● Health:
○ setElementHealth(theVehicle, newHealth): This function sets the health of a
vehicle element.39
newHealth: A floating-point number representing the desired health. A value

of 1000 typically means completely healthy.51
○ Visual Effects: Specific health thresholds trigger distinct visual effects on
vehicles:
■ 650 health: No white steam, no black smoke.
■ 450 health: 100% white steam, 50% black smoke.
■ 250 health: No white steam, 100% black smoke.
■ 249 health: Fire with large black smoke.39
● Other Common Properties:
○ setVehicleLocked(theVehicle, locked): Locks or unlocks vehicle doors (true or
false).51
○ setVehiclePlateText(theVehicle, text): Sets the text displayed on the vehicle's
license plate.51
○ setVehicleSirensOn(theVehicle, state): Turns sirens on or off for emergency
vehicles.51
○ addVehicleUpgrade(theVehicle, upgradeID): Adds a visual or performance
upgrade (e.g., NOS, spoilers).51
○ setVehiclePanelState(theVehicle, panelID, state): Controls the damage state
of specific vehicle panels (e.g., bumpers, doors, windshield).41
state ranges from 0 (undamaged) to 3 (very damaged).41
○ fixVehicle(theVehicle): Instantly repairs a vehicle to full health and fixes all
panels.
○ blowVehicle(theVehicle): Causes the vehicle to explode.

Code Example (Controlling Vehicle Properties):

Lua

-- server.lua

-- Command to set vehicle color to random RGB


addCommandHandler("randomcolor", function(playerSource)
local vehicle = getPedOccupiedVehicle(playerSource) -- Get the vehicle the player is
currently in
if vehicle then
-- Generate random RGB values
r, g, b = math.random(255), math.random(255), math.random(255)
local
setVehicleColor(vehicle, r, g, b) -- Apply the random color
outputChatBox("Vehicle color set to random RGB!", playerSource, r, g, b)
else
outputChatBox("You are not in a vehicle!", playerSource, 255, 0, 0)
end
end)

-- Command to toggle vehicle engine state (on/off)


addCommandHandler("toggleengine", function(playerSource)
local vehicle = getPedOccupiedVehicle(playerSource)
-- Check if player is in a vehicle and is the driver
if vehicle and getVehicleController(vehicle) == playerSource then
setVehicleEngineState(vehicle, not getVehicleEngineState(vehicle)) -- Toggle the
engine state
outputChatBox("Engine toggled ".. (getVehicleEngineState(vehicle) and "ON" or
"OFF").. "!", playerSource, 0, 255, 0)
else
outputChatBox("You must be the driver of a vehicle to toggle its engine!", playerSource,
255, 0, 0)
end
end)

-- Command to repair vehicle (set health to 1000)


addCommandHandler("repair", function(playerSource)
local vehicle = getPedOccupiedVehicle(playerSource)
if vehicle then
setElementHealth(vehicle, 1000) -- Set vehicle health to max
outputChatBox("Vehicle repaired!", playerSource, 0, 255, 0)
else
outputChatBox("You are not in a vehicle!", playerSource, 255, 0, 0)
end
end)

-- Command to break the front bumper of player's vehicle


addCommandHandler("breakbumper", function(playerSource)
local vehicle = getPedOccupiedVehicle(playerSource)
if vehicle then
-- PanelID 5 is front bumper for cars, state 3 is very damaged
setVehiclePanelState(vehicle, 5, 3, true, false) -- spawnFlyingComponent=true,
breakGlass=false
outputChatBox("Front bumper broken!", playerSource, 255, 150, 0)
else
outputChatBox("You are not in a vehicle!", playerSource, 255, 0, 0)
end
end)

Tips for Beginners:


● Always use getPedOccupiedVehicle(player) to retrieve the vehicle element a
player is currently in before attempting to modify its properties.
● Be aware of the visual side effects of setElementHealth on vehicles at different
health thresholds; this can be used for visual feedback in damage systems.
● Remember that setVehicleColor has two main formats (RGB and palette IDs);
ensure you use the correct one for your desired effect.

Common Mistakes:
● Direct Speed Manipulation: Trying to find a non-existent setVehicleSpeed
function. Instead, control speed indirectly via engine state, player controls, or
custom handling modifications.
● Incorrect Color Format: Mixing up RGB values with palette IDs, or not providing
the required four palette IDs when using that format, can lead to unexpected
colors or function failure.
● Client-side Property Changes: Modifying vehicle properties client-side when
they need to be synchronized for all players. Only server-side changes will be
replicated reliably across the network.
● Ignoring getVehicleController: For functions like setVehicleEngineState, it's
often important to check if the player is actually the driver of the vehicle using
getVehicleController(vehicle) == player before allowing them to manipulate it.

The granular control over vehicle properties 39 allows for highly

dynamic and interactive vehicle gameplay, from simple customization to complex


damage models and unique vehicle behaviors. The extensive API for vehicle
manipulation means developers are not limited to static vehicles. They can create
systems for vehicle customization (paint jobs, upgrades), dynamic damage and repair
systems, and even unique vehicle abilities (e.g., a vehicle that cannot be locked, or
one that automatically turns off its engine when exited). The built-in visual cues for
health (steam, smoke, fire) further enhance gameplay feedback.39 This opens up
possibilities for complex vehicle-based game modes, such as racing with damage
mechanics, car dealerships, or even survival modes where vehicle maintenance is
crucial. Developers should leverage these functions to create engaging and
immersive vehicle experiences that go beyond the default GTA:SA mechanics.

3. Attaching objects to vehicles

The ability to attach objects to vehicles significantly expands the possibilities for
custom visual elements, functional additions, and interactive gameplay mechanics in
MTA:SA. While the most direct example is attaching trailers, the underlying principles
extend to custom objects as well.
● attachTrailerToVehicle(towingVehicle, trailerVehicle):
○ This function is specifically designed to attach a trailer-type vehicle to a
trailer-towing-type vehicle.51
○ towingVehicle: The vehicle that will pull the trailer (e.g., a truck).
○ trailerVehicle: The vehicle that will be attached as a trailer.
○ Important Note: It is crucial to ensure both the towing vehicle and the trailer
vehicle are fully created and valid elements before attempting the
attachment. Using setTimer with a small delay for the trailer creation after the
towing vehicle can help ensure this.51
● General Object Attachment (Conceptual):
○ While attachTrailerToVehicle is specific, MTA:SA also provides a general
attachElements(elementToAttach, attachToElement, [x, y, z, rx, ry, rz])
function (not explicitly in provided snippets but a core MTA function). This
function allows any element (including objects created with createObject) to
be attached to another element (like a vehicle).
○ Dummies: Vehicles in GTA:SA models often have predefined "dummies" –
internal attachment points with specific names (e.g., light_front_main,
seat_front, exhaust, trailer_attach).53 These can be targeted using functions
like
setVehicleDummyPosition to precisely place attached elements relative to the
vehicle's model.53 This implies a more general system for attaching
any element to a vehicle, not just predefined trailers.
Code Example (Attaching a Trailer):

Lua

-- server.lua

-- Command to spawn a truck and a trailer, then attach them


addCommandHandler("spawntrucktrailer", function(playerSource)
local x, y, z = getElementPosition(playerSource)
local rotZ = getElementRotation(playerSource)

-- 1. Spawn the truck (Roadtrain model ID 515)


local truck = createVehicle(515, x + 5, y, z + 1, 0, 0, rotZ)
if not truck then
outputChatBox("Failed to spawn truck.", playerSource, 255, 0, 0)
return
end

-- 2. Use a timer to ensure the truck is fully created and streamed before spawning and attaching
the trailer.
-- This helps prevent potential issues where attachment fails because the parent element isn't
ready.
setTimer(function()
-- 3. Spawn the trailer (Trailer model ID 435)
local trailer = createVehicle(435, x, y, z + 1, 0, 0, rotZ)
if trailer then
attachTrailerToVehicle(truck, trailer) -- Attach the trailer to the truck
outputChatBox("Spawned truck and attached trailer!", playerSource, 0, 255, 0)
warpPedIntoVehicle(playerSource, truck) -- Warp player into the truck
else
outputChatBox("Failed to spawn trailer. Cleaning up truck.", playerSource, 255, 0, 0)
destroyElement(truck) -- Clean up the truck if trailer creation fails
end
end, 100, 1) -- 100ms delay, run once
end)
Tips for Beginners:
● When attaching elements, always ensure both the parent element (e.g., vehicle)
and the child element (e.g., trailer, object) are fully created and valid before
attempting the attachment. Using setTimer with a small delay can be a good
practice for this.
● For custom visual objects, you would typically use createObject to create the
object and then attachElements (a general MTA function) to link it to the vehicle
element. Experiment with position and rotation offsets to place the object
correctly.

Common Mistakes:
● Premature Attachment: Attempting to attach elements immediately after
createVehicle or createObject without a small delay can sometimes lead to
attachment failures, especially if the game engine hasn't fully processed the
element creation.
● Incorrect Vehicle Types: Trying to attach a trailer to a vehicle that is not a
designated "towing vehicle" or attempting to attach a non-trailer vehicle as a
trailer.
● Misunderstanding Dummies: While setVehicleDummyPosition is mentioned,
directly setting dummy positions is typically for internal engine use or very
advanced scenarios. For attaching custom objects, attachElements with
calculated offsets is more common.

The ability to attach elements (like trailers or custom objects) to vehicles 51 extends
MTA:SA's

modding capabilities beyond static models, enabling dynamic vehicle configurations


and interactive gameplay elements. This suggests a more general system for
attaching any element to a vehicle, not just predefined trailers. The concept of
"dummies" implies that vehicle models have internal, named attachment points that
scripts can target.53 This is a powerful feature for creating custom vehicle
accessories, weapons, or visual effects that move with the vehicle. Developers can
therefore create highly customized vehicle experiences, such as adding custom
spoilers, turrets, or even interactive components like mobile shops on a truck. This
enhances visual variety and opens up new gameplay mechanics that are not natively
present in GTA:SA.
4. Vehicle events

MTA:SA provides a comprehensive suite of client-side and server-side events


specifically related to vehicles. These events allow developers to create dynamic and
responsive gameplay by reacting to various vehicle-related actions, from entry and
exit to collisions and explosions.
● Common Client-side Vehicle Events: These events are triggered and handled
on the player's client.
○ onClientVehicleEnter(thePed, seat): Triggered when a player or ped finishes
entering a vehicle.54
○ onClientVehicleExit(thePed, seat): Triggered when a player or ped finishes
exiting a vehicle.54
○ onClientVehicleExplode(): Triggered when a vehicle explodes.24 This event
cannot be canceled.24
○ onClientVehicleDamage(attacker, weapon, loss, damagePosX, damagePosY,
damagePosZ, tireID): Triggered when a vehicle takes damage.54 This event
can be canceled using cancelEvent() to prevent health reduction, though
physical damage/deformation will remain.54
○ onClientVehicleCollision(theHitElement, damageImpulseMag, bodyPart,
collisionX, collisionY, collisionZ, normalX, normalY, normalZ, hitElementForce,
model): Triggered when a vehicle collides with another element or a world
object.54 This event occurs
before in-game reactions like damage or knock-offs.54
○ onClientVehicleNitroStateChange(state): Triggered when a vehicle's nitro
state changes (activated/deactivated).54
○ onClientVehicleRespawn(): Triggered when a vehicle respawns.54
○ onClientTrailerAttach(vehicle, towedBy): Triggered when a trailer becomes
attached to a towing vehicle.54
○ onClientTrailerDetach(towedBy): Triggered when a trailer detaches from its
towing vehicle.54
○ onClientVehicleStartEnter(thePed, seat, door): Triggered when a player/ped
begins the animation of entering a vehicle.54 This event can be canceled to
prevent entry.54
○ onClientVehicleStartExit(thePed, seat, door): Triggered when a player/ped
begins the animation of exiting a vehicle.54
○ onClientVehicleWeaponHit(weaponType, hitElement, hitX, hitY, hitZ, model,
materialID): Triggered when a vehicle weapon hits an element or the world.54
● Source Element: For most vehicle events, the source element within the event
handler is the vehicle itself that triggered the event (e.g., the vehicle that was
entered, exploded, or took damage).24

Code Example (Vehicle Events):

Lua

-- client.lua

-- Client-side: Update a GUI label when local player enters/exits a vehicle


vehicleStatusLabel = guiCreateLabel(10, 200, 200, 20, "Currently on foot", false)
local
showCursor(true) -- Ensure cursor is visible for GUI interaction (for testing, typically managed by
other GUI scripts)

addEventHandler("onClientVehicleEnter", getRootElement(), function(thePlayer, seat)


if thePlayer == localPlayer then -- Check if the event was for the local player
guiSetText(vehicleStatusLabel, "Currently in a ".. getVehicleName(source)) -- 'source'
is the vehicle
end
end)

addEventHandler("onClientVehicleExit", getRootElement(), function(thePlayer, seat)


if thePlayer == localPlayer then
guiSetText(vehicleStatusLabel, "Currently on foot")
end
end)

-- Client-side: Announce when any vehicle explodes nearby


addEventHandler("onClientVehicleExplode", root, function()
local vehicleName = getVehicleName(source) -- 'source' is the exploding vehicle
outputChatBox(vehicleName.. " just exploded nearby!", 255, 0, 0) -- Red message
end)

-- server.lua
-- Server-side: Turn off engine when driver exits (revisiting from Module 3)
addEventHandler("onPlayerVehicleExit", root, function(theVehicle, leftSeat, jackerPlayer)
if leftSeat == 0 and not jackerPlayer then -- If the driver exited voluntarily
setVehicleEngineState(theVehicle, false) -- Turn off the engine
outputChatBox("Engine turned off for ".. getVehicleName(theVehicle).. ".", root, 255,
150, 0)
end
end)

-- Server-side: Make SWAT Tanks (model ID 601) immune to weapon damage


-- This demonstrates using onClientVehicleDamage and cancelEvent()
addEventHandler("onClientVehicleDamage", root, function(attacker, weapon, loss, x, y, z, tire)
if (getElementModel(source) == 601) then -- Check if the damaged vehicle is a SWAT Tank
if weapon then -- Check if damage was caused by a weapon (not environmental)
cancelEvent() -- Cancel the damage event, preventing health loss
-- Optionally, inform the attacker that the tank is invulnerable
if attacker and isElement(attacker) and getElementType(attacker) == "player" then
outputChatBox("SWAT Tank is bulletproof!", attacker, 255, 255, 0)
end
end
end
end)

Tips for Beginners:


● Understand the distinction between events that trigger when an action starts
(e.g., onClientVehicleStartEnter) and when it completes (e.g.,
onClientVehicleEnter). Start events are useful for pre-checks and potential
cancellations.
● Use cancelEvent() with caution. While powerful for overriding default game
behavior (like preventing damage), ensure it aligns with your game design and
doesn't create unexpected side effects.
● Always check the MTA:SA Wiki for the specific parameters passed by each event,
as they vary significantly.22

Common Mistakes:
● Ignoring Non-Cancellable Events: Attempting to cancelEvent() on events that
are explicitly stated as non-cancellable (e.g., onClientVehicleExplode).24 This will
have no effect.
● Over-attaching Client-Side Handlers: Attaching client-side vehicle event
handlers to root without checking if thePlayer == localPlayer if the logic is only
intended for the local player. This can lead to unnecessary processing for events
that don't concern the local client.
● Not Handling nil Parameters: Some event parameters (like theAttacker in
onClientVehicleDamage or theHitElement in onClientVehicleCollision) can be nil
or false if, for example, the damage was environmental or the collision was with a
world object.54 Always include checks for
nil before attempting to use these parameters.

The comprehensive suite of vehicle events 54 provides developers with

fine-grained control over vehicle lifecycle and interactions, enabling the creation of
highly realistic and dynamic vehicle-based gameplay systems. The cancellability of
some events offers a powerful mechanism for overriding native game logic. This is not
just a basic notification system; it is a complete API for reacting to almost every
significant vehicle-related action. The ability to cancel events (e.g.,
onClientVehicleDamage) is particularly powerful, allowing developers to implement
custom damage models, invincibility, or unique vehicle behaviors that deviate from
GTA:SA's defaults.54 This is how complex systems like custom repair shops, fuel
systems, or specialized vehicle abilities are built. Developers can therefore build
highly interactive and immersive vehicle systems. For example, a script could track
vehicle damage, require repairs, or implement custom physics based on collision
events. The level of detail provided by event parameters (e.g.,

damagePosX, damagePosY, damagePosZ for damage events) allows for very precise
and realistic scripting.

Practice Exercises (Module 4)

1. Vehicle Spawner GUI:


○ Create a simple client-side GUI with a button.
○ When the button is clicked, trigger a server event, passing a vehicle model ID
(e.g., 411 for Infernus) and the local player's current position to the server.
○ On the server, receive this event and use createVehicle to spawn the
specified vehicle at the player's location.
○ Advanced: Add an edit box to the GUI where the player can type a vehicle ID
or name, and pass this input to the server.
2. Self-Destruct Vehicle:
○ Create a server-side command /boom.
○ When a player uses this command while in a vehicle, use blowVehicle to make
their current vehicle explode.
○ Add a client-side event handler for onClientVehicleExplode (attached to
root). This handler should output a chat message "A vehicle just went
BOOM!" when any vehicle explodes.
3. Custom Vehicle Entry/Exit Messages:
○ Create client-side onClientVehicleEnter and onClientVehicleExit event
handlers.
○ These handlers should check if thePlayer is localPlayer.
○ If so, output a custom chat message (e.g., "You entered a [VehicleName]!" or
"You exited a vehicle!") using getVehicleName(source).

📔 Module 5: GUI in MTA:SA

MTA:SA provides a comprehensive set of client-side functions for creating Graphical


User Interfaces (GUIs) that can significantly enhance player interaction and game
mode functionality. These GUIs are rendered directly on the player's screen, separate
from the 3D game world.

1. Creating a basic GUI

MTA:SA utilizes the freeware CEGUI system, which replaces the original Grand Theft
Auto GUI, allowing developers to draw custom widgets for in-game user interaction.
All GUI elements are created client-side and are managed within a hierarchical
structure, where elements can have parent-child relationships.
● guiCreateWindow(x, y, width, height, titleBarText, [relative=false]):
○ This is the foundational function for creating a new GUI window.56 Windows
serve as containers for other GUI elements and do not have a parent element
themselves.56
○ x, y, width, height: Floating-point numbers defining the position and size of
the window.
○ relative: If true, x, y, width, height are interpreted as fractions of the screen
size (0.0 to 1.0). If false, they are absolute pixel coordinates.56 Using
relative=true is highly recommended for GUIs that scale correctly across
different screen resolutions.
○ titleBarText: The text displayed in the window's title bar.56
○ Windows can be set as non-movable (guiWindowSetMovable) and non-
sizable (guiWindowSetSizable) for fixed UI elements.56
● showCursor(state):
○ This function is crucial for GUI interaction. It shows or hides the mouse
cursor.57 The cursor must be visible for players to interact with GUI elements.
● GUI Element Hierarchy: GUI elements are organized in a tree-like structure. A
parent element defines the coordinate system for its children. If a parent is
moved or hidden, its children move or hide with it.

Code Example (Basic GUI Window):

Lua

-- client.lua

local myWindow = nil -- Declare global/resource-scope variable for the window

-- Function to create the GUI window


function createBasicGUI()
-- Get screen dimensions to center the window
local screenWidth, screenHeight = guiGetScreenSize()
local windowWidth = 400
local windowHeight = 300
local windowX = (screenWidth - windowWidth) / 2
local windowY = (screenHeight - windowHeight) / 2

-- Create the window (absolute positioning for simplicity in this example)


myWindow = guiCreateWindow(windowX, windowY, windowWidth, windowHeight,
"My First GUI Window", false)

-- Make the window non-movable and non-sizable for a fixed UI


guiWindowSetMovable(myWindow, false)
guiWindowSetSizable(myWindow, false)

-- Show the mouse cursor so the player can interact with the GUI
showCursor(true)

outputChatBox("Basic GUI window created. Press F8 to close console and see it.", 0, 255, 255)
end

-- Command to open the GUI


addCommandHandler("openbasicgui", function()
if not myWindow then -- Only create if it doesn't exist
createBasicGUI()
else
guiSetVisible(myWindow, true) -- Show if already created but hidden
showCursor(true)
end
end)

-- Command to close the GUI


addCommandHandler("closebasicgui", function()
if myWindow then
guiSetVisible(myWindow, false) -- Hide the window
showCursor(false) -- Hide cursor when GUI is not active
end
end)

-- Add an event handler to create the GUI when the resource starts (optional, for auto-display)
-- addEventHandler("onClientResourceStart", resourceRoot, createBasicGUI)

Tips for Beginners:


● Always use relative=true for GUI element positioning and sizing if you want your
GUI to automatically scale and look good on different screen resolutions.
Calculate positions and sizes as fractions (e.g., 0.5 for 50% width).
● Remember to use showCursor(true) when your GUI is active and
showCursor(false) when it's hidden, so players can interact with it and then
resume normal game controls.

Common Mistakes:
● Invisible GUI: Forgetting to call showCursor(true) when the GUI is meant to be
interactive, making it impossible for the player to click on elements.
● Hardcoded Pixel Positions: Using absolute pixel coordinates for all GUI
elements, which leads to GUIs that look distorted or out of place on screens with
different resolutions.
● Window as Child: Attempting to set a parent for guiCreateWindow. Windows are
root-level GUI elements and cannot have a parent.56

2. Buttons, Labels, and Windows

Beyond the basic window, MTA:SA provides functions to create common GUI
elements like buttons and labels, which are essential for building interactive user
interfaces.
● Windows (guiCreateWindow): As discussed, windows act as the primary
containers for other GUI elements.56 They provide a draggable and sizable frame
(unless disabled) for your UI.
● Buttons (guiCreateButton):
○ guiCreateButton(x, y, width, height, text, [relative=false], [parent=nil]):
Creates a clickable button.58
○ text: The string displayed on the button.58
○ parent: The GUI element (e.g., a window or panel) to which the button is
attached.58 Its position and size will be relative to this parent if
relative is true.58
● Labels (guiCreateLabel):
○ guiCreateLabel(x, y, width, height, text, [relative=false], [parent=nil]): Creates
a static text display that cannot be edited by the user.59
○ text: The string content of the label.59
○ Labels are useful for displaying information, titles, or instructions.59 Font size
is not directly supported; instead, you change the font itself using
guiSetFont.59
Code Example (Window with Button and Label):

Lua

-- client.lua

local myWindow = nil


local myButton = nil
local myLabel = nil

function createInteractiveGUI()
-- Create the window (using relative positioning for the window itself)
screenWidth, screenHeight = guiGetScreenSize()
local
myWindow = guiCreateWindow(screenWidth * 0.25, screenHeight * 0.25,
screenWidth * 0.5, screenHeight * 0.5, "Interactive GUI", false)
guiWindowSetMovable(myWindow, false)
guiWindowSetSizable(myWindow, false)

-- Create a label inside the window (relative to the window)


myLabel = guiCreateLabel(0.1, 0.1, 0.8, 0.2, "Click the button below!", true, myWindow)
guiLabelSetHorizontalAlign(myLabel, "center") -- Center the text horizontally
guiLabelSetVerticalAlign(myLabel, "center") -- Center the text vertically
guiSetFont(myLabel, "default-bold-small") -- Set a bold font

-- Create a button inside the window (relative to the window)


myButton = guiCreateButton(0.3, 0.7, 0.4, 0.15, "Click Me!", true, myWindow)

-- Show the cursor


showCursor(true)

outputChatBox("Interactive GUI created. Click the button!", 0, 255, 255)


end

-- Command to open the GUI


addCommandHandler("openinteractivegui", function()
if not myWindow then
createInteractiveGUI()
else
guiSetVisible(myWindow, true)
showCursor(true)
end
end)

-- Command to close the GUI


addCommandHandler("closeinteractivegui", function()
if myWindow then
guiSetVisible(myWindow, false)
showCursor(false)
end
end)

Tips for Beginners:


● Design your GUI hierarchy carefully. Placing elements inside appropriate parent
containers (windows, panels) simplifies positioning and management.
● Use guiSetFont to control the appearance of text on labels and buttons. Explore
available fonts on the MTA:SA Wiki.

Common Mistakes:
● Overlapping Elements: Not calculating positions and sizes correctly, leading to
GUI elements overlapping or extending beyond their parent containers.
● Unreadable Text: Using default fonts or colors that make text difficult to read
against the background.
● Missing Parent: Creating GUI elements without specifying a parent, which can
make their positioning unpredictable or cause them to appear outside the
intended window.

3. Handling GUI events (onClick, onChange)

Interactive GUIs require event handling to respond to user input. MTA:SA provides
specific client-side events for GUI elements, allowing scripts to react to clicks, text
changes, and other interactions.
● onClientGUIClick(button, state, absoluteX, absoluteY):
○ This event is triggered when any GUI element is clicked.25
○ button: The mouse button used ("left", "right", "middle").25
○ state: The state of the mouse button ("down" when pressed, "up" when
released).25 Note: currently, only the
"up" state is reliably supported for this event.25
○ absoluteX, absoluteY: The 2D pixel coordinates of the mouse cursor on the
screen when the click occurred.25
○ Source Element: The source element for this event is the GUI element that
was clicked.25
○ Propagation: If the clicked GUI element has a parent, onClientGUIClick will
also trigger for the parent. This propagation can be prevented by setting
propagate=false in addEventHandler.25
● onClientGUIChanged(element):
○ This event is fired when the text content of a memo (multi-line text box) or an
editbox (single-line text input) changes.60 This can happen either due to user
input or programmatic changes via
guiSetText.60
○ element: The GUI element (memo or editbox) whose text content changed.60
○ Source Element: The source element for this event is the element that was
changed.60
● onClientGUIAccepted(editBox):
○ This event is triggered specifically when the Enter key is pressed while an
editbox has input focus.61
○ editBox: The editbox element that had focus when Enter was pressed.61
○ Source Element: The source element is the editbox itself.61
● onClientGUITabSwitched(selectedTab):
○ Triggered when a user switches between tabs in a guiCreateTabPanel.62
○ selectedTab: The tab element that was newly selected.62
○ Note: When adding the event handler on the tab panel, propagate must be
true.62

Code Example (Handling GUI Events):

Lua
-- client.lua

local myWindow = nil


local outputLabel = nil
local inputEditBox = nil
local submitButton = nil

function createGUIWithEvents()
screenWidth, screenHeight = guiGetScreenSize()
local
myWindow = guiCreateWindow(screenWidth * 0.25, screenHeight * 0.25,
screenWidth * 0.5, screenHeight * 0.5, "Event Demo GUI", false)
guiWindowSetMovable(myWindow, false)
guiWindowSetSizable(myWindow, false)

outputLabel = guiCreateLabel(0.1, 0.1, 0.8, 0.2, "Type something and click Submit!", true,
myWindow)
guiLabelSetHorizontalAlign(outputLabel, "center")
guiLabelSetVerticalAlign(outputLabel, "center")
guiSetFont(outputLabel, "default-bold-small")

inputEditBox = guiCreateEdit(0.1, 0.4, 0.8, 0.1, "Your message here...", true, myWindow)
guiEditSetMaxLength(inputEditBox, 64) -- Limit input length

submitButton = guiCreateButton(0.3, 0.7, 0.4, 0.15, "Submit", true, myWindow)

showCursor(true)

-- Event Handler for Button Click


addEventHandler("onClientGUIClick", submitButton, function(button, state)
if button == "left" and state == "up" then -- Check for left mouse button release
local inputText = guiGetText(inputEditBox)
guiSetText(outputLabel, "You typed: ".. inputText)
outputChatBox("GUI Button Clicked! Text: ".. inputText, 0, 255, 0)
end
end, false) -- 'false' to prevent propagation to window

-- Event Handler for EditBox Text Change


addEventHandler("onClientGUIChanged", inputEditBox, function()
local currentText = guiGetText(source) -- 'source' is the editBox
outputDebugString("EditBox text changed to: ".. currentText) -- Use debug string to avoid
chat spam
end)

-- Event Handler for EditBox Enter Press (onClientGUIAccepted)


addEventHandler("onClientGUIAccepted", inputEditBox, function()
local acceptedText = guiGetText(source)
outputChatBox("You pressed Enter! Text: ".. acceptedText, 255, 150, 0)
guiSetText(outputLabel, "Enter pressed! Text: ".. acceptedText)
end)
end

addCommandHandler("openguievents", function()
if not myWindow then
createGUIWithEvents()
else
guiSetVisible(myWindow, true)
showCursor(true)
end
end)

addCommandHandler("closeguievents", function()
if myWindow then
guiSetVisible(myWindow, false)
showCursor(false)
end
end)

Tips for Beginners:


● Always check the button and state parameters in onClientGUIClick to ensure
you're reacting to the intended mouse button (e.g., "left") and action (e.g., "up"
for release).
● For onClientGUIChanged, remember it fires on any change, which can be very
frequent. Use outputDebugString instead of outputChatBox for debugging its
behavior to avoid chat spam.
● Use onClientGUIAccepted when you want to trigger an action only after the user
has finished typing and pressed Enter in an editbox.

Common Mistakes:
● Ignoring button and state: Not checking which mouse button was clicked or if it
was a press or release, leading to unintended triggers.
● Spamming Chat: Using outputChatBox inside onClientGUIChanged or other
high-frequency GUI events, which can flood the chat for the player.
outputDebugString is more appropriate for such debugging.60
● Propagation Issues: Not understanding event propagation for GUI elements. If
you attach a handler to a button and its parent window, the event might fire
twice. Use propagate=false in addEventHandler to prevent this if needed.25

4. Linking GUI with game logic

Creating interactive GUIs is only half the battle; the real power comes from
connecting these client-side interfaces to the server-side game logic. This is
achieved through the client-server event communication model.
● Client-Side GUI, Server-Side Logic:
○ GUIs are inherently client-side.1 They are drawn on the player's screen and
handle local input.
○ Any game logic that needs to be secure, persistent, or synchronized across
all players (e.g., changing player money, spawning vehicles, updating player
stats) must reside on the server.21
● The Communication Bridge: triggerServerEvent:
○ When a player interacts with a GUI element (e.g., clicks a button, types in an
editbox and presses Enter), the client-side script captures this interaction via
GUI events (like onClientGUIClick, onClientGUIAccepted).
○ The client-side script then uses triggerServerEvent(eventName, localPlayer,
[arguments...]) to send a message (an event) to the server.28
○ This event carries any necessary data from the GUI input (e.g., selected item,
typed text, button ID).
● Server-Side Processing:
○ On the server, an addEventHandler for the corresponding eventName (which
must be registered with addEvent(eventName, true)) receives the event from
the client.28
○The server-side handler processes the data, validates it (crucial for security,
as client data can be faked) 21, and then executes the appropriate game logic
(e.g., spawns a vehicle, updates a database, changes player stats).
○ The client global variable on the server-side identifies the player who
triggered the event.29
● Server-to-Client Feedback:
○ After processing, the server might send feedback back to the client (e.g.,
"Vehicle spawned!", "Insufficient funds") using triggerClientEvent(client,
feedbackEventName, root, [arguments...]).28
○ The client-side script then handles this feedback, perhaps by updating a GUI
label or displaying a chat message.

Code Example (GUI to Game Logic - Vehicle Spawner):

This example builds on previous GUI and vehicle spawning concepts.


● meta.xml (Ensure this is in your resource folder)
XML
<meta>
<info author="YourName" version="1.0.0" name="GUIVehicleSpawner" description="GUI for
spawning vehicles" type="script" />
<script src="client.lua" type="client" />
<script src="server.lua" type="server" />
</meta>

● client.lua
Lua
local vehicleSpawnerWindow = nil
local vehicleIDEdit = nil
local spawnButton = nil
local statusLabel = nil

function createVehicleSpawnerGUI()
screenWidth, screenHeight = guiGetScreenSize()
local
vehicleSpawnerWindow = guiCreateWindow(screenWidth * 0.3, screenHeight *
0.3, screenWidth * 0.4, screenHeight * 0.4, "Vehicle Spawner", false)
guiWindowSetMovable(vehicleSpawnerWindow, false)
guiWindowSetSizable(vehicleSpawnerWindow, false)

guiCreateLabel(0.1, 0.1, 0.8, 0.1, "Enter Vehicle ID/Name:", true,


vehicleSpawnerWindow)
vehicleIDEdit = guiCreateEdit(0.1, 0.2, 0.8, 0.1, "411 (Infernus)", true,
vehicleSpawnerWindow)
spawnButton = guiCreateButton(0.3, 0.7, 0.4, 0.15, "Spawn Vehicle", true,
vehicleSpawnerWindow)
statusLabel = guiCreateLabel(0.1, 0.5, 0.8, 0.1, "", true, vehicleSpawnerWindow)
guiLabelSetHorizontalAlign(statusLabel, "center")

showCursor(true)

-- Handle button click


addEventHandler("onClientGUIClick", spawnButton, function(button, state)
if button == "left" and state == "up" then
local vehicleInput = guiGetText(vehicleIDEdit)
guiSetText(statusLabel, "Requesting spawn...")
-- Trigger server event to request vehicle spawn
triggerServerEvent("requestVehicleSpawn", localPlayer, vehicleInput)
end
end, false)

-- Handle feedback from server


addEvent("vehicleSpawnFeedback", true) -- Must be registered as remote triggerable
addEventHandler("vehicleSpawnFeedback", localPlayer, function(message, success)
guiSetText(statusLabel, message)
if success then
guiLabelSetColor(statusLabel, 0, 255, 0) -- Green for success
else
guiLabelSetColor(statusLabel, 255, 0, 0) -- Red for failure
end
end)
end

addCommandHandler("openvehiclespawner", function()
if not vehicleSpawnerWindow then
createVehicleSpawnerGUI()
else
guiSetVisible(vehicleSpawnerWindow, true)
showCursor(true)
end
end)

addCommandHandler("closevehiclespawner", function()
if vehicleSpawnerWindow then
guiSetVisible(vehicleSpawnerWindow, false)
showCursor(false)
end
end)

● server.lua
Lua
-- Server-side: Register the event that clients will trigger
addEvent("requestVehicleSpawn", true) -- 'true' is crucial for client-to-server events

-- Server-side: Handle the vehicle spawn request from client


addEventHandler("requestVehicleSpawn", root, function(vehicleInput)
-- Validate input from client (NEVER trust client data directly!)
if not vehicleInput or type(vehicleInput) ~= "string" or string.len(vehicleInput) == 0
then
triggerClientEvent(client, "vehicleSpawnFeedback", root, "Invalid vehicle input.",
false)
return
end

vehicleID = tonumber(vehicleInput) or
local
getVehicleModelFromName(vehicleInput)

if notvehicleID then
triggerClientEvent(client, "vehicleSpawnFeedback", root, "Invalid vehicle ID or name.",
false)
return
end

-- Get player's position to spawn the vehicle


local x, y, z = getElementPosition(client)
local rotZ = getElementRotation(client)
local spawnX = x + (math.cos(math.rad(rotZ)) * 5)
local spawnY = y + (math.sin(math.rad(rotZ)) * 5)
local spawnZ = z + 1

local newVehicle = createVehicle(vehicleID, spawnX, spawnY, spawnZ, 0, 0, rotZ)

newVehicle then
if
triggerClientEvent(client, "vehicleSpawnFeedback", root, "Spawned "..
getVehicleNameFromModel(vehicleID).. "!", true)
warpPedIntoVehicle(client, newVehicle)
outputConsole(getPlayerName(client).. " spawned a "..
getVehicleNameFromModel(vehicleID))
else
triggerClientEvent(client, "vehicleSpawnFeedback", root, "Failed to spawn vehicle
(limit/error).", false)
outputConsole("Failed to spawn vehicle for ".. getPlayerName(client).. ": "..
vehicleInput)
end
end)

Tips for Beginners:


● Always remember the client-server boundary: GUI is client-side, core game logic
is server-side. Communication is via events.
● Validate all data received from a client-triggered event on the server-side. A
malicious client can send any data, regardless of what your GUI permits.
● Provide clear feedback to the player via GUI updates or chat messages about the
success or failure of their actions.

Common Mistakes:
● Security Holes: Trusting input from client-side GUIs without server-side
validation. For example, if a GUI has a button to "give 100 money," a cheater
could modify their client to send an event requesting "1000000 money" if the
server doesn't validate the amount.
● Direct Logic: Attempting to perform game-changing actions (like
setPlayerMoney or createVehicle) directly from client-side GUI event handlers.
These functions are server-side and will not work correctly or securely from the
client.
● Missing addEvent(..., true): Forgetting to register the events as remotely
triggerable on both client and server, preventing communication.

The integration of GUI with game logic in MTA:SA fundamentally relies on the client-
server event model. This architecture ensures that while players can interact with a
rich graphical interface on their local machine, all critical game state changes and
sensitive operations are handled securely and authoritatively on the server. This
design is crucial for maintaining fairness and preventing exploits in a multiplayer
environment. The client-side GUI acts as a user-friendly input and display layer, while
the server-side scripts perform the heavy lifting of processing requests, applying
game rules, and synchronizing changes across all players. This separation of
concerns is a cornerstone of robust multiplayer game development.

Practice Exercises (Module 5)

1. Player Status GUI:


○ Create a client-side GUI window with labels for "Health:", "Money:", and "Skin
ID:".
○ Use guiSetText to update these labels with the localPlayer's current health,
money, and skin ID.
○ Use setTimer to update this GUI every 1 second.
○ Advanced: Make the GUI appear/disappear with a key press (e.g., F4) and
ensure the cursor is shown/hidden accordingly.
2. Teleport Button:
○ Create a client-side GUI with a button labeled "Teleport to Los Santos
Airport".
○ When clicked, the client-side script should trigger a server event, passing a
fixed set of coordinates (e.g., LS Airport coordinates).
○ On the server, receive this event and use setElementPosition to teleport the
client player to those coordinates.
○ Send a chat message back to the player confirming the teleport.
3. Chat Input GUI:
○ Create a client-side GUI with an editbox and a button labeled "Send Chat".
○ When the button is clicked or Enter is pressed in the editbox, get the text
from the editbox.
○ Trigger a server event, passing this text.
○ On the server, receive the text and use outputChatBox to display it to all
players, prefixed with the sending player's name (e.g., [PlayerName]:
[Message]).

📕 Module 6: Advanced Topics

1. Databases in MTA:SA (MySQL, SQLite)

For any persistent game mode, especially RPGs, storing player data, vehicle
information, or world states beyond a single server session is essential. MTA:SA
provides built-in functions to connect and interact with databases, primarily SQLite
and MySQL.
● Why Databases?
○ Data Persistence: Store data that needs to survive server restarts (e.g.,
player accounts, money, inventory, vehicle ownership).33
○ Scalability: Manage large amounts of structured data efficiently.
○ Complexity: Enable complex systems like login/registration, inventory,
leveling, and property systems.
● dbConnect(databaseType, host, [username], [password], [options]):
○ This function establishes a connection to a database and returns a database
connection element.63
○ databaseType: "sqlite" or "mysql".63
○ host:
■ For SQLite: A filepath to a .db file. If the path starts with :/, it uses the
server's global databases directory.63 The file will be created if it doesn't
exist.63
■ For MySQL: A string of key=value pairs (e.g.,
dbname=yourdb;host=127.0.0.1;port=3306).63
○ username, password: Required for MySQL, ignored by SQLite.63
○ options: A string of key=value pairs for advanced settings (e.g., share=1,
batch=1, autoreconnect=1).63
○ Best Practice: It is highly recommended to call dbConnect only once when
the resource starts and store the returned connection element globally.
Reconnecting multiple times can impact server performance.63
● dbQuery(dbConnection, sqlQuery, [arguments...]):
○ Executes an SQL query on the specified database connection.63
○ dbConnection: The element returned by dbConnect.
○ sqlQuery: The SQL query string. Use ? as placeholders for arguments to
prevent SQL injection vulnerabilities.63
○ Asynchronous Nature: Database queries are asynchronous. dbQuery
returns a query handle immediately. The actual results are retrieved later
using dbPoll.
● dbPoll(queryHandle, timeout):
○ Retrieves the results of an asynchronous query.63
○ queryHandle: The handle returned by dbQuery.
○ timeout: How long to wait for results (-1 for infinite, 0 for immediate check).63
○ Returns a table of rows for SELECT queries, or result, numRows for
INSERT/UPDATE/DELETE operations.63
● dbExec(dbConnection, sqlQuery, [arguments...]):
○ A synchronous version of dbQuery for INSERT/UPDATE/DELETE queries that
do not return results. It's often used for simple write operations where
immediate result polling isn't needed.
● destroyElement(dbConnection): Closes the database connection when the
resource stops or is no longer needed.63

Code Example (SQLite Database Interaction):

This example demonstrates connecting to an SQLite database, creating a table,


inserting data, and retrieving data.
● meta.xml (Ensure this is in your resource folder)
XML
<meta>
<info author="YourName" version="1.0.0" name="DatabaseExample"
description="Demonstrates SQLite database usage" type="script" />
<script src="server.lua" type="server" />
</meta>

● server.lua
Lua
local dbConnection = nil -- Global variable to store the database connection
-- Function to connect to the database and create a table on resource start
addEventHandler("onResourceStart", resourceRoot, function()
-- Connect to an SQLite database file named 'player_data.db' in the resource's directory
dbConnection = dbConnect("sqlite", "player_data.db")

if dbConnection then
outputConsole("Successfully connected to SQLite database: player_data.db")
-- Create a table for player data if it doesn't exist
local createTableQuery =]
local queryHandle = dbQuery(dbConnection, createTableQuery)
dbPoll(queryHandle, -1) -- Wait for the query to complete
outputConsole("Players table checked/created.")
else
outputConsole("ERROR: Failed to connect to SQLite database!")
end
end)

-- Function to close the database connection on resource stop


addEventHandler("onResourceStop", resourceRoot, function()
if dbConnection then
destroyElement(dbConnection)
outputConsole("Disconnected from SQLite database.")
end
end)

-- Example: Save player data when they quit


addEventHandler("onPlayerQuit", root, function()
if dbConnection then
local playerSerial = getPlayerSerial(source)
local playerName = getPlayerName(source)
-- Assuming you have player data stored in a table like this:
-- local playerData = { money = getPlayerMoney(source), level = getElementData(source,
"level"), xp = getElementData(source, "xp") }
-- For this example, let's use dummy data or actual game data if available
local playerMoney = getPlayerMoney(source)
local playerLevel = getElementData(source, "level") or 1 -- Get from element data or
default
local playerXP = getElementData(source, "xp") or 0 -- Get from element data or
default

insertOrUpdateQuery =]
local
local queryHandle = dbQuery(dbConnection, insertOrUpdateQuery,
playerSerial, playerName, playerMoney, playerLevel, playerXP)
dbPoll(queryHandle, -1)
outputConsole("Saved data for ".. playerName.. " (".. playerSerial.. ") to database.")
end
end)

-- Example: Load player data when they join


addEventHandler("onPlayerJoin", root, function()
if dbConnection then
local playerSerial = getPlayerSerial(source)
local playerName = getPlayerName(source)

local selectQuery = "SELECT name, money, level, xp FROM players WHERE serial =?;"
local queryHandle = dbQuery(dbConnection, selectQuery, playerSerial)
local result = dbPoll(queryHandle, -1) -- Wait for results

if result and #result > 0 then


local row = result
setPlayerMoney(source, row.money)
setElementData(source, "level", row.level)
setElementData(source, "xp", row.xp)
outputChatBox("Welcome back, ".. row.name.. "! Your data loaded.", source, 0,
255, 0)
outputConsole("Loaded data for ".. row.name.. " (".. playerSerial.. ") from
database.")
else
-- New player or no data found, initialize
setPlayerMoney(source, 1000)
setElementData(source, "level", 1)
setElementData(source, "xp", 0)
outputChatBox("Welcome, new player ".. playerName.. "! Initialized your data.",
source, 0, 255, 0)
outputConsole("Initialized new data for ".. playerName.. " (".. playerSerial.. ").")
end
end
end)

-- Command to check player's money from DB (for demonstration)


addCommandHandler("dbmoney", function(playerSource)
if dbConnection then
local playerSerial = getPlayerSerial(playerSource)
local selectMoneyQuery = "SELECT money FROM players WHERE serial =?;"
local queryHandle = dbQuery(dbConnection, selectMoneyQuery, playerSerial)
local result = dbPoll(queryHandle, -1)
if result and #result > 0 then
outputChatBox("Your money in DB: $".. result.money, playerSource, 255, 255, 0)
else
outputChatBox("No money data found in DB for you.", playerSource, 255, 0, 0)
end
else
outputChatBox("Database not connected.", playerSource, 255, 0, 0)
end
end)

Tips for Beginners:


● Always use dbConnect only once on onResourceStart and store the connection
element in a global or resource-scoped variable. Close it on onResourceStop.
● Use ? placeholders in your SQL queries with dbQuery to pass dynamic values.
This is crucial for preventing SQL injection attacks.
● Understand the asynchronous nature of dbQuery and use dbPoll to retrieve
results. For simple INSERT/UPDATE/DELETE where no results are needed, dbExec
can be a simpler synchronous alternative.

Common Mistakes:
● Frequent Connections: Opening and closing database connections for every
query. This is highly inefficient and can severely impact server performance.63
● SQL Injection: Concatenating user-provided strings directly into SQL queries
without using ? placeholders. This creates a major security vulnerability.
● Blocking Operations: Using dbPoll with a -1 timeout (infinite wait) on the main
thread for very long queries, which can freeze the server until the query
completes. Consider using setTimer or separate threads for long-running
database operations.
● Forgetting to Poll: Calling dbQuery but never calling dbPoll to retrieve the
results, leading to unhandled query handles and potential memory issues.

2. Creating custom game modes (Example: Battle Royale)

MTA:SA is designed as a sandbox engine where gameplay functionality is solely


provided by Lua scripting, allowing users to create and host their own unique game
modes.1 A custom game mode is essentially a collection of resources that define
specific rules, objectives, and player experiences.
● Gamemode Architecture:
○ A game mode is typically a resource itself, tagged with type="gamemode" in
its meta.xml.19
○ It often works in conjunction with a map resource (tagged type="map"), which
defines the physical world elements like spawn points, objects, and pickups.65
○ The mapmanager resource (a default MTA resource) handles loading
gamemodes and maps, often triggering events like onGamemodeMapStart to
inform the gamemode script that a map has loaded.65
○ Game modes can include client-side, server-side, and shared scripts to
manage different aspects of gameplay.8
● Key Components of a Gamemode:
○ Player Spawning and Initialization: Defining where and how players spawn,
their initial health, weapons, and any starting equipment.8
○ Game Rules and Objectives: Implementing the core logic, such as win
conditions, scoring, round management, and player interactions.8
○ World Manipulation: Dynamically creating, moving, or destroying objects,
vehicles, and pickups to set up the game environment.1
○ UI/HUD: Custom graphical interfaces to display game-specific information
(e.g., scoreboards, timers, player stats).1
○ Events: Extensive use of MTA's event system for all interactions and state
changes.8
○ Persistence (Databases): For game modes that require player progress or
world changes to be saved, database integration is crucial.33
● Example: Battle Royale Gamemode Concepts:
○ Core Loop: Players spawn, loot, fight, and a "safe zone" shrinks over time,
forcing players together. The last player/team alive wins.
○ Player Spawning: Random spawn points across a large map, possibly from
the air (e.g., parachuting).
○ Loot System: Randomly spawned weapons, armor, health packs, and other
items across the map.8 An inventory system would be needed to manage
collected items.70
○ Shrinking Safe Zone (Gas/Circle): A dynamic element (e.g., a large, moving
collision shape or a visual effect) that deals damage to players outside it.69
This would require client-side rendering for visual feedback and server-side
logic for damage application.
○ Player Elimination: When a player's health reaches zero, they are eliminated
from the round.
○ Match Management: Server-side logic to track remaining players/teams,
determine the winner, and reset the round.
○ UI: A custom HUD to show remaining players, map (with safe zone), and
inventory.69

Conceptual Code Structure (Battle Royale Snippets):


● meta.xml (for the Battle Royale gamemode resource)
XML
<meta>
<info author="YourName" version="1.0.0" name="BattleRoyale" description="A custom Battle
Royale gamemode" type="gamemode" />
<script src="server.lua" type="server" />
<script src="client.lua" type="client" />
<script src="shared.lua" type="shared" />
<file src="br_map.map" type="map" /> <file src="images/circle_overlay.png" /> </meta>

● server.lua (Battle Royale core logic)


Lua
-- Server-side: Game state management
local gameStarted = false
local alivePlayers = {}
local safeZoneRadius = 5000 -- Initial radius
local safeZoneCenter = {x = 0, y = 0}

-- Event handler for player joins (spawn them, add to alive list)
addEventHandler("onPlayerJoin", root, function()
-- Initial spawn logic (e.g., in a lobby or starting area)
spawnPlayer(source, 0, 0, 5)
setElementData(source, "isAlive", true)
table.insert(alivePlayers, source)
outputChatBox("Welcome to Battle Royale!", source, 0, 255, 0)
end)

-- Command to start the game (admin only)


addCommandHandler("startbr", function(playerSource)
if isObjectInACLGroup("user."..
getAccountName(getPlayerAccount(playerSource)), aclGetGroup("Admin")) then
if not gameStarted then
gameStarted = true
outputChatBox("Battle Royale game started!", root, 255, 255, 0)
-- Teleport all players to random spawn points high in the air
for _, player in ipairs(alivePlayers) do
local randX = math.random(-3000, 3000)
local randY = math.random(-3000, 3000)
spawnPlayer(player, randX, randY, 1000) -- Spawn high up
-- Trigger client event to show parachute GUI or similar
triggerClientEvent(player, "startParachute", player)
end
setTimer(startShrinkZone, 60000, 1) -- Start shrinking zone after 1 minute
else
outputChatBox("Game already in progress.", playerSource, 255, 0, 0)
end
else
outputChatBox("You are not an admin.", playerSource, 255, 0, 0)
end
end)

-- Function to shrink the safe zone (simplified)


function startShrinkZone()
safeZoneRadius = safeZoneRadius - 500
outputChatBox("Safe zone is shrinking! Current radius: ".. safeZoneRadius, root, 255,
150, 0)
-- Trigger client event to update safe zone visualization
triggerClientEvent(root, "updateSafeZone", root, safeZoneCenter.x,
safeZoneCenter.y, safeZoneRadius)

-- Check for players outside safe zone and apply damage


setTimer(function()
for _, player in ipairs(alivePlayers) do
local px, py, pz = getElementPosition(player)
local dist = getDistanceBetweenPoints2D(px, py, safeZoneCenter.x,
safeZoneCenter.y)
if dist > safeZoneRadius then
setElementHealth(player, getElementHealth(player) - 10) -- Apply gas
damage
outputChatBox("You are outside the safe zone!", player, 255, 0, 0)
end
end
end, 2000, 0) -- Apply damage every 2 seconds

if safeZoneRadius > 500 then -- Continue shrinking until a minimum size


setTimer(startShrinkZone, 30000, 1) -- Shrink again after 30 seconds
else
outputChatBox("Final showdown!", root, 255, 255, 0)
end
end)

-- Event handler for player death (check win condition)


addEventHandler("onPlayerWasted", root, function()
local player = source
setElementData(player, "isAlive", false)
for i, p in ipairs(alivePlayers) do
if p == player then
table.remove(alivePlayers, i)
break
end
end
outputChatBox(getPlayerName(player).. " was eliminated!", root, 200, 200, 200)

if #alivePlayers <= 1 and gameStarted then


-- Game over logic
local winner = alivePlayers
if winner then
outputChatBox("Winner: ".. getPlayerName(winner).. "!", root, 0, 255, 0)
else
outputChatBox("No winner found. Game ended.", root, 255, 255, 0)
end
gameStarted = false
alivePlayers = {} -- Reset for next round
setTimer(function()
outputChatBox("New round starting soon...", root, 255, 255, 255)
-- Reset map, clear vehicles, etc.
end, 10000, 1)
end
end)

● client.lua (Battle Royale client-side components)


Lua
-- Client-side: Event to update safe zone visualization
addEvent("updateSafeZone", true)
addEventHandler("updateSafeZone", root, function(centerX, centerY, radius)
-- Implement dxDrawCircle or similar to draw the safe zone on the map/screen
-- For a real BR, you'd draw a dynamic circle on the radar or a screen overlay
outputChatBox("Client received safe zone update: ".. radius, 0, 255, 255)
end)

-- Client-side: Example for parachute start


addEvent("startParachute", true)
addEventHandler("startParachute", localPlayer, function()
outputChatBox("Parachuting down! Find loot!", 255, 255, 0)
-- You would typically create a custom parachute animation, GUI, etc.
end)

Tips for Beginners:


● Start with a simple core loop and gradually add features.
● Clearly separate client-side (GUI, local effects) and server-side (game logic, state
management) responsibilities.
● Leverage MTA's existing functions for player/vehicle management, events, and
GUI.
Common Mistakes:
● Over-complicating Early: Trying to implement too many features at once,
leading to a complex and buggy codebase.
● Client-Side Game Logic: Putting critical game rules (e.g., win conditions,
damage calculation) on the client, which is easily exploitable.
● Lack of Persistence: Not using databases for player data, leading to lost
progress after server restarts.

MTA:SA's design as a game engine with exposed Lua scripting functionality means
that gameplay functionality is solely provided by the scripting language. This allows
users to create their own combination of scripts and content to customize and host
their own type of game. This implies that the platform provides a minimal "sandbox
style gameplay" 64 by default, which can be heavily extended through Lua.64 This
architecture empowers developers to create virtually any game mode imaginable,
from simple deathmatches to complex RPGs, by building upon MTA:SA's core
framework and synchronizing state between client and server.1 The ability to create
custom game modes is not just a feature; it is the

defining characteristic of MTA:SA as a modding platform. This level of flexibility allows


for a vast array of unique player experiences, making the platform highly adaptable
and community-driven.

3. Optimizing scripts for performance

Script optimization is the process of improving code to make it run faster and
consume fewer system resources (CPU, memory, bandwidth).71 In MTA:SA, efficient
scripting is crucial for maintaining a smooth and responsive experience for players,
especially on busy servers.
● Why Optimize?
○ Speed: Faster scripts lead to quicker response times in-game.71
○ Efficiency: Reduced CPU and memory usage saves server costs and
improves overall system stability.71
○ User Experience: Optimized scripts result in smoother gameplay, fewer lags,
and a better experience for players.71
● General Lua Optimization Tips:
○Prefer Local Variables: Accessing local variables is significantly faster than
accessing global variables, as Lua has to perform a lookup in different scopes
for globals.72 Declare variables as
local within functions whenever possible.73
○ Minimize Function Calls: Each function call has a performance cost.
Consolidate operations within a single function where feasible to reduce
unnecessary calls.73
○ Efficient Data Structures: Choose the right data structure for the task. Flat
arrays are generally faster than complex nested tables for data-heavy
applications.73 Using separate variables instead of tables for frequently
accessed, non-related data can also be faster.72
○ Optimize Loops:
■ Reduce the number of loop iterations.71
■ For numerical loops, use for i = start, end, delta do rather than while loops
if the number of iterations is known.73
■ For multi-dimensional arrays, optimize index calculation to avoid costly
multiplications in the innermost loop.72
○ Avoid Unnecessary Table Lookups: For frequently accessed table keys,
store the value in a local variable if it doesn't change.74
● MTA:SA Specific Optimization Tips:
○ Client-Side onClientRender / onClientPreRender / onClientHUDRender:
These events are called every frame.27 Any heavy logic placed here will cause
significant client-side CPU usage and lag.27
■ Minimize calculations and drawing operations.
■ Use getDistanceBetweenPoints3D checks to only render elements when
the player is nearby.27
■ Optimize dxDraw* functions using dxCreateRenderTarget to merge
multiple draws into one image, reducing calls.27
○ Event Handler Specificity: Attach event handlers to the most specific
element possible (e.g., localPlayer or a specific vehicle) instead of root.22 This
ensures your handler only runs when truly relevant, reducing unnecessary
processing.
○ Client-Side Attribute Setting: For player attributes like health, rotation, or
armor, setting them client-side (via setElementHealth, setElementRotation,
setPedArmor on the client) can be more efficient than server-side, as it
avoids sending RPCs (Remote Procedure Calls) to all players.27 The client then
updates the server via puresync. However, this is not advised for position due
to low refresh rates.27
○ Memory Management:
■ When deleting variables or element/account data, use nil instead of false
to reduce memory and disk usage.27
■ Be aware of memory leaks, especially with dynamically created elements
like textures (dxCreateTexture).75 If you create elements, ensure they are
properly destroyed when no longer needed to prevent memory
accumulation.75 Caching textures instead of constantly creating and
destroying them can be a workaround for some leaks.75
■ Server memory usage should be monitored; if it exceeds 1.2 GB, it's close
to an Out Of Memory (OOM) error, indicating a need for server restart or
leak investigation.7
○ Database Operations: Batch queries (batch=1 option in dbConnect) can
significantly speed up inserts/updates.63 Avoid frequent
dbConnect/dbDisconnect calls.63
● Profiling and Benchmarking:
○ Use profiling tools to identify performance bottlenecks in your scripts.71 These
tools track runtime performance, pinpointing slow functions and memory
hogs.71
○ Benchmarking helps test how often a function executes or how long a block
of code takes.72

Tips for Beginners:


● "Premature Optimization is the Root of All Evil": Focus on writing clean,
readable, and functional code first. Only optimize when you identify a specific
performance bottleneck through testing or profiling.73
● Monitor Performance: Use MTA's built-in performance tools (e.g.,
performancebrowser or ipb in-game) to identify which resources are consuming
the most CPU/memory.27
● Client-Side Caution: Be extremely mindful of any code running in
onClientRender or other per-frame events, as they are the most common cause
of client-side lag.27

Common Mistakes:
● Global Variable Abuse: Over-reliance on global variables without using local,
leading to slower access times and potential conflicts.72
● Unnecessary Calculations in Loops: Performing redundant calculations inside
loops that could be done once outside the loop.
● Memory Leaks: Failing to destroy dynamically created elements (e.g., GUI
elements, objects, textures) when they are no longer needed, leading to
accumulated memory usage and eventual crashes.75
● Ignoring Server Maintenance: Not regularly restarting the server to clear
accumulated memory from "software aging" or unmanaged elements, leading to
instability over long periods.7

Effective script optimization in MTA:SA is not just about writing "faster" code; it is
about understanding the unique performance characteristics of the MTA:SA engine
and its client-server architecture.27 The biggest cause of client script CPU usage is
anything done in

onClient/Pre/HUD/Render because it is called every frame.27 This means that


developers must be acutely aware of the frequency of their code execution and
design accordingly. For instance, frequently drawing complex GUI elements or
performing heavy calculations in per-frame events can severely impact client-side
performance, leading to a poor user experience. Similarly, server-side performance is
affected by inefficient database calls or unmanaged memory. This understanding
necessitates a strategic approach to code placement and resource management,
emphasizing local variables, specific event handling, and diligent cleanup.

4. Security tips for MTA:SA servers

Server security in MTA:SA is paramount to protect against cheating, exploits, and


unauthorized access, ensuring a fair and stable environment for players. Given the
client-server architecture, a "never trust the client" mentality is crucial.
● Client-Side Data is Vulnerable:
○ Any data or Lua logic stored on the client-side (including .lua files
themselves) is at risk of being accessed and manipulated by malicious
clients.21
○ Scripts marked as shared also run client-side, making their client-side logic
vulnerable.21
○ Mitigation: Store all confidential or important data and sensitive Lua logic
exclusively on the server-side.21
■ For client-side scripts, use cache="false" in meta.xml to prevent the file
from being saved on the player's PC, making it slightly harder for casual
cheaters to access.21
■ Consider Lua compilation with obfuscation (level 3 API) for client-side
scripts to make them harder to reverse-engineer.21
● Input Validation (Server-Side):
○ Never Trust Client Data: This is the golden rule.21 Any data received from
the client (e.g., via
triggerServerEvent, chat commands, GUI input) must be validated and
sanitized on the server before being processed by any application functions.21
○ Syntactic and Semantic Validation:
■ Syntactic validation: Enforce correct format (e.g., numbers are numbers,
strings are within length limits).38
■ Semantic validation: Ensure values make sense in the game context (e.g.,
a player cannot request more money than they are allowed, or teleport to
invalid coordinates).38
○ SQL Injection Prevention: When interacting with databases, always use
parameterized queries (e.g., ? placeholders in dbQuery) instead of
concatenating user input directly into SQL strings.63
● Access Control List (ACL):
○ Properly configure acl.xml to define permissions for administrative commands
and sensitive functions.7
○ Use hasObjectPermissionTo() or isObjectInACLGroup() server-side to verify a
player's rights before executing privileged actions.21
○ Crucial: When checking permissions for events triggered from clients, use
the global client variable to identify the player, not source if source could be
faked.21
● Event Security:
○ Remote Triggering: For server-side events that should not be triggered by
clients, ensure addEvent is called without true as the second argument (i.e.,
addEvent(eventName, false) or simply addEvent(eventName)).21 This prevents
clients from remotely calling server-only events.
○ Event Faking: Be aware that malicious clients might attempt to "fake" events
by passing incorrect source elements or parameters.21 Always use the global
client variable on the server-side to identify the actual player who triggered a
client-to-server event.29
○ Unique Event Names: Avoid using the same names for your custom events
as MTA native server events (e.g., onPlayerLogin), as this could open doors
for cheater manipulations.21
● Element Data Security:
○ setElementData can be vulnerable to client-side modification if not properly
secured.76
○ Enable elementdata_whitelisted in mtaserver.conf to ignore client-sent
element data sync packets unless explicitly allowed.77
○ Avoid syncing sensitive element data to the client if it's not needed for client-
side visuals, as it can be read from RAM.21
● Server Maintenance and Updates:
○ Keep your MTA:SA server software and default resources up-to-date.21
Updates often include critical security fixes.21
○ Regularly restart your server to prevent "software aging" and memory leaks,
which can lead to instability and crashes.7
● Third-Party Resources:
○ Exercise extreme caution when using resources from unknown or untrusted
sources.21
○ Before running, analyze their meta.xml for hidden scripts and review their
source code for malicious logic.21
○ Avoid running or keeping compiled resources (.luac files) if their legitimacy is
uncertain, as compiled code is harder to inspect.21

Tips for Beginners:


● Server-Side Logic for Security: If a piece of game logic needs to be secure or
affect all players, it must be handled server-side.
● Validate Everything: Develop a habit of validating all inputs, especially from
players, on the server. Assume the client is always trying to cheat.
● Stay Updated: Keep your server and resources updated to benefit from the
latest security patches.

Common Mistakes:
● Client-Side "Security": Attempting to implement security checks or sensitive
logic exclusively on the client-side, which is easily bypassed by cheaters.
● SQL Injection: Not using parameterized queries for database interactions,
creating a critical vulnerability.
● Weak ACL Configuration: Failing to properly restrict administrative commands
or sensitive functions via ACL, allowing unauthorized players to abuse them.
● Ignoring client Variable: Using source instead of client to identify the player
who triggered a client-to-server event, which can be faked by cheaters.21
● Blindly Using Third-Party Scripts: Running unverified scripts from unknown
sources, which could contain backdoors or malicious code.

Server security in

Works cited

1. Multi Theft Auto - Wikipedia, accessed on August 3, 2025,


https://en.wikipedia.org/wiki/Multi_Theft_Auto
2. Beginner's Guide to Playing Multi Theft Auto: San Andreas - GitHub Gist,
accessed on August 3, 2025,
https://gist.github.com/Fernando-A-Rocha/f99294cf32d8a205830068d9668a90
90
3. What do y'all use lua for? - Reddit, accessed on August 3, 2025,
https://www.reddit.com/r/lua/comments/17f5ce6/what_do_yall_use_lua_for/
4. getting started - Lua, accessed on August 3, 2025, https://www.lua.org/start.html
5. Lua Data Types - GeeksforGeeks, accessed on August 3, 2025,
https://www.geeksforgeeks.org/lua-data-types/
6. Client Manual - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Client_Manual
7. Server Manual - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Server_Manual
8. Scripting Introduction - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Scripting_Introduction
9. MTA:SA Lua Debugger and Test Framework - Visual Studio Marketplace,
accessed on August 3, 2025, https://marketplace.visualstudio.com/items?
itemName=jusonex.mtatd
10. Troubleshooting Path of Building Lua Errors: A Comprehensive Guide - APIPark,
accessed on August 3, 2025, https://apipark.com/techblog/en/troubleshooting-
path-of-building-lua-errors-a-comprehensive-guide/
11. MTA:SA Lua - Visual Studio Marketplace, accessed on August 3, 2025,
https://marketplace.visualstudio.com/items?itemName=subtixx.mtasa-lua
12. MTA DevKit - Visual Studio Marketplace, accessed on August 3, 2025,
https://marketplace.visualstudio.com/items?itemName=Yoel4devs.mta-devkit
13. lua-users wiki: Lua Types Tutorial - lua-users.org, accessed on August 3, 2025,
http://lua-users.org/wiki/LuaTypesTutorial
14. Lua Basic Overview - Pixera, accessed on August 3, 2025,
https://help.pixera.one/en_US/lua-scripting/lua-basic-overview
15. Troubleshooting Common Lua Errors in Game Development - A Comprehensive
Guide, accessed on August 3, 2025, https://moldstud.com/articles/p-
troubleshooting-common-lua-errors-in-game-development-a-comprehensive-
guide
16. Lua Functions - Tutorialspoint, accessed on August 3, 2025,
https://www.tutorialspoint.com/lua/lua_functions.htm
17. Making a PM System - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Making_a_PM_System
18. Meta.xml | Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.preview.multitheftauto.com/reference/Meta.xml/
19. Meta.xml - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Meta.xml
20. This repository contains quickstart source files for creating your own custom
gamemode in Multi Theft Auto: San Andreas (MTA:SA). - GitHub, accessed on
August 3, 2025, https://github.com/VibingCreator/mta-sa-quickstart
21. Script security - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Script_security
22. Event System | Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.preview.multitheftauto.com/reference/Event_System/
23. Event system - Multi Theft Auto: Wiki, accessed on August 3, 2025,
http://wiki.multitheftauto.com/wiki/Event
24. onClientVehicleExplode - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/OnClientVehicleExplode
25. onClientGUIClick - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/OnClientGUIClick
26. onPlayerJoin - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/OnPlayerJoin
27. Scripting Tips - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Scripting_Tips
28. triggerClientEvent - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/TriggerClientEvent
29. triggerServerEvent - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/TriggerServerEvent
30. onPlayerQuit - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/OnPlayerQuit
31. Server-Side Scripting Basics | Lenovo US, accessed on August 3, 2025,
https://www.lenovo.com/us/en/glossary/server-side-scripting/
32. Server-side scripting - Wikipedia, accessed on August 3, 2025,
https://en.wikipedia.org/wiki/Server-side_scripting
33. Learning to code with MTA:SA - Episode 1 - YouTube, accessed on August 3,
2025, https://m.youtube.com/watch?
v=Goqj5knKLQM&pp=ygUMI3NhbXRhc3R1ZGlv
34. TriggerServerEvent - Cfx.re Docs - FiveM Docs, accessed on August 3, 2025,
https://docs.fivem.net/docs/scripting-reference/runtimes/lua/functions/
TriggerServerEvent/
35. outputChatBox - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/OutputChatBox
36. onClientKey - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/OnClientKey
37. addDebugHook - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/AddDebugHook
38. Input Validation - OWASP Cheat Sheet Series, accessed on August 3, 2025,
https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.h
tml
39. setElementHealth - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/SetElementHealth
40. setPedMaxHealth - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/SetPedMaxHealth
41. setVehiclePanelState - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/SetVehiclePanelState
42. Shared Scripting Functions - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Shared_Scripting_Functions
43. setPlayerStat - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/SetPlayerStat
44. takePlayerMoney - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/TakePlayerMoney
45. setPedSkin - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/SetPedSkin
46. getPlayerSkin - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/GetPlayerSkin
47. toggleControl - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/ToggleControl
48. Control names - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Control_names
49. setControlState - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/SetControlState
50. addCommandHandler - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/AddCommandHandler
51. Element/Vehicle - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Element/Vehicle
52. setVehicleEngineState - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/SetVehicleEngineState
53. setVehicleDummyPosition - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/SetVehicleDummyPosition
54. Client Scripting Events - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Client_Scripting_Events
55. onClientVehicleRespawn - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/OnClientVehicleRespawn
56. guiCreateWindow - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/GuiCreateWindow
57. onClientClick - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/OnClientClick
58. guiCreateButton - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/GuiCreateButton
59. guiCreateLabel - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/GuiCreateLabel
60. onClientGUIChanged - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/OnClientGUIChanged
61. onClientGUIAccepted - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/OnClientGUIAccepted
62. onClientGUITabSwitched - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/OnClientGUITabSwitched
63. dbConnect - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/DbConnect
64. multitheftauto/mtasa-blue: Multi Theft Auto is a game engine that incorporates
an extendable network play element into a proprietary commercial single-player
game. - GitHub, accessed on August 3, 2025,
https://github.com/multitheftauto/mtasa-blue
65. Writing Gamemodes - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Writing_Gamemodes
66. Resource:Editor - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Resource:Editor
67. spawnPlayer - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/SpawnPlayer
68. createBuilding - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/CreateBuilding
69. [GameMode] SA-MP War of Banish [Battle Royale look like PUBG][STILL DEV],
accessed on August 3, 2025, https://sampforum.blast.hk/showthread.php?
tid=659357
70. MTA-DayZ/inventory.lua at master - GitHub, accessed on August 3, 2025,
https://github.com/ValdriGaR/MTA-DayZ/blob/master/inventory.lua
71. Everything You Need to Know When Assessing Script Optimization Skills -
Alooba, accessed on August 3, 2025,
https://www.alooba.com/skills/concepts/automationscripting-346/script-
optimization/
72. Lua Optimization Tips - Luanti Documentation, accessed on August 3, 2025,
https://docs.luanti.org/for-creators/lua-optimization-tips/
73. Top Lua Performance Tips - Best Practices for Efficient Coding - MoldStud,
accessed on August 3, 2025, https://moldstud.com/articles/p-top-lua-
performance-tips-best-practices-for-efficient-coding
74. Boost Lua Performance with Advanced Analysis Techniques - MoldStud,
accessed on August 3, 2025, https://moldstud.com/articles/p-boost-lua-
performance-with-advanced-analysis-techniques
75. Running out of memory (and crash) by loading content on demand · Issue #2869 ·
multitheftauto/mtasa-blue - GitHub, accessed on August 3, 2025,
https://github.com/multitheftauto/mtasa-blue/issues/2869
76. Protected Element Data · Issue #3286 · multitheftauto/mtasa-blue - GitHub,
accessed on August 3, 2025,
https://github.com/multitheftauto/mtasa-blue/issues/3286
77. Server mtaserver.conf - Multi Theft Auto: Wiki, accessed on August 3, 2025,
https://wiki.multitheftauto.com/wiki/Server_mtaserver.conf

You might also like