Level Editor/Scripting/Sandbox limits
Map scripts run on other people's servers and machines, so they get a small, fixed set of tools. When a map loads, the game checks every script against that set, and a map with a script that uses anything else does not load at all. The check reports every offending name with its line, for example:
line 12: `OS` is not available to a server script
What a script can use
- Its own
api, and only the calls its side has. A client script cannot use a server call such asapi.set_team, even if it writes the name correctly. See GamemodeServerApi and GamemodeClientApi. - Its own names: the variables, constants and functions it declares, and their parameters.
- The GDScript language:
var,const,func,if,elif,else,for,while,match,return,break,continue,super, and lambdas passed tosort_custom,map,filter,reduce,anyorall. - Values and maths: whole and decimal numbers, strings, vectors, colors, Arrays, Dictionaries and packed arrays, with a set of their methods, plus maths, random numbers,
str,len,range,print,push_warningandpush_error.
The complete list, the same one the game checks against, is on What a script may use.
What a script cannot do
- Load anything.
loadandpreloadare refused. A script names a pickup kind or a widget instead of loading a scene. - Declare a global class.
class_nameis refused. - Extend anything but its own base class. The first line is
extends GamemodeServerScript,extends GamemodeClientScriptorextends GamemodeBotScript; extending a path such asextends "res://..."is refused. - Reach the engine or the computer.
OS,FileAccess,DirAccess,ClassDB,Engine,Time, nodes, the scene tree and networking are not there. Players, zones and pickups are always plain numbers and names, never objects. - Call things by name.
call,callv,Callable,get,setandget_scriptare refused. That includes a Dictionary'sget: writedict[key]after checkingdict.has(key), ordict.get_or_add(key, default)when adding the default is what you want. A lambda can be handed tosort_customand the like, but not called directly. - Change the game's randomness.
seedandrandomizeare refused.randf,randi,randf_rangeandrandi_rangework. - Turn a number into an object.
instance_from_idandweakrefare refused.
A safe pattern for per-player data
var _streak: Dictionary = {}
func _player_died(entity_id: int, killer_id: int, default_seconds: float) -> void:
api.respawn_in(entity_id, default_seconds)
_streak.erase(entity_id)
if killer_id >= 0:
var streak: int = (_streak[killer_id] if _streak.has(killer_id) else 0) + 1
_streak[killer_id] = streak
if streak == 5:
api.notify_all("%s is on a streak" % api.team_name(api.team_of(killer_id)))
func _player_left(entity_id: int) -> void:
_streak.erase(entity_id)
Players are entity ids; there is no call that gives you a player's name, so talk about teams instead.