Creating Sandbox 1.0 Addons
From Sandbox Mod Wiki
|
Introduction
Sandbox 1.0 supports "addons", small scripts that you can plug into your server to add extra functionality to the Sandbox core. With these, you can add new commands and features to your server without having to modify any of the base Sandbox code - thus, you can easily transfer your code between servers and you don't have to worry about Sandbox updates deleting it. Also, each addon will immediately stop and report any errors that occur, rather than taking all of Sandbox down with them.
Sandbox addons are single .py files, that go into mods/sandbox/python/game/gamemodes/addons/. You can select which addons to load by modifying the pertinent setting in mods/sandbox/settings/sandbox/addons.ini.
Creating an Addon
Quick Start
Here's a sample of a very simple add-on that adds the classic @ju [n] chat command to the server. This command moves the player up by [n] meters, thus making him "jump." This code is stored in mods/sandbox/python/game/gamemodes/addons/jump.py.
NAME = "Jump" VERSION = "1.0" AUTHOR = "Elton 'Elxx' Muuga" WEBSITE = "http://labs.elxx.net" COMMANDS = { "ju": ["distance","Jump upwards a total of [distance] meters."] } class Addon: def __init__(self, core): self.core = core def chat_ju(self, p, params): distance = float(params[0]) pPos = p.getPosition() p.setPosition((pPos[0],pPos[1] + distance,pPos[2]))
The first few lines are made up of metadata that describes your addon.
NAME = "Jump" VERSION = "1.0" AUTHOR = "Elton 'Elxx' Muuga" WEBSITE = "http://labs.elxx.net" COMMANDS = { "ju": ["distance","Jump upwards a total of [distance] meters."] }
The subsequent lines define the base class, common to all addons.
class Addon: def __init__(self, core): self.core = core
Sandbox depends on there being an Addon class with an __init__(self, core) function. The Sandbox core is passed to this function to let you modify Sandbox variables from within your add-on.
def chat_ju(self, p, params): distance = float(params[0]) pPos = p.getPosition() p.setPosition((pPos[0],pPos[1] + distance,pPos[2]))
This function utilizes a new event in Sandbox 1.0. You can create a function with the form chat_anything - when someone says @anything foo bar, that function is called with p being the player who said it and params holding a list of the parameters separated by spaces - in this case, it would be ['foo','bar']. In this addon, we grab the first parameter in that list and convert it to a float since we're expecting it to be a number that the user has input to say how high/low he wants to jump.
As you can see, some new methods have been added to the player object as well. p.getPosition() is just a shortcut for p.getVehicle().getPosition() - the same applies for p.setPosition().
Metadata
You can add certain variables to your addon file that will be displayed when a user uses the @ainfo command to get information about the add-on.
NAME = "My Addon" VERSION = "1.0" AUTHOR = "Me" WEBSITE = "http://www.google.com" DESCRIPTION = "This addon is awesome." COMMANDS = { "pie": ["amount flavor","Give yourself [amount] number of pies with the flavor of [flavor]."] }
All these variables will be displayed to the user. You can leave out any of them, none are necessarily required. In addition, there are some special variables you can set:
HIDDEN = 1If the HIDDEN variable is defined (with any value), your add-on will not show up when a player uses the @listaddons command. In addition, they will not be able to use @ainfo to get any information about your add-on.
LEVELS = ["strike_at_karkand","crystal_coast"]
If the LEVELS variable is defined (as a list), your add-on will only be automatically loaded (by addons.ini) if the current running level is in the LEVELS list. You can still load the addon manually later using @loadaddon.
GAMEMODES = ["gpm_cq","gpm_coop"]
If the GAMEMODES variable is defined (as a list), your add-on will only be automatically loaded (by addons.ini) if the current running gamemode is in the GAMEMODES list. You can still load the addon manually later using @loadaddon.
Reloading & Debugging
Sandbox 1.0 has several commands that can help you debug your addons without having to disconnect from the currently running game to make changes.
@reloadaddon addon1 addon2 - Reload the listed addon(s) if they are currently loaded.
@loadaddon addon1 addon2 - Load (activate) the listed addon(s) if they are not currently loaded.
@unloadaddon addon1 addon2 - Unload (deactivate) the listed addon(s) if they are currently loaded.
@reloadalladdons - Reload all currently loaded addons.
@unloadalladdons - Unload (deactivate) all currently loaded addons.
Events
Here's a list of all the events you can use in your add-ons. Just define a function with this exact name and variables, and it will be fired each time the event happens. No more dealing with having to assign event handlers and crap.
With most of these events, if you return True, processing of all other functions linked to the event will not occur, effectively allowing your add-on to "hijack" the event and completely replace the Sandbox code that would normally handle it.
onGameStatusChanged(self, status) onTimeLimitReached(self, value) onPlayerDeath(self, playerObject, soldierObject) onPlayerKilled(self, victimPlayerObject, attackerPlayerObject, weaponObject, assists, victimSoldierObject) onPlayerSpawn(self, playerObject, soldierObject) onPickupKit(self, playerObject, kitObject) onDropKit(self, playerObject, kitObject) onEnterVehicle(self, playerObject, vehicleObject, freeSoldier) onExitVehicle(self, playerObject, vehicleObject) onVehicleDestroyed(self, vehicleObject, attackerObject) onPlayerConnect(self, playerObject) onPlayerDisconnect(self, playerObject)
Check the BF2 Technical Information Wiki for information on these events.
onDeleteObject(self, object)
Fires when an object is about to be deleted, for any reason (before it's deleted - you can return True to cancel the delete).
onGrabTimer(self)
Fires on each tick of the grab timer. Return a value of True and the Sandbox processing of the event will be cancelled.
chat_XXXX(self, p, params)
Fires when @XXXX is typed by player p in-game. Also given is a list of parameters after the command.
chat_post_XXXX(self, p, params)
Same as chat_XXXX, except this fires after Sandbox has processed the built-in chat_XXXX event.
rcon_XXXX(self, p, params)
Fires when rcon XXXX is called by player p in-game. Also given is a list of parameters after the command.
rcon_post_XXXX(self, p, params)
Same rcon_XXXX, except this fires after Sandbox has processed the event. Useful for when you want to find what object a user just grabbed, for example.
Timers
To simplify timer creation & use and to reduce server lag, Sandbox 1.0 runs on just two timers. One is called every 0.0001 seconds (basically as fast as possible), and is accessed by defining a function:
tickFrame(self)
All code in that function will execute on every frame - use this if you're doing something that needs constant input, such as moving an object smoothly. However, if you need a timer for something that needs to be done periodically but not quickly, use:
tickHalfSec(self)
All code in this function will execute approximately every 0.5 seconds.
tickOneSec(self)
All code in this function will execute approximately every second. Of course, you can use a variable to count off every 10, 30, etc. seconds if needed.
Methods
Player
Unlike earlier versions, Sandbox 1.0 does not use the pD global variable to store data about each player. To store an attribute for a player, you can just directly define it:
p.timesHeLiedAboutReleaseDate = 8
Now, whenever a player object is passed to an event, you can access that attribute. Remember that you'll get an error if it hasn't been defined, though! You should always define all the attributes you're going to use right when a player connects. It's also a good idea to check, just in case:
if hasattr(p, "timesHeLiedAboutReleaseDate"): # do something
As mentioned before, in Sandbox, the player objects you get in your functions have a few extra methods that BF2 didn't have:
p.getTracerLocation(distance = -1)
Get the spot where this player's objects are spawned. If distance == -1, the player's tracerDistance will be used.
p.getSoldierCam()
Return the camera object for the player. This is used by Sandbox to determine the player's tracer location.
p.getKeyHash()
Get the CD key hash of the player. Only works in multiplayer games.
p.hasJetpack()
Returns True if the player is using a kit with a jetpack.
p.getPosition()
Alias for p.getDefaultVehicle().getPosition()
p.setPosition(pos)
Alias for p.getVehicle().setPosition(pos)
p.getVehicleRoot()
Alias for getRootParent(p.getVehicle())
p.sendMsg(msg)
Displays a message in the server chat addressed to this player. This replaces the self.sendMsg(p, msg) that was in Sandbox 0.5.
p.clearGrabbed()
Drop any object/group that the player currently has grabbed.
Sandbox also defines lots of attributes for each player for its own use. Some of them may come in handy for add-ons, such as:
p.levelThis defines the Sandbox permission level for a player, from 0 to 21. The permission level controls what commands a player can use.
PlayerManager
The BF2 PlayerManager tends to shuffle and mix players around every now and then for some reason. Sandbox has alternative functions to assist you in getting the correct player objects. You should always use these instead of the bf2.PlayerManager functions, because these will always return correct Sandbox player objects.
self.core.getPlayerByIndex(index) self.core.getPlayerByName(name)
Should be self-explanatory...
self.core.getAllPlayers()
Get a list of all the player objects currently connected to the game.
self.core.getAlivePlayers()
Get a list of all the player objects that are currently connected and alive.
Objects
If you want to get info about an object that was spawned by Sandbox, do not use BF2's functions to find the object. Sandbox keeps several lists and dictionaries handy for your use:
self.core.objects
self.core.objectsOfTemplate
Objects also have a few new attributes:
obj.oIdThe ID number of the object. This is used to pass an rcon command to the server to delete the object.
obj.isDynamicThis is a boolean that tells you whether the object will move around on its own when nobody has grabbed it.
obj.ownerThe player object of the person who spawned this object.
obj.isLockedA boolean that denotes whether the only person who can grab this object is the owner.

