The new site of Sapp!

If your server didn't get auto-updated, it's probably because it doesn't have admin rights and can't write to Halo's directory. Therefore, it can't delete the old dll and copy the new one.
To fix this, run your server as administrator or just update it from this site manually.


Sapp 10.0 (2017.02.19.)
Fix: Fixed a Halo bug that combo messages (double kill, triple kill, killtacular) weren't announced after ~16 minutes of the start of the game.
Improvement: Message of the day is now synchronized instantly to the players using the sv_motd command.
New: motd [message] command: displays/sets the Message of the day without requiring a file. It is also synchronized instantly to the players.
New: reload_gametypes command: After using this command, you can use new gametypes added to the savegames folder without restarting the server.
New: save_respawn_time [boolean] command: If enabled, respawn time is also saved when a player leaves. This disables players to evade team kill penalty with simply rejoining the game.
New: gravity [value] command to change the gravity. It will be synchronised with Anticheat and HAC2 clients only. Note that default gravity value is ~0.003565.
New: sj_level [level(-1-5)] command: This is the unified command that replaces disable_sj and sj_admin_level commands. -1 means sightjacking is enabled for everyone, 5 means it's disabled.
New: $ticks variable that returns the number of ticks elapsed since the game start (1 sec = 30 ticks).
New: chat_message <player index> <message> [color r g b [a]] command that displays a colored chat message for the player for HAC2 users.
New: rcon_message <player index> <message> [color r g b [a]] command that displays a colored rcon message for the player for HAC2 users.
New: hud_message <player index> <message> command that displays a message on the HUD for the player in the "blue text" field for HAC2 users.
New: play_sound <player index> <sound index> command that plays the selected sound for the given player if he is a HAC2 user. See Lua scripting section for the list of possible sounds and the corresponding indexes (soon).
New: hud_color <player index> <color index> [red] [green] [blue] command that sets the HUD color for HAC2. See Lua scripting section for more information (soon).
New: spawn_object_location <type> <name> <location name> [rotation] [MetaID] command to spawn an object to a given location.
New: add_var <name> <type> command that adds a custom variable.
del_var <name> command that deletes a cusotm variable.
set_var <player_index> <name> <value> [value player index] command that changes a custom variable. See Lua scripting section for more information (soon).
lua_api_version and sapp_version global values in the Lua API.
Lua event is raised when an object is stick to a player: function OnStick(PlayerIndex, VictimPlayerIndex, Object, VictimObject, Where), PlayerIndex is the index of the player who stick the another player (victim), VictimPlayerIndex is the index of the player who got stick, Object is the memory address of the object that was stick to the victim, VictimObject is the object of the victim that was stick (note that it can be a vehicle too), Where is the index of the body part of the victim's object.
An event is now raised on assist: event_assist
Commands from Sapp's init.txt now won't be in the command history unless they are default Halo commands.
Improvement: Stats saved of left players with the save_scores command now won't be deleted at reload, however they will be deleted at map reset.
Improvement: Player's speed, health, and shield now won't be reset to 1 at reload/unload. God and lag mode is still reset.
Fix: Timed custom commands started form the init.txt now won't have output in the console. Such say commands will also have the prefix set with the msg_prefix command instead of the admin_prefix.
Fix: rand Lua API command now won't crash if the minimum and the maximum value is the same. It will simply return the given value.

Lua API version is now

Sapp 9.8.1 (2016. 08. 12)
Fix: disable_object command now disables objects for blues too when both teams are specified.
Battery is now instantly synchronized when the battery command is used.
New: sync_ammo Lua API function now has an optional second parameter to sync the secondary mag of the weapon. Use 0 (default) to sync primary, 1 to sync secondary mag.
Fix: $map, $mode and $gt variables are now initialized properly at Sapp load, before settings are loaded.

Sapp 9.8 (2016. 08. 09.)
New: EVENT_SCORE is now raised when someone scores a lap in race.
New: Added feature to block objects for individual players, the commands are the following:
block_object <player_expr> <object name with path>
unblock_object <player_expr> <object name with path>
You can also block every object and vehicle for the given player(s):block_all_objects <player expression> <block(0-1)>
block_all_vehicles <player expression> <block(0-1)>
Fix: Fixed the $score variable broken in 9.7

Sapp 9.7.2 (2016. 08. 06.)
Fix: Time left is now synchronized at map reset.

Sapp 9.7.1 (2016. 08. 05.)
New: Time left from the game is now visible on the scoreboard for the clients with Anticheat and HAC2 (soon).

Sapp 9.7 (2016. 08. 04.)
New: disable_timer_offsets command. Weapons and powerups will spawn exactly in the same rate as they were designed to (like in Halo Xbox).
By default in Halo PC and CE, the object respawn times are "stretched" from a 0 to 10 seconds range for some reason. From reversing the code, respawn timers looks like this:
actual_respawn_rate = object_respawn_rate + floor((netgame_equipment_number / total_netgame_equipments) * 300) ticks.
For example; if there are total 37 netgame equipments, and an equipment is #6 in the netgame equipments list, and it's original respawn rate is 900 ticks (30 sec), then the real respawn rate will be 900 + floor((6 / 37.0f) * 300.0f) = 900 + 48 = 948 ticks ~ 31,6s. The above command fixes this behavior, so objects respawn in the rate as it's defined in the map.
New: slayer_score and ctf_score were replaced by the score command, slayer_score_team and ctf_score_team were replaced with the team_score that works with every game type. The $score, $redscore and $bluescore variables now also return the actual score of the players/teams in every game type. The save_scores feature now also works with every game type, in race even the current lap's progress is saved.
Fix: Fixed a rare crash in no-lead mode.

Sapp 9.6.2 (2016. 07. 17.)
Fix: Fixed a memory leak in Lua timers that persisted until Sapp was reloaded/unloaded (since 9.6).
Fix: EVENT_END_GAME is now called in race gametype when the game ends naturally (someone wins).

Sapp 9.6.1 (2016. 06. 11.)
Fix: Fixed a bug from the previous version when normal players were level 0 instead of -1 initially.
Fix: st (team change) command now won't do anything in non-team games.

Sapp 9.6 (2016. 06. 04.)
New: Added usage for every command which will be printed if you use invalid parameters. You can also check the usage of a command with usage <command>
Changes: Some minor changes in the following command names (underscores were added):
admin add -> admin_add
admin add_manually -> admin_add_manually
admin list -> admin_list
admin change_pw -> admin_change_pw
admin change_level -> admin_change_level
admin del -> admin_del
loc add -> loc_add
loc del -> loc_del
loc list -> loc_list
loc listall -> loc_listal
area add_sphere -> area_add_sphere
area add_cuboid -> area_add_cuboid
area del -> area_del
area list -> area_list
area listall -> area_listall
New: CPU and memory load were added to the cpu command
New: Same info can be queried from Lua scripts:
local CurrentCPULoad, TotalCPULoad, MemoryLoad = system_status()
New: It was also added to the remote console protocol, see the Remote Console page for more info.
New: Added team scores to the remote console scores query.
Improvement: Updated the LuaJIT API to 2.1.0-beta2
Fix: Fixed a bug that caused random hangs/crashes. Thanks for blackfather (admin of the BK servers) for helping me tracking this one down.
Improvement: IP-bans are now stored in the ipbans.txt even if they are not permanent.
Improvement: Refactored huge part of the code again, I might not remember all the changes I made.

Check out the Remote Console web interface by 002.

Remote console version is now
Lua API version is now

Sapp (2015. 11. 10.)
New: Chat type were added to the Lua chat event.
Some aliases weren't always saved.
Fix: Some command's output was always echoed to Lua scripts.

Sapp 9.5.3 (2015. 11. 10.)
Fix: block_tc now always works, not only when autobalance is enabled in the gametype.
Improvement: Remote console is now bind to the same local IP that Halo uses.
Fix: read_* functions now return nil on NoAccess and Guarded addresses.
Improvement: read_* functions are much faster now when safe_read is enabled (but still significantly slower than without).
Fix: Executing commands from the console or the init.txt that are longer than 254 characters won't crash the server anymore.
Other: Small improvements and fixes.

Sapp 9.5.2 (2015. 10. 20.)
New: event_reset, runs when the sv_map_reset command is executed.
Fix: Fixed custom_sleep crashed Wine when called from the init.txt, in the last version.
Other: Minor improvements.

Sapp 9.5.1 (2015. 10. 18.)
Fix: Fixed broken sv_map (crashed) and lua_api_v (showed the old version) command and vehicle-exit hook from the previous version.
Fix: custom_sleep value will be set correctly when Sapp is loaded during a game.

Sapp 9.5 (2015. 10. 17.)

New: Tab-completion in the console now works with Sapp commands too.
New: hill_timer command, changes the time after the hill moves. Default is 60 seconds, works only in "Crazy King" (when moving hill is enabled).
New: antilagspawn command, enables/disables anti-lagspawn. It is disabled by default now.
New: disable_backtap command, if enabled, backtaps won't kill players instantly, just cause normal melee damage.
Improvement: Refactored every codecave, this result in minor performance improvement and also some potential bugs were fixed.
EVENT_DAMAGE_APPLICATION Lua event, it's called when a player takes damage. It is based on the "OnDamageApplication" event in Phasor, thx to Oxide.
Function signature is OnDamageApplication(PlayerIndex, Causer, MetaID, Damage, HitString, Backtap), where PlayerIndex is the index of the player who suffers the damage, Causer is the index of the player who caused the damage, MetaID is the Tag-Index of the damage object, Damage is the amount of damage made, HitString can be "head", "body" or "legs", Backtap is true if the damage was a backtap. It has two optional return values, first is a boolean if the damage should be enabled, the second is the new damage amount you want to apply.

Example snippet for doubling headshot damage:

function OnDamageApplication(PlayerIndex, Causer, MetaID, Damage, HitString, Backtap)
    if HitString == "head" then
        return true, Damage * 2

Lua API version is now

Also check out the new Lua API summary page by 002.

Sapp 9.4.1 (2015. 09. 24.):

Fix: The remote console query RC_QUERY_STATS now returns the correct Json.
Improvement: Better Json separating algorithm, now every Json query must be terminated by an end line character ('\n'), like the queries Sapp sends.

Sapp 9.4 (2015. 09. 12.):

Fix: Fixed the bug when server crashed if a Lua script blocked a command.
New: Added Json based Remote Console protocol. More details + client in the next week.

The remote console is using TCP ports, the default is the same as the server port (which is UDP).

remote_console [enabled]: Enables/disables the remote console
remote_console_port [port]: Sets the remote console port, only takes effect if used before enabling the remote console.
remote_console_list: Lists the connected remote clients.

Sapp 9.3 (2015. 08. 31.):

Improvement: Reduced Halo's CPU usage by ~5-10%.
Improvement: Completely rewritten no-lead mod, which significantly reduces it's CPU usage, up to ~90%.
New: event_prejoin, runs when a player machine joins.
New: $app even variable, 2: Anticheat, 4: HAC2 (will work when Btcc updates HAC2)
New: sj_admin_level command, enables the usage of HAC2's SightJacker for players with at least the given admin level, value can be between 0 and 5, default value is 5, which means no one can use SJ (will work when Btcc updates HAC2).
New: subtract_afks command, if enabled, the number AFK players will be subtracted in the player number on the server list, except if the server is full). Default value: 0
New: console_input command, if disabled, the console won't accept any input, this slightly improves reg, mostly useful in servers at hosting companies, where you don't have access to the console anyways. Default value: 1
New: The generic game type will be reported and visible on the serverlist.
Improvement: event_login will run every case a player logs in, even when it's automatic.

Sapp 9.2 (2015. 06. 04.):

Improvement: Improved the Anticheat client - Sapp communication. This should fix the problem for some players who were unable to authenticate themselves as AC users.
Note that to be able to play with anticheat, the server always must have the latest Sapp version!
New: antiglitch, which kills the player if he is out of the BSP, useful in maps such as Danger Canyon or Coldsnap. Thx for 002 for this great find.
New: lua_list command that lists the currant state of the Lua scripts in the server.
gamespeed [speed] command which will be synchronized with the HAC2 clients too when it will be ready.
New: zombies [team] command, this will give some information for a future HAC2 with new medals about which team is the Zombies team. 0 - none, 1 - red, 2 - blue
Improvement: MTV and Disabling SJ will be instant too with the new HAC2.
Improvement: event_score has it's own codecave now, like this way it won't be triggered when you set it manually with the ctf_score command.
Fix: Fixed the Time Left feature in the info command which broke when the map was reset.
Fixed the rprint command
New: New Lua API function, powerup_interact(ObjectID, PlayerIndex) which makes the player interact with a powerup spawned with the spawn_object function. It returns false if the player was dead, the ObjectID was invalid, or the object wasn't an equipment, otherwise true, even if the player couldn't interact with the object, for example because he had already full HP or Over Shield.
Improvement: enter_vehicle function now returns false if the player was dead, the ObjectID was invalid, or the object wasn't a vehicle, otherwise true.
Lua API version is now

Sapp 9.1 (2015. 05. 27.):

Change:  Anti-lagspawn is now only active in the first second instead of the first 2.
Fix: Servers should not "idle-out" randomly anymore (this happened mostly with modded maps). This was a Halo-bug.
Fix: Fixed the mtv command, players can't enter a seat anymore which is already taken.
Improvement: Improved the files and the info command. The files command now shows the full path for every file, the info command now shows the time remaining from the game.
Other: Updated LuaJit to 2.0.4, this mostly contains bugfixes, no change happened in the API.
Other small fixes and improvements.
Compiled with Visual Studio 2015 RC.

Sapp 9.0.2 (2015. 04. 13.):

Fix: Fixed a bug from the previous update where float numbers didn't got parsed if they had a "." in them.
Fix: Anti-lagspawn now should correctly disable jumping in the first two seconds, but it might be still not perfect with laggy players.
Fix: Fixed a lot of typos and grammar mistakes, thx to 002.

Sapp 9.0.1 (2015. 04. 12.):

Important: Anticheat now only works with this latest version!

Fix: vdel_all command will delete every vehicle.
Fix: Player death was buggy when a vehicle was destroyed by a script on some maps.
Fix: Lua script's main chunk was loaded every time when lua_load command was called, and after some loads, this crashed the server.
Fix: Players now can't melee while zooming and can't jump and melee in the first second after they spawned, this fixes phantom-shooting and lag-spawn.
Fix: cheat_spawn_warthog and cheat_all_* commands now disabled in scrim mode.
Fix: Giving wrong arguments to the w8 or wait commands now won't stop the whole sequence to execute, only the w8/wait commands will be ignored.

Sapp 9.0 (2015. 04. 03.):

New: Default Halo commands will have output even if they are executed from the chat, password and rcon_password command were removed due to being deprecated now.
New: Some naughty commands will work without the "me" expression. For example "kill" is equal to "kill me" now. This only works if there are no other arguments or they are optional.
New Command: setadmin, it will make players admin for the current session. Level must be between -1 and 4. Set level to -1 to delete their admin level.
Fix: mapcycle_begin and map_spec commands will disable mapvote now.
Fix: Improved reload command that fixes some bugs that happened after executing it.
New: V2 admins will be automatically logged in at the first time they execute an rcon command with their password, also if sapp_rcon is disabled (not recommended), anyone will be able to login with the default rcon password, use the chat as rcon and not getting kicked for being afk or having high ping. Note that now sapp_rcon must be enabled for the login command to work as the same way as before for the V2 admins, otherwise it will try to compare the password entered to the default rcon password.
New Commands: stfu and unstfu that blocks/unblocks every server and rcon message to you which is generated by events or scripts.
New Command: mapvote_begin, that will launch the game with a randomly chosen mapvote from the first max_votes options.
Fix: Some other small fixes with the mapcycle and mapvote.
Fix: Some Prefs command threw error messages if they were enabled already.
Change: wdel command now deletes the current weapon from the players hand if you specify 0 as the second argument, if you only specify the player index (or 5 as the second argument), it will still delete every weapon.
Change: The boost and the teleport commands will be more instant now.
Change: The $score variable now also works with slayer gametypes.
Fix: Sometimes Sapp counted a player as dead, and therefore some player commands didn't work with him.
Fix: Improved the way Halo kills the players when they quit, now they shouldn't stuck in the server on some modded maps where fall damage is 0.
Improvement: The main loop timer (defined with the custom_sleep command), therefore the pings, the timed events and the Lua timers will be more accurate and effective now.
Improvement: Added thread-pool and memory-pool for Sapp to reduce overhead with lot of events and scripts.
Improvement: Lot of other optimizations to reduce overhead at (Lua) event callbacks.

Lua API:
Change: Now only the syntax checking will run at Sapp load, the main chunk will be only called at the lua_load command.
New: Added 2 global values; halo_type that contains either "PC" or "CE" and pid that contains the Process ID.
New: Added rprint function that lets you to send rcon messages to the player.
New: Added intersect function that checks for collisions form a given point and a vector. First 3 is the starting coordinates, second 3 is the vector, 7th (optional) is the ObjectID to ignore, usually this belongs to a player who's camera vectors you use. Don't forget to multiply the camera vector's coordinates by about 1000 otherwise the intersect won't "reach" anything. Returns values are success,x,y,z,object_hit. success is true if there were an intersection, otherwise false. x, y, z are the coordinates of the intersection, object_hit is the ObjectID if something was hit, otherwise 0xFFFFFFFF.
New: Added lookup_tag Lua function that returns the address of the tag entry and has two variants, lookup_tag(MetaID) and lookup_tag(type, name).
The tag entry has the following structure (Class0 is the 4 letter type):
struct Tag_Entry
    DWORD Class0;
    DWORD Class1;
    DWORD Class2;
    DWORD MetaIndex;
    char* TagName;
    void* TagStruct;
    BYTE Unknown[8];

Change: execute_command is updated and now takes 2 other optional arguments. Example: execute_command("kill", PlayerIndex, true). The command will be executed as the "PlayerIndex" player were executing it, true means that you will get the output of the command in the "EVENT_ECHO" callback that will be described below. Now it also returns a boolean, false if the command was unknown, wrong or "can't be used now", otherwise true.
New: Added execute_command_sequence function that executes commands separated by semicolons (like the events and the custom commands), it works in the same way as the execute_command except it has no return value.
Fix: Rotation will work with everything now with the spawn_object function (and also with the spawn command), and not only with vehicles.
Fix: The to_real_index command returned wrong converted index if the supported index was invalid.
Change: The assign_weapon function now returns a boolean if it was successful or not.
Change: The spawn_object function now returns 0xFFFFFFFF as ObjectID on an error.
Change: The camo function now takes the time in ticks instead of seconds (1 second = 30 ticks).
Improvement: The optimized the get_var function, now it should have much less overhead.

New Lua events:
"EVENT_COMMAND" that runs whenever a player executes a command.
Example: function OnCommand(PlayerIndex, Command, Enviroment, Password), where PlayerIndex and Command are obvious, Enviroment: 0: console, 1: rcon, 2: chat, Password is the password used in the rcon, otherwise a nil. This event is executed before Sapp checks for admin permissions, and the command will be blocked if the function returns false (same as EVENT_CHAT).

"EVENT_ECHO", Example: function OnEcho(PlayerIndex, Echo) that will return the outputs of the command in the "Echo" parameter if the execute_command were used with the true third parameter. Note that this is called once for every line.

"EVENT_OBJECT_SPAWN" callback which is called when an object is being created. The signature of the callback function is OnObjectSpawn (PlayerIndex, MapID, ParentID, ObjectID)
PlayerIndex is the Index value of the associated player (or 0), MapID is the "MetaIndex" aka the tags table ID, ParentID is the ObjectID of the parent object or 0xFFFFFFFF if there is no parent, ObjectID will be the ID of the object being created, but it's not valid until the callback returns. If you return false, the object creation will be blocked, otherwise you can return a second parameter which is the MapID of the object that you want to change the object which is being created. Note that it must have the same type, otherwise this parameter is ignored.

New: Better error reporting; if an error happens in your Lua script, not only the error information will be a bit more detailed, but the OnError function will be called in your script (only if you declared it). Just add the following code to (possible the top) of your script and you also get stack trace information that will might help a lot:
function OnError(Message)
You can also use StackTracePlus which gives even more information.

New: Added read_bit and write_bit functions. Usage:
read_bit(address, bit)
write_bit(address, bit, value)
The address always belongs to a byte, therefore the bit parameter should be always between 0 and 7, and the value parameter will be always converted to 0 or 1.

api_version = ""


Fix: Map caching added for 1.10 exe's, this strings.dll won't work with older servers anymore, please use the exe provided in the archive.
Fix: Now it fixes the "Using profile path ." bug in the server console, the path will be displayed properly.
Fix: Small fixes for the client and for Anticheat login.
Other: It's also packed with UPX now to reduce the size.

Sapp 8.7.2 (2015. 02. 20.):

Fixed false aimbot detection that happened randomly at player spawn.
st/teamup/balance_teams commands now won't give -1 death for already dead people.
Other minor fixes and optimizations.


Fixed the bug where anticheat usernames were displayed incorrectly in servers where anticheat is not enabled.

Sapp 8.7.1:

Updates to support new features for the anticheat. Players with Anticheat ON won't be able to join to lower versions anymore. Please update your server ASAP if it didn't do it automatically yet. Read more here.

Custom maps will be automatically loaded for Halo PC, you don't have to use the "map_load" command anymore in the init.txt. Also Sapp won't try to checksum at map_load in Halo PC since it's useless and caused crash at some badly converted maps.
Potential fix for aimbot-scan being too sensitive and generates many false positives.

Sapp 8.6.4:

Bug fixes:

Map won't get automatically skipped anymore when the last player leaves.
Unbanning an IP-range ban will unban the proper IP now.

New features:

Sapp will use the custom IP to connect to the global server if it's specified with the -ip switch.
query_add will now overwrite the existing values, query_del can be used with the key name to delete a query pair.

Sapp 8.6.3:

Bug fixes:

destroy_weapon won't crash the server anymore.
query_delete command won't crash the server with too big index.
AFK kick won't kick players anymore when they are in passenger/gunner seat in the vehicle for long.
Setting too big value for clead won't cause random lead (sometimes even negative) anymore.

When a player leaves, Sapp will recount the "skips" and runs the next map if there are enough votes.

Sapp 8.6.2:

Fixed AFK timer when players are in vehicles.
Players using the /afk command won't respawn anymore at new game.

Sapp 8.6.1:

AFK timer won't reset anymore if a frag grenade explosion hits the player.
Fixed the bug when antispam always used textban even if it was set to mute.
sapp_mapcycle will now automatically disable mapvoting if it was enabled and vice versa.

Sapp 8.6:

New event:
event_prespawn: Runs at player spawn, before client notification.

New variables:
$afk: The afk timer of the player.
$ping: The ping of the player.

Mapvote now checks if map and gametype are correct before they are added.

Lua API version changed to

Sapp 8.5.2:

Fixed the cmd_add and cmd_del command taking wrong argument as command name.
Fixed when the server sometimes hang at unload if there were a Lua timer running.

Sapp 8.5.1:

Fixed the bug that the 16th wasn't recognized.
Fixed the bug that the aimbot_ban command wasn't working after the first time it's used.

Sapp 8.5:

Bug fixes:
bans command will be fixed and won't crash the server.
ipbans command will be fixed too and it won't crash the server if two admins ban the same player in the same time.
"Amount" expressions for the battery command (For ex.: battery $n +10) will work properly (did anyone noticed they don't?). Not like there is any point using it since the battery must be between 0 and 100 otherwise it won't really work.
Lua timers will stop when a script is unloaded.

Removed or changed commands:
cmd list: Removed, same as "list custom", it was always undocumented.
cmd change: Removed, undocumented, unpractical.
cmd add: Changed to cmd_add, added documentation.
cmd del: Changed to cmd_del, added documentation.
despawn: Removed, useless.
olist: Removed, useless.
c4_ag: Removed, people used it in every map without knowing what it does or how it works, you can write a better one with Lua scripts anyways.
event: Removed, undocumented, unpractical (could add an event in the same way they are in the events.txt, but only worked form the console because the "$variables" got replaced otherwise).
clear_object_cache: Removed, undocumented, got deprecated with optimizations.
cmdstart: Renamed to cmdstart1.
cmdstart1: Renamed to cmdstart2.

New command:
wdrop: Forces player to drop his current weapon.

New event:
event_tick: It will be called every thread cycle (30 times / sec), so you don't have to create Lua threads (timers) just for this.

New Lua API functions:
read_vector3d and write_vector3d: They read/write 3 floats in the same time, useful for getting player coordinates or other 3D vectors.
exit_vehicle: Ejects the given player from the vehicle.
drop_weapon: Forces the player to drop the current weapon in his hands.
camo: Makes a player invisible.
kill: Kills the given player.
unregister_callback: Unregisters a Lua callback.
Visit Lua scripting page for more information.

eventdel and cmd_del won't affect the files anymore.
enable_object command now can take the object name too, not only the index (why I didn't do it like this at first o.O)
vdel command is split into two commands; the original vdel which now takes a player expression, and to vdel_all that deletes every vehicle spawned with Sapp.
Added documentation for custom variables.
Added "unalterable" attribute for some commands which means you can't change their name or level.
These commands are the following: about, info, lead, clead, login, change_password.
Spawn event is moved back after client notification since it caused clients crash with some mods that used the wadd/wdel commands.
Kill command doesn't display the "xy died" message anymore when the game is ending.
Aimbot detection will detect lock-ons.

Due to a change in a network structure, querying maps for download won't work correctly from older Sapp versions.

Sapp 8.4.1:

Fixed the bug that alias command sometimes showed names duplicated.

Improved thread handling, unloading Sapp shouldn't randomly freeze the server anymore.

Sapp will work on Windows XP again, you can download the XP version from the downloads page.

Sapp 8.4:

New features:
Added timer function for Lua API.
timer(<milliseconds>, <callback>, [arguments]...)

This means that the "callback" function will be called with the given argument(s) after the specified "milliseconds" passed. If the callback function returns true, the timer will be called again with the same arguments, if returns false or nothing then it won't be called again.

For example if you want a function to run once after 5 seconds a player joined, then it looks like:

function OnPlayerJoin(PlayerIndex)
    timer(5000, "hello", PlayerIndex)

function hello(PlayerIndex)
    say(PlayerIndex, "Hello, "..get_var(PlayerIndex, "$name").."!")
    return false

This equals to the following:
event_join 'w8 5;say $n "Hello, $name!"'

An example for a "ticking" timer:

stop = false
ticks = 0
function OnGameStart()
    stop = false
    ticks = 0
    timer(1000, "test")

function OnGameEnd()
    stop = true

function test()
    ticks = ticks + 1
    say_all(ticks.."s passed since game start)
    if stop == false then
        return true
        return false

Note that doing anything from the scripts is fully thread safe.

OnScriptUnload will be called now at unload/reload commands for every script that was loaded.
Fixed that PlayerIndex was broken if there were more than one script registered for a callback.

LuaAPI version is now

Vehicles spawned with Sapp will now respawn automatically using the vehicle respawn timer defined in the gametype.

Added "rotation" option for object spawning (only works with vehicles when spawned to a given coordinate).
spawn <type> <name> <x> <y> <z> [rot]
The value is in radians.

Option for spawning object based on their MetaID (for protected custom maps) from Lua scripts.
spawn_object(<type>, <name>, [[x], [y], [z]], [rot], [MetaID])

For example if you want to spawn a weapon in a protected map and give it to a player:
local obj = spawn("weap", "thatweaponyoualwayswanted", 0, 0, 0, 0, 0xE25E00EA)
assign_weapon(obj, PlayerIndex)

If the MetaID is specified, you don't have to give a real path to the object, only should give the type correctly.
You can get the MetaID of a weapon or a vehicle with this Lua script.

Other changes:
Spawn event is now before client notification.
Vehicle enter event now have a "$seat" special argument.
Mapvote allows the same option multiple times.
Ping kicking works properly again.
The afk command is now "naughty" (can't be used in scrim mode).
Reduced CPU usage of no-lead mod especially with vehicles.
Lot of code optimizations and bug fixes.
Sapp is now packed with UPX to reduce the size.

Sapp 8.3:

Warning! Some of these features aren't fully tested due to lack of time. Please use them cautiously and report bugs on the forum.

Added safe_read, safe_write and sig_scan functions. Please read the Lua scripting page for more details.

Lua API version is now

Removed double broadcasting since GameSpy shut down. Also removed v1 and v2 commands.

Fixed the bug when admins didn't got checked against the IPs specified in the users.txt

Mapvotes now can have player ranges just like the mapcycle. Example entry from mapvotes.txt:

bloodgulch:ctf:Bloodgulch CTF:4:12

Which means mapvote option will only be shown if there are 4-12 players in the server.

Sapp 8.2.2:

Since it looks like GameSpy won't shut down for another month, I added back v1, v2 commands and double broadcasting.

Sapp 8.2.1:

Fixed a bug when Sapp crashed at reload command if no map was running.

Added new command to call a lua script function:
lua_call <script name> <function name> [arguments]

For example if your "test.lua" script has a function like:
function TestFunc(index, msg)
    say(tonumber(index), msg)

Then it can be called from Sapp like lua_call test TestFunc 1 Hello!

Note that every argument of the function is passed as a string.

Lua API version is now


Sapp 8.2:

disable_all_objects command now does not effects interacting with vehicles. Instead, use the new command; disable_all_vehicles to prevent teams entering any vehicle.

Replaced random number generator function in Sapp to Windows's default CSPRNG.

Removed the $boolrand variable.

Added some more Lua API commands to make scripting easier:

say(PlayerIndex, message)

Sends a chat message to the given player (equals to say $n "message").


Sends a chat message to every player in the server (equals to say * "message")

cprint(message, color)

Prints a message to the console, the color argument is a number and optional, if not specified the default console color will be used.

rand(min, max)

Returns a cryptographically secure pseudo-random number.

If no arguments are specified, the number will be between 0 and 2^31, if only 1 argument is specified, the number will be between 0 and the specified argument, if both, then the random number will be between the two arguments. Note that the random number can be the minimum value, but the maximum will be always one less than the max argument. For example: rand(1, 5) will return either 1, 2, 3 or 4.

Lua API version is now


Sapp 8.1:

Removed the gs_ping_fix command, since it's not needed with the new GS-emulator.

Rewrote event_wpickup and added event_wdrop.

event_wpickup now has two special arguments.
$type: 1 means weapon, 2 means nade
$index: If $type is 1, this is the weapon slot where the new weapon got added, can be 1 to 4. If $type is 2, this is the type of the nade that got picked up, 1 means frag nade and 2 means plasma nade.
Edit: Please note that now if you wdel from event_wpickup, and then assign a new weapon to the player, it will trigger event_wpickup again. Instead, just use the disable_all_objects command to prevent the specified teams picking up any weapon. Check the Naughty Commands page for more information about the command.

Corresponding Lua event can be like:
function OnWeaponPickup(PlayerIndex, index, type)

event_wdrop: Called when a player drops a weapon or a flag\oddball.
Special argument is $index, which is the number of the slot where the player dropped the weapon from, can be 1 to 4.

Corresponding Lua event can be like:
function OnWeaponDrop(PlayerIndex, index)

Added some more Lua API functions that you can call from your script:

Converts back to PlayerIndex (1-16) from real player index (0-15), 0 means that the player is not in the server.

Returns true if the player is present in the server, false if not.

Returns true if the player is alive, false if player is dead or not in the server.

Lua API version is now

Sapp 8.0:

From this version, Sapp stops broadcasting to the old GameSpy master server, and automatically replaces the DNS to Btcc22's emulator, and also changes the version to
The v1, v2 and vspec commands got removed.

Mayor change in map downloading: From now on, Sapp downloads maps compressed as .7z instead of .zip, this will result faster downloading and extracting.
All Sapp version before 8.0 will be unable to download maps in the future.

If you get the red 'Error: Failed to find signature for "QueryMapName Fix"!' message in the console with the new 1.10 exe, just ignore it. It was a bug in older exes that got fixed in 1.10, so Sapp doesn't find it anymore.

Added basic Lua API, which is using LuaJIT-2.0.3.

Important notes:

First of all, please don't use any Lua script that you don't know to be 100% safe.

Later I'll have a list of safe scripts here.

Also, it's not compatible with Phasor scripts at all, so please don't try to load them and then wonder why it's not working.

This feature is for users who already have experience with Lua, or for people who want to learn it by themselves. I can't give any support about scripting in Lua, so please only talk to me about this if you found a bug or have an idea what else I should add.

Sapp will try to process every .lua file from the gametypes\sapp\lua folder and compile them. You will likely to get an error message here if you made a syntax mistake in your script and it won't be available.

To enable lua callbacks, use the lua 1 command. This is the general switch to enable\disable calling any lua scripts.

However, to make a lua script active, you have to use the lua_load <script_name> command. If you want to disable a script, use the lua_unload <script_name> command.

Things that must be in your lua script:

API Version: This is the version of the Lua API of Sapp that your script is compatible with, in this first version it's
So when you writing your script, put the actual version from the latest Sapp into your script file like:
api_version = ""

You can get the current version from Sapp with the lua_api_v command.

If the mayor API version is different in your script that the one in Sapp that tries to load it, it means they are not compatible and your script won't be loaded.
If the minor version is different, you will get a warning message, but Sapp will still load your script, but it might won't work correctly.

function OnScriptLoad()
function OnScriptUnload()

This two functions are necessary, they will be called when you use the lua_load and lua_unload commands.
In the first one, you have to register the callbacks that you want Sapp to call your function at an event.
For the best performance, only register the callbacks that you actually need and using it.

For Example:
function OnScriptLoad()
    register_callback(cb['EVENT_SPAWN'], "OnPlayerSpawn")
    register_callback(cb['EVENT_DIE'], "OnPlayerDeath")
    register_callback(cb['EVENT_CHAT'], "OnChatMessage")

function OnScriptUnload()
    -- Not really necessary to fill this if you have nothing to reset.

And your function will be:

function OnEventDie(PlayerIndex)

function OnEventDie(PlayerIndex, Killer)

And a special one:

function OnChatMessage(PlayerIndex, Message)
    return true

This is the only event that has a return value, a boolean. If you return true, the chat message will be visible, if false, the chat message will be blocked.
Message is just a copy of the original chat message, therefore you can't modify that.

cb is a table of the possible callbacks, which are the following:


You can give any name to your functions. The first parameter is always the player index (1-16), which is used for Halo and Sapp commands.
If there are special arguments, they will be the second, third, etc. parameters, but atm. every event has only 0-1 special arguments.

Sapp functions that you can call:

Executes any Halo or Sapp command.
execute_command("k "..PlayerIndex.." 'no reason'")

get_var(player_index, var)
Returns any default or custom sapp variable.
local hp = get_var(PlayerIndex, "$hp")
local warnings = get_var(PlayerIndex, "$warnings")

The following functions are only for advanced users:

Returns the real index (0-15) that Halo uses for player tables and some other score tables, -1 if the player is not present.

Returns the memory address of the player's player table entry.

Returns the memory address of the dynamic player object. 0 if the player is not present or dead.

Returns the dynamic memory of an object. 0 if the object doesn't exist anymore.

spawn_object(type, name, x, y, z)
Spawns an object to the given coordinates and returns it's ObjectID that you can use in the other commands.
local ball = spawn_object("weap", "weapons\\ball\\ball", 12.34, -20.5, 10)
Note that here you have to use double backslash in the object names.

Destroys the given object.

Synchronizes the ammo for the given ObjectID (weapon).

assign_weapon(ObjectID, PlayerIndex)
Assigns the weapon with the ObjectID to the given player.

enter_vehicle(ObjectID, PlayerIndex, seat)
Enters the player to the vehicle with the given ObjectID into the specified seat.

Memory management functions:

Note that these functions have no security check if you pass them a valid address or not, and if you don't you will probably crash the whole server, so be careful when using these.
Read functions take one argument, the address to read from, and returns the value they have read.
Write functions take two arguments, the address to write to, and the value to write, and have no return value. They are not able to write read-only memory regions (like Halo's asm code etc.).
Later I'll probably add safe_read_* and safe_write_* functions.

Functions to read memory:


Functions to write memory:

    write_char(address, value)
    write_byte(address, value)
    write_short(address, value)
    write_word(address, value)
    write_int(address, value)
    write_dword(address, value)
    write_float(address, value)
    write_double(address, value)
    write_string(address, value)

(byte is unsigned char, word is unsigned short, dword is unsigned int, float is 4, double is 8 byte floating point numbers.
strings are array of chars, and not wide-char strings (with 2 byte characters such as player names).

Sapp 7.3.1:

Bug fixes:

event_leave now should work again on Halo PC 1.10.

Memory leak is fixed for Halo 1.10.

cevent <name> [player_number]  Now should always affect the correct player, if no player_number specified, will apply to the player or player's event who executes it, same goes for var_set now.

Sapp 7.3:

There will be an official 1.10 patch to save Halo, and from this version, Sapp will also broadcast to the new master server (made by Btcc22), which is going to completely replace the current gamespy server after May 31. Note that until then, servers will be on both serverlist.
Read more here about this topic.

New features:

From now you can use custom variables to store text/numbers and use them for more flexible modding.The commands are the following:

var_add <name> <type> Adds a new variable, where type 0 - global string, 1 - global int, 2 - global float, 3 - player string, 4 - player int, 5 - player float
Globals are containing only 1 variable, while player ones has a different variable for each player.

var_del <name> Deletes the given variable.

var_set <name> <value_expression> [player_number] Sets a given value, player_number is only required for player variables.

var_conv <name> Converts int variable to float and vice versa, not available for string.

var_list Lists all variable name and their types.

Some simple examples for usage for globals:

This makes a time command that tells since how long the current game is running:
var_add time 1
var_add min 1
var_add sec 1

event_start 'var_set time 0'
timeloop 'w8 1;var_set time +1;timeloop'
time 'var_set min $time;var_set min /60;var_set sec $time; var_set sec `;say $n "Current game is running since $min minutes and $sec seconds!"' -1

This one sets the assists to everyone's health:
var_add health 2
(As a global variable, since it's only used as temporary, no need one for every player)

event_alive 'var_set health $hp;var_set health *100;var_conv health;cevent hpassists $n;var_conv health'
event_custom $ename:hpassists 'assists $n $health'
event_die 'assists $n 0'

And a simple warning system and for player variables:
var_add warnings 4
event_tk 'var_set warnings +1 $n;say $n "You got a warning for betraying! ($warnings)";cevent warn'
event_custom $ename:warn $warnings=3 'say $n "One more warn and you will be kicked, $name!"'
event_custom $ename:warn $warnings=4 'k $n "Too many warnings."'
warn #n 'var_set warnings +1 #n;say #n "You got a warning from admin $name! ($warnings)";cevent warn #n' 3

Other changes:

cevent command now has a second argument to specify the player number.
ip should not freeze forever anymore when can't return the DNS.
Fixed map_next and map_prev commands crashing when there was no Sapp mapcycle running.
Fixed some code for the version 1.10


Sapp 7.2:

Server messages now have two different prefix, ** SAPP ** for auto-messages and events, and ** ADMIN ** for messages that an admin typed "real-time".
You can set the first with the msg_prefix [text] and with admin_prefix [text] the second one.

No-Lead now works on vehicle-guns, and also using less CPU, except if many player is on moving vehicles, the CPU usage can increase significantly.

Improved aimbot-detection a bit.

list command is now level -1 and only lists the commands that you have rights to use.

Fixes some rare crash with the $ip variable and some other cases.

New event variable: $boolrand, returns a random 0 or 1.

random_bonus command has been removed, since you can do it with events and custom commands.

Optimization in command and event execution.

There might be some other changes I don't remember, if so, report to me on xfire, also if you found (new) bugs.


Sapp 7.1:

The real version 7!

New threading core, this will result better performance and faster unloading.
Fixed some memory leaks at the object spawn command, some player commands, and at unloading.
Fixed huge memory leak in Halo at player join, now your server's memory usage won't keep growing until you restart it.
Faster command and event execution with less memory usage.
Basic arithmetics and custom event for events system:
Now you can use arithmetic operations on the condition variables. Allowed operators are: +, -, *, /.
Note that the variable must be in the left side and the modifier number in the right side.
New event type: event_custom, special argument: $ename. To launch a custom event, use the cevent <ename> command.
event_score $reds>$blues+1 'say * "Poor blues, that wasn't really fair, $name!"'
event_score $blues>$reds+1 'say * "Poor reds, that wasn't really fair, $name!"'
event_leave $reds*2<=$blues 'say * "The teams are too unbalanced!";st randblue'
event_leave $blues*2<=$reds 'say * "The teams are too unbalanced!";st randred'

With custom events:
event_custom $ename:autobalancered $reds>$blues+1 'st randred;say * "Teams Have Been Balanced"'
event_custom $ename:autobalanceblue $blues>$reds+1 'st randblue;say * "Teams Have Been Balanced"'
event_leave 'w8 15;cevent autobalancered'
event_leave 'w8 15;cevent autobalanceblue'

teamup: Will set up the two teams based on the tags in the player names.
This only works if at least one team has proper tags and all are the same, otherwise it won't be always 100% accurate.

The loader.dll is included in the downloaded archive, which is the same as Anticheat's loader, it also reduces CPU usage at loading maps both for server and client.


Sapp 7.0.1:

Fixed the bug when some players didn't respawn.

Sapp 7.0:

This version supports client sided anticheat.
To enable anticheat in your server, put anticheat 1 to the sapp's init.txt
Note that once it's enabled, this feature can't be disabled until you restart your server!
Also, you will need the anticheat client in order to play in anticheat servers!

The info command now shows the sapp version, and the state of the nolead and anticheat mode too.


SAPP for Halo PC


SAPP for Halo CE