diff --git a/game/gamesrc/objects/baseobjects.py b/game/gamesrc/objects/baseobjects.py index 689588f4cf..5c2e6d5ccf 100644 --- a/game/gamesrc/objects/baseobjects.py +++ b/game/gamesrc/objects/baseobjects.py @@ -42,56 +42,90 @@ class Object(BaseObject): __setattr__ since these are used heavily by the typeclass system of Evennia and messing with them might well break things for you. - Hooks (these are class methods, so their arguments should also start with self): - at_object_creation() - only called once, when object is first created. - Almost all object customizations go here. - at_first_login() - only called once, the very first time user logs in. - at_pre_login() - called every time the user connects, after they have - identified, just before the system actually logs them in. - at_post_login() - called at the end of login, just before setting the - player loose in the world. - at_disconnect() - called just before the use is disconnected (this is also - called if the system determines the player lost their link) - at_object_delete() - called just before the database object is permanently - deleted from the database with obj.delete(). Note that cleaning out contents - and deleting connected exits is not needed, this is handled - automatically when doing obj.delete(). If this method returns - False, deletion is aborted. + * Base properties defined/available on all Objects - at_before_move(destination) - called by obj.move_to() just before moving object to the destination. - If this method returns False, move is cancelled. - announce_move_from(destination) - called while still standing in the old location, - if obj.move_to() has argument quiet=False. - announce_move_to(source_location) - called after move, while standing in the new location - if obj.move_to() has argument quiet=False. - at_after_move(source_location) - always called after a move has been performed. + key (string) - name of object + name (string)- same as key + aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings. + dbref (int, read-only) - unique #id-number. Also "id" can be used. + dbobj (Object, read-only) - link to database model. dbobj.typeclass points back to this class + typeclass (Object, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch. + date_created (string) - time stamp of object creation + permissions (list of strings) - list of permission strings + + player (Player) - controlling player (will also return offline player) + location (Object) - current location. Is None if this is a room + home (Object) - safety start-location + sessions (list of Sessions, read-only) - returns all sessions connected to this object + has_player (bool, read-only)- will only return *connected* players + contents (list of Objects, read-only) - returns all objects inside this object (including exits) + exits (list of Objects, read-only) - returns all exits from this object, if any + destination (Object) - only set if this object is an exit. + is_superuser (bool, read-only) - True/False if this user is a superuser + + * Handlers available + + locks - lock-handler: use locks.add() to add new lock strings + db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr + ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data + scripts - script-handler. Add new scripts to object with scripts.add() + cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object + nicks - nick-handler. New nicks with nicks.add(). - at_object_leave(obj, target_location) - called when this object loose an object (e.g. - someone leaving the room, an object is given away etc) - at_object_receive(obj, source_location) - called when this object receives another object - (e.g. a room being entered, an object moved into inventory) + * Helper methods (see src.objects.objects.py for full headers) - - - - return_appearance(looker) - by default, this is used by the 'look' command to - request this object to describe itself. Looker - is the object requesting to get the information. - at_desc(looker=None) - by default called whenever the appearance is requested. - - at_msg_receive(self, msg, from_obj=None, data=None) - called whenever someone sends a message to this object. - message aborted if hook returns False. - at_msg_send(self, msg, to_obj=None, data=None) - called when this objects sends a message. This can only be - called if from_obj is specified in the call to msg(). - at_object_delete() - calleed when this object is to be deleted. If returns False, deletion is aborted. + search(ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, player=False) + execute_cmd(raw_string) + msg(message, from_obj=None, data=None) + msg_contents(message, exclude=None, from_obj=None, data=None) + move_to(destination, quiet=False, emit_to_obj=None, use_destination=True) + copy(new_key=None) + delete() + is_typeclass(typeclass, exact=False) + swap_typeclass(new_typeclass, clean_attributes=False, no_default=True) + access(accessing_obj, access_type='read', default=False) + check_permstring(permstring) - at_get(getter) - called after object has been picked up. Does not stop pickup. - at_drop(dropper) - called when this object has been dropped. - at_say(speaker, message) - by default, called if an object inside this object speaks + * Hooks (these are class methods, so their arguments should also start with self): + + basetype_setup() - only called once, used for behind-the-scenes setup. Normally not modified. + basetype_posthook_setup() - customization in basetype, after the object has been created; Normally not modified. + + at_object_creation() - only called once, when object is first created. Object customizations go here. + at_object_delete() - called just before deleting an object. If returning False, deletion is aborted. Note that all objects + inside a deleted object are automatically moved to their , they don't need to be removed here. + + at_init() - called whenever typeclass is cached from memory, at least once every server restart/reload + at_cmdset_get() - this is called just before the command handler requests a cmdset from this object + at_first_login() - (player-controlled objects only) called once, the very first time user logs in. + at_pre_login() - (player-controlled objects only) called every time the user connects, after they have identified, before other setup + at_post_login() - (player-controlled objects only) called at the end of login, just before setting the player loose in the world. + at_disconnect() - (player-controlled objects only) called just before the user disconnects (or goes linkless) + at_server_reload() - called before server is reloaded + at_server_shutdown() - called just before server is fully shut down + + at_before_move(destination) - called just before moving object to the destination. If returns False, move is cancelled. + announce_move_from(destination) - called in old location, just before move, if obj.move_to() has quiet=False + announce_move_to(source_location) - called in new location, just after move, if obj.move_to() has quiet=False + at_after_move(source_location) - always called after a move has been successfully performed. + at_object_leave(obj, target_location) - called when an object leaves this object in any fashion + at_object_receive(obj, source_location) - called when this object receives another object + + at_before_traverse(traversing_object) - (exit-objects only) called just before an object traverses this object + at_after_traverse(traversing_object, source_location) - (exit-objects only) called just after a traversal has happened. + at_failed_traverse(traversing_object) - (exit-objects only) called if traversal fails and property err_traverse is not defined. + + at_msg_receive(self, msg, from_obj=None, data=None) - called when a message (via self.msg()) is sent to this obj. + If returns false, aborts send. + at_msg_send(self, msg, to_obj=None, data=None) - called when this objects sends a message to someone via self.msg(). + + return_appearance(looker) - describes this object. Used by "look" command by default + at_desc(looker=None) - called by 'look' whenever the appearance is requested. + at_get(getter) - called after object has been picked up. Does not stop pickup. + at_drop(dropper) - called when this object has been dropped. + at_say(speaker, message) - by default, called if an object inside this object speaks - at_server_reload() - called when server is reloading - at_server_shutdown() - called when server is resetting/shutting down """ pass diff --git a/game/gamesrc/scripts/basescript.py b/game/gamesrc/scripts/basescript.py index fafe440feb..a006ff39be 100644 --- a/game/gamesrc/scripts/basescript.py +++ b/game/gamesrc/scripts/basescript.py @@ -22,27 +22,43 @@ class Script(BaseScript): All scripts should inherit from this class and implement some or all of its hook functions and variables. - Important variables controlling the script object: - self.key - the name of all scripts inheriting from this class - (defaults to ), used in lists and searches. - self.desc - a description of the script, used in lists - self.interval (seconds) - How often the event is triggered and calls self.at_repeat() - (see below) Defaults to 0 - that is, never calls at_repeat(). - self.start_delay (True/False). If True, will wait self.interval seconds - befor calling self.at_repeat() for the first time. Defaults to False. - self.repeats - The number of times at_repeat() should be called before automatically - stopping the script. Default is 0, which means infinitely many repeats. - self.persistent (True/False). If True, the script will survive a server restart - (defaults to False). + * available properties - self.obj (game Object)- this ties this script to a particular object. It is - usually not needed to set this parameter explicitly; it's set in the - create methods. + key (string) - name of object + name (string)- same as key + aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings. + dbref (int, read-only) - unique #id-number. Also "id" can be used. + dbobj (Object, read-only) - link to database model. dbobj.typeclass points back to this class + typeclass (Object, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch. + date_created (string) - time stamp of object creation + permissions (list of strings) - list of permission strings + desc (string) - optional description of script, shown in listings + obj (Object) - optional object that this script is connected to and acts on (set automatically by obj.scripts.add()) + interval (int) - how often script should run, in seconds. <0 turns off ticker + start_delay (bool) - if the script should start repeating right away or wait self.interval seconds + repeats (int) - how many times the script should repeat before stopping. 0 means infinite repeats + persistent (bool) - if script should survive a server shutdown or not + is_active (bool) - if script is currently running - Hook methods (should also include self as the first argument): - at_script_creation() - called only once, when an object of this class - is first created. + * Handlers + + locks - lock-handler: use locks.add() to add new lock strings + db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr + ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data + + * Helper methods + + start() - start script (this usually happens automatically at creation and obj.script.add() etc) + stop() - stop script, and delete it + pause() - put the script on hold, until unpause() is called. If script is persistent, the pause state will survive a shutdown. + unpause() - restart a previously paused script. The script will continue as if it was never paused. + time_until_next_repeat() - if a timed script (interval>0), returns time until next tick + + * Hook methods (should also include self as the first argument): + + at_script_creation() - called only once, when an object of this + class is first created. is_valid() - is called to check if the script is valid to be running at the current time. If is_valid() returns False, the running script is stopped and removed from the game. You can use this @@ -59,5 +75,9 @@ class Script(BaseScript): self.interval==0, this method will never be called. at_stop() - Called as the script object is stopped and is about to be removed from the game, e.g. because is_valid() returned False. + at_server_reload() - Called when server reloads. Can be used to save temporary + variables you want should survive a reload. + at_server_shutdown() - called at a full server shutdown. + """ pass diff --git a/src/objects/models.py b/src/objects/models.py index fb12372081..3188fa2501 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -491,6 +491,10 @@ class ObjectDB(TypedObject): This will also find players that don't currently have a character. + Returns - a unique Object/Player match or None. All error + messages are handled by system-commands and the parser-handlers + specified in settings. + Use * to search for objects controlled by a specific player. Note that the object controlled by the player will be returned, not the player object itself. This also means that @@ -538,13 +542,20 @@ class ObjectDB(TypedObject): def execute_cmd(self, raw_string): """ Do something as this object. This command transparently - lets its typeclass execute the command. - raw_string - raw command input coming from the command line. + lets its typeclass execute the command. Evennia also calls + this method whenever the player sends a command on the command line. - The return from this method is None for all default commands - (it's the return value of cmd.func()) and is not used in any - way by the engine. It might be useful for admins wanting to - implement some sort of 'nested' command structure though, + Argument: + raw_string (string) - raw command input + + Returns Deferred - this is an asynchronous Twisted object that will + not fire until the command has actually finished executing. To overload + this one needs to attach callback functions to it, with addCallback(function). + This function will be called with an eventual return value from the command + execution. + + This return is not used at all by Evennia by default, but might be useful + for coders intending to implement some sort of nested command structure. """ # nick replacement - we require full-word matching. @@ -599,15 +610,27 @@ class ObjectDB(TypedObject): self.msg_contents(message, exclude=exclude, from_obj=from_obj, data=data) def move_to(self, destination, quiet=False, - emit_to_obj=None): + emit_to_obj=None, use_destination=True): """ Moves this object to a new location. + + Moves this object to a new location. Note that if is an + exit object (i.e. it has "destination"!=None), the move_to will + happen to this destination and -not- into the exit object itself, unless + use_destination=False. Note that no lock checks are done by this function, + such things are assumed to have been handled before calling move_to. destination: (Object) Reference to the object to move to. This can also be an exit object, in which case the destination property is used as destination. quiet: (bool) If true, don't emit left/arrived messages. emit_to_obj: (Object) object to receive error messages + use_destination (bool): Default is for objects to use the "destination" property + of destinations as the target to move to. Turning off this + keyword allows objects to move "inside" exit objects. + + Returns True/False depending on if there were problems with the move. This method + may also return various error messages to the emit_to_obj. """ def logerr(string=""): trc = traceback.format_exc() @@ -765,10 +788,12 @@ class ObjectDB(TypedObject): def copy(self, new_key=None): """ - Makes an identical copy of this object and returns - it. The copy will be named _copy by default. If you - want to customize the copy by changing some settings, use - the manager method copy_object directly. + Makes an identical copy of this object. If you want to customize the copy by + changing some settings, use ObjectDB.object.copy_object() directly. + + new_key (string) - new key/name of copied object. If new_key is not specified, the copy will be named + _copy by default. + Returns: Object (copy of this one) """ if not new_key: new_key = "%s_copy" % self.key diff --git a/src/objects/objects.py b/src/objects/objects.py index 33857a3d40..9bea5cc4ee 100644 --- a/src/objects/objects.py +++ b/src/objects/objects.py @@ -29,6 +29,254 @@ class Object(TypeClass): objects in the game. """ + ## available properties + + # key (string) - name of object + # name (string)- same as key + # aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings. + # dbref (int, read-only) - unique #id-number. Also "id" can be used. + # dbobj (Object, read-only) - link to database model. dbobj.typeclass points back to this class + # typeclass (Object, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch. + # date_created (string) - time stamp of object creation + # permissions (list of strings) - list of permission strings + + # player (Player) - controlling player (will also return offline player) + # location (Object) - current location. Is None if this is a room + # home (Object) - safety start-location + # sessions (list of Sessions, read-only) - returns all sessions connected to this object + # has_player (bool, read-only)- will only return *connected* players + # contents (list of Objects, read-only) - returns all objects inside this object (including exits) + # exits (list of Objects, read-only) - returns all exits from this object, if any + # destination (Object) - only set if this object is an exit. + # is_superuser (bool, read-only) - True/False if this user is a superuser + + # Handlers + # locks - lock-handler: use locks.add() to add new lock strings + # db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr + # ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data + # scripts - script-handler. Add new scripts to object with scripts.add() + # cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object + # nicks - nick-handler. New nicks with nicks.add(). + + ## methods inherited from the database object (overload them here) + + def search(self, ostring, + global_search=False, + attribute_name=None, + use_nicks=False, + location=None, + ignore_errors=False, + player=False): + """ + Perform a standard object search in the database, handling + multiple results and lack thereof gracefully. + + ostring: (str) The string to match object names against. + Obs - To find a player, append * to the + start of ostring. + global_search(bool): Search all objects, not just the current + location/inventory + attribute_name (string) Which attribute to match + (if None, uses default 'name') + use_nicks (bool) : Use nickname replace (off by default) + location (Object): If None, use caller's current location + ignore_errors (bool): Don't display any error messages even + if there are none/multiple matches - + just return the result as a list. + player (Objectt): Don't search for an Object but a Player. + This will also find players that don't + currently have a character. + + Returns - a unique Object/Player match or None. All error + messages are handled by system-commands and the parser-handlers + specified in settings. + + Use * to search for objects controlled by a specific + player. Note that the object controlled by the player will be + returned, not the player object itself. This also means that + this will not find Players without a character. Use the keyword + player=True to find player objects. + + Note - for multiple matches, the engine accepts a number + linked to the key in order to separate the matches from + each other without showing the dbref explicitly. Default + syntax for this is 'N-searchword'. So for example, if there + are three objects in the room all named 'ball', you could + address the individual ball as '1-ball', '2-ball', '3-ball' + etc. + """ + return self.dbobj.search(ostring, + global_search=global_search, + attribute_name=attribute_name, + use_nicks=use_nicks, + location=location, + ignore_errors=ignore_errors, + player=player) + + def execute_cmd(self, raw_string): + """ + Do something as this object. This command transparently + lets its typeclass execute the command. Evennia also calls + this method whenever the player sends a command on the command line. + + Argument: + raw_string (string) - raw command input + + Returns Deferred - this is an asynchronous Twisted object that will + not fire until the command has actually finished executing. To overload + this one needs to attach callback functions to it, with addCallback(function). + This function will be called with an eventual return value from the command + execution. + + This return is not used at all by Evennia by default, but might be useful + for coders intending to implement some sort of nested command structure. + """ + return self.dbobj.execute_cmd(raw_string) + + def msg(self, message, from_obj=None, data=None): + """ + Emits something to any sessions attached to the object. + + message (str): The message to send + from_obj (obj): object that is sending. + data (object): an optional data object that may or may not + be used by the protocol. + """ + self.dbobj.msg(message, from_obj=from_obj, data=data) + + def msg_contents(self, message, exclude=None, from_obj=None, data=None): + """ + Emits something to all objects inside an object. + + exclude is a list of objects not to send to. See self.msg() for more info. + """ + self.dbobj.msg_contents(message, exclude=exclude, from_obj=from_obj, data=data) + + def move_to(self, destination, quiet=False, + emit_to_obj=None, use_destination=True): + """ + Moves this object to a new location. Note that if is an + exit object (i.e. it has "destination"!=None), the move_to will + happen to this destination and -not- into the exit object itself, unless + use_destination=False. Note that no lock checks are done by this function, + such things are assumed to have been handled before calling move_to. + + destination: (Object) Reference to the object to move to. This + can also be an exit object, in which case the destination + property is used as destination. + quiet: (bool) If true, don't emit left/arrived messages. + emit_to_obj: (Object) object to receive error messages + use_destination (bool): Default is for objects to use the "destination" property + of destinations as the target to move to. Turning off this + keyword allows objects to move "inside" exit objects. + Returns True/False depending on if there were problems with the move. This method + may also return various error messages to the emit_to_obj. + + """ + return self.dbobj.move_to(destination, quiet=quiet, + emit_to_obj=emit_to_obj, use_destination=use_destination) + + def copy(self, new_key=None): + """ + Makes an identical copy of this object. If you want to customize the copy by + changing some settings, use ObjectDB.object.copy_object() directly. + + new_key (string) - new key/name of copied object. If new_key is not specified, the copy will be named + _copy by default. + Returns: Object (copy of this one) + """ + return self.dbobj.copy(new_key=new_key) + + def delete(self): + """ + Deletes this object. + Before deletion, this method makes sure to move all contained + objects to their respective home locations, as well as clean + up all exits to/from the object. + + Returns: boolean True if deletion succeded, False if there + were errors during deletion or deletion otherwise + failed. + """ + return self.dbobj.delete() + + + # methods inherited from the typeclass system + + def is_typeclass(self, typeclass, exact=False): + """ + Returns true if this object has this type + OR has a typeclass which is an subclass of + the given typeclass. + + typeclass - can be a class object or the + python path to such an object to match against. + + exact - returns true only if the object's + type is exactly this typeclass, ignoring + parents. + + Returns: Boolean + """ + return self.dbobj.is_typeclass(typeclass, exact=exact) + + def swap_typeclass(self, new_typeclass, clean_attributes=False, no_default=True): + """ + This performs an in-situ swap of the typeclass. This means + that in-game, this object will suddenly be something else. + Player will not be affected. To 'move' a player to a different + object entirely (while retaining this object's type), use + self.player.swap_object(). + + Note that this might be an error prone operation if the + old/new typeclass was heavily customized - your code + might expect one and not the other, so be careful to + bug test your code if using this feature! Often its easiest + to create a new object and just swap the player over to + that one instead. + + Arguments: + new_typeclass (path/classobj) - type to switch to + clean_attributes (bool/list) - will delete all attributes + stored on this object (but not any + of the database fields such as name or + location). You can't get attributes back, + but this is often the safest bet to make + sure nothing in the new typeclass clashes + with the old one. If you supply a list, + only those named attributes will be cleared. + no_default - if this is active, the swapper will not allow for + swapping to a default typeclass in case the given + one fails for some reason. Instead the old one + will be preserved. + Returns: + boolean True/False depending on if the swap worked or not. + + + """ + self.dbobj.swap_typeclass(new_typeclass, clean_attributes=clean_attributes, no_default=no_default) + + def access(self, accessing_obj, access_type='read', default=False): + """ + Determines if another object has permission to access this object in whatever way. + + accessing_obj (Object)- object trying to access this one + access_type (string) - type of access sought + default (bool) - what to return if no lock of access_type was found + """ + return self.dbobj.access(accessing_obj, access_type=access_type, default=default) + + def check_permstring(self, permstring): + """ + This explicitly checks the given string against this object's + 'permissions' property without involving any locks. + + permstring (string) - permission string that need to match a permission on the object. + (example: 'Builders') + """ + return self.dbobj.check_permstring(permstring) + + def __eq__(self, other): """ Checks for equality against an id string or another object or user. @@ -46,8 +294,10 @@ class Object(TypeClass): pass return result - # hooks called by the game engine + ## hooks called by the game engine + + def basetype_setup(self): """ This sets up the default properties of an Object, @@ -72,23 +322,6 @@ class Object(TypeClass): self.locks.add("call:true()") # allow to call commands on this object self.locks.add("puppet:id(%s) or perm(Immortals) or pperm(Immortals)" % dbref) # restricts puppeting of this object - def at_object_creation(self): - """ - Called once, when this object is first - created. - """ - pass - - def at_init(self): - """ - This is always called whenever this object is initiated -- - that is, whenever it its typeclass is cached from memory. This - happens on-demand first time the object is used or activated - in some way after being created but also after each server - restart or reload. - """ - pass - def basetype_posthook_setup(self): """ Called once, after basetype_setup and at_object_creation. This should generally not be overloaded unless @@ -98,20 +331,30 @@ class Object(TypeClass): """ pass - def at_server_reload(self): - """ - This hook is called whenever the server is shutting down for restart/reboot. - If you want to, for example, save non-persistent properties across a restart, - this is the place to do it. + def at_object_creation(self): """ + Called once, when this object is first created. + """ pass - def at_server_shutdown(self): + + def at_object_delete(self): """ - This hook is called whenever the server is shutting down fully (i.e. not for - a restart). + Called just before the database object is + permanently delete()d from the database. If + this method returns False, deletion is aborted. """ - pass + return True + + def at_init(self): + """ + This is always called whenever this object is initiated -- + that is, whenever it its typeclass is cached from memory. This + happens on-demand first time the object is used or activated + in some way after being created but also after each server + restart or reload. + """ + pass def at_cmdset_get(self): """ @@ -149,6 +392,22 @@ class Object(TypeClass): """ pass + def at_server_reload(self): + """ + This hook is called whenever the server is shutting down for restart/reboot. + If you want to, for example, save non-persistent properties across a restart, + this is the place to do it. + """ + pass + + def at_server_shutdown(self): + """ + This hook is called whenever the server is shutting down fully (i.e. not for + a restart). + """ + pass + + # hooks called when moving the object def at_before_move(self, destination): @@ -260,6 +519,39 @@ class Object(TypeClass): """ pass + def at_msg_receive(self, msg, from_obj=None, data=None): + """ + This hook is called whenever someone + sends a message to this object. + + Note that from_obj may be None if the sender did + not include itself as an argument to the obj.msg() + call - so you have to check for this. . + + Consider this a pre-processing method before + msg is passed on to the user sesssion. If this + method returns False, the msg will not be + passed on. + + msg = the message received + from_obj = the one sending the message + """ + return True + + def at_msg_send(self, msg, to_obj=None, data=None): + """ + This is a hook that is called when /this/ object + sends a message to another object with obj.msg() + while also specifying that it is the one sending. + + Note that this method is executed on the object + passed along with the msg() function (i.e. using + obj.msg(msg, caller) will then launch caller.at_msg()) + and if no object was passed, it will never be called. + """ + pass + + # hooks called by the default cmdset. def return_appearance(self, pobject): @@ -296,38 +588,6 @@ class Object(TypeClass): string += ", ".join(things) return string - def at_msg_receive(self, msg, from_obj=None, data=None): - """ - This hook is called whenever someone - sends a message to this object. - - Note that from_obj may be None if the sender did - not include itself as an argument to the obj.msg() - call - so you have to check for this. . - - Consider this a pre-processing method before - msg is passed on to the user sesssion. If this - method returns False, the msg will not be - passed on. - - msg = the message received - from_obj = the one sending the message - """ - return True - - def at_msg_send(self, msg, to_obj=None, data=None): - """ - This is a hook that is called when /this/ object - sends a message to another object with obj.msg() - while also specifying that it is the one sending. - - Note that this method is executed on the object - passed along with the msg() function (i.e. using - obj.msg(msg, caller) will then launch caller.at_msg()) - and if no object was passed, it will never be called. - """ - pass - def at_desc(self, looker=None): """ This is called whenever someone looks @@ -335,14 +595,6 @@ class Object(TypeClass): object. """ pass - - def at_object_delete(self): - """ - Called just before the database object is - permanently delete()d from the database. If - this method returns False, deletion is aborted. - """ - return True def at_get(self, getter): """ diff --git a/src/players/models.py b/src/players/models.py index 9ffcb4ad15..2e07eda870 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -120,7 +120,6 @@ class PlayerDB(TypedObject): The TypedObject supplies the following (inherited) properties: key - main name - name - alias for key typeclass_path - the path to the decorating typeclass typeclass - auto-linked typeclass date_created - time stamp of object creation @@ -363,7 +362,7 @@ class PlayerDB(TypedObject): if nick.db_nick in raw_list: raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1) break - cmdhandler.cmdhandler(self.typeclass, raw_string) + return cmdhandler.cmdhandler(self.typeclass, raw_string) def search(self, ostring, global_search=False, attribute_name=None, use_nicks=False, location=None, ignore_errors=False, player=False): diff --git a/src/players/player.py b/src/players/player.py index 502321e1b7..7d0a17937b 100644 --- a/src/players/player.py +++ b/src/players/player.py @@ -18,6 +18,161 @@ class Player(TypeClass): """ Base typeclass for all Players. """ + + ## available properties + + # key (string) - name of player + # name (string)- wrapper for user.username + # aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings. + # dbref (int, read-only) - unique #id-number. Also "id" can be used. + # dbobj (Player, read-only) - link to database model. dbobj.typeclass points back to this class + # typeclass (Player, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch. + # date_created (string) - time stamp of object creation + # permissions (list of strings) - list of permission strings + + # user (User, read-only) - django User authorization object + # obj (Object) - game object controlled by player. 'character' can also be used. + # sessions (list of Sessions) - sessions connected to this player + # is_superuser (bool, read-only) - if the connected user is a superuser + + ## Handlers + + # locks - lock-handler: use locks.add() to add new lock strings + # db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr + # ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data + # scripts - script-handler. Add new scripts to object with scripts.add() + # cmdset - cmdset-handler. Use cmdset.add() to add new cmdsets to object + # nicks - nick-handler. New nicks with nicks.add(). + + + ## methods inherited from database model + + def msg(self, outgoing_string, from_obj=None, data=None): + """ + Evennia -> User + This is the main route for sending data back to the user from the server. + + outgoing_string (string) - text data to send + from_obj (Object/Player) - source object of message to send + data (?) - arbitrary data object containing eventual protocol-specific options + + """ + self.dbobj.msg(outgoing_string, from_obj=from_obj, data=data) + + def swap_character(self, new_character, delete_old_character=False): + """ + Swaps the character controlled by this Player, if possible. + + new_character (Object) - character/object to swap to + delete_old_character (bool) - delete the old character when swapping + + Returns: True/False depending on if swap suceeded or not. + """ + return self.dbobj.swap_character(new_character, delete_old_character=delete_old_character) + + def execute_cmd(self, raw_string): + """ + Do something as this object. This command transparently + lets its typeclass execute the command. Evennia also calls + this method whenever the player sends a command on the command line. + + Argument: + raw_string (string) - raw command input + + Returns Deferred - this is an asynchronous Twisted object that will + not fire until the command has actually finished executing. To overload + this one needs to attach callback functions to it, with addCallback(function). + This function will be called with an eventual return value from the command + execution. + + This return is not used at all by Evennia by default, but might be useful + for coders intending to implement some sort of nested command structure. + """ + self.dbobj.execute_cmd(raw_string) + + def search(self, ostring, global_search=False, attribute_name=None, use_nicks=False, + location=None, ignore_errors=False, player=False): + """ + This method mimicks object.search if self.character is set. Otherwise only + other Players can be searched with this method. + """ + self.dbobj.search(ostring, global_search=global_search, attribute_name=attribute_name, use_nicks=use_nicks, + location=location, ignore_errors=ignore_errors, player=player) + + def is_typeclass(self, typeclass, exact=False): + """ + Returns true if this object has this type + OR has a typeclass which is an subclass of + the given typeclass. + + typeclass - can be a class object or the + python path to such an object to match against. + + exact - returns true only if the object's + type is exactly this typeclass, ignoring + parents. + + Returns: Boolean + """ + return self.dbobj.is_typeclass(typeclass, exact=exact) + + def swap_typeclass(self, new_typeclass, clean_attributes=False, no_default=True): + """ + This performs an in-situ swap of the typeclass. This means + that in-game, this object will suddenly be something else. + Player will not be affected. To 'move' a player to a different + object entirely (while retaining this object's type), use + self.player.swap_object(). + + Note that this might be an error prone operation if the + old/new typeclass was heavily customized - your code + might expect one and not the other, so be careful to + bug test your code if using this feature! Often its easiest + to create a new object and just swap the player over to + that one instead. + + Arguments: + new_typeclass (path/classobj) - type to switch to + clean_attributes (bool/list) - will delete all attributes + stored on this object (but not any + of the database fields such as name or + location). You can't get attributes back, + but this is often the safest bet to make + sure nothing in the new typeclass clashes + with the old one. If you supply a list, + only those named attributes will be cleared. + no_default - if this is active, the swapper will not allow for + swapping to a default typeclass in case the given + one fails for some reason. Instead the old one + will be preserved. + Returns: + boolean True/False depending on if the swap worked or not. + + + """ + self.dbobj.swap_typeclass(new_typeclass, clean_attributes=clean_attributes, no_default=no_default) + + def access(self, accessing_obj, access_type='read', default=False): + """ + Determines if another object has permission to access this object in whatever way. + + accessing_obj (Object)- object trying to access this one + access_type (string) - type of access sought + default (bool) - what to return if no lock of access_type was found + """ + return self.dbobj.access(accessing_obj, access_type=access_type, default=default) + + def check_permstring(self, permstring): + """ + This explicitly checks the given string against this object's + 'permissions' property without involving any locks. + + permstring (string) - permission string that need to match a permission on the object. + (example: 'Builders') + """ + return self.dbobj.check_permstring(permstring) + + ## player hooks def basetype_setup(self): """ @@ -63,14 +218,11 @@ class Player(TypeClass): """ pass - # Note that the hooks below also exist - # in the character object's typeclass. You - # can often ignore these and rely on the - # character ones instead, unless you - # are implementing a multi-character game - # and have some things that should be done - # regardless of which character is currently - # connected to this player. + # Note that the hooks below also exist in the character object's + # typeclass. You can often ignore these and rely on the character + # ones instead, unless you are implementing a multi-character game + # and have some things that should be done regardless of which + # character is currently connected to this player. def at_cmdset_get(self): """ @@ -87,12 +239,14 @@ class Player(TypeClass): time the user logs in. """ pass + def at_pre_login(self): """ Called every time the user logs in, before they are actually logged in. """ pass + def at_post_login(self): """ Called at the end of the login @@ -100,6 +254,7 @@ class Player(TypeClass): them loose. """ pass + def at_disconnect(self, reason=None): """ Called just before user diff --git a/src/scripts/scripts.py b/src/scripts/scripts.py index cdb97add97..b3c8d4db46 100644 --- a/src/scripts/scripts.py +++ b/src/scripts/scripts.py @@ -101,6 +101,8 @@ class ScriptClass(TypeClass): except Exception: logger.log_trace() + + # Public methods def time_until_next_repeat(self): """ @@ -251,6 +253,32 @@ class Script(ScriptClass): the hooks called by the script machinery. """ + ## available properties + + # key (string) - name of object + # name (string)- same as key + # aliases (list of strings) - aliases to the object. Will be saved to database as AliasDB entries but returned as strings. + # dbref (int, read-only) - unique #id-number. Also "id" can be used. + # dbobj (Object, read-only) - link to database model. dbobj.typeclass points back to this class + # typeclass (Object, read-only) - this links back to this class as an identified only. Use self.swap_typeclass() to switch. + # date_created (string) - time stamp of object creation + # permissions (list of strings) - list of permission strings + + # desc (string) - optional description of script, shown in listings + # obj (Object) - optional object that this script is connected to and acts on (set automatically by obj.scripts.add()) + # interval (int) - how often script should run, in seconds. <0 turns off ticker + # start_delay (bool) - if the script should start repeating right away or wait self.interval seconds + # repeats (int) - how many times the script should repeat before stopping. 0 means infinite repeats + # persistent (bool) - if script should survive a server shutdown or not + # is_active (bool) - if script is currently running + + ## Handlers + + # locks - lock-handler: use locks.add() to add new lock strings + # db - attribute-handler: store/retrieve database attributes on this self.db.myattr=val, val=self.db.myattr + # ndb - non-persistent attribute handler: same as db but does not create a database entry when storing data + + def at_script_creation(self): """ Only called once, by the create function. diff --git a/src/typeclasses/typeclass.py b/src/typeclasses/typeclass.py index 67167eb577..67acd8c6f2 100644 --- a/src/typeclasses/typeclass.py +++ b/src/typeclasses/typeclass.py @@ -10,7 +10,7 @@ that are protected, so as to not overwrite property names used by the typesystem or django itself. """ -from src.utils import logger +from src.utils.logger import log_trace, log_errmsg from django.conf import settings # these are called so many times it's worth to avoid lookup calls @@ -92,14 +92,8 @@ class TypeClass(object): property on the class, it will NOT be accessible through getattr. """ - try: - dbobj = GA(self, 'dbobj') - except AttributeError: - dbobj = None - logger.log_trace("This is probably due to an unsafe reload.") - raise if propname == 'dbobj': - return dbobj + return GA(self, 'dbobj') if propname.startswith('__') and propname.endswith('__'): # python specials are parsed as-is (otherwise things like # isinstance() fail to identify the typeclass) @@ -108,20 +102,19 @@ class TypeClass(object): try: return GA(self, propname) except AttributeError: + try: + dbobj = GA(self, 'dbobj') + except AttributeError: + log_trace("Typeclass CRITICAL ERROR! dbobj not found for Typeclass %s!" % self) + raise try: return GA(dbobj, propname) except AttributeError: try: - if propname == 'ndb': - # get non-persistent data (getattr raises AttributeError) - return getattr(GA(dbobj, 'ndb'), propname) - else: - return GA(dbobj,"get_attribute_raise")(propname) + return GA(dbobj,"get_attribute_raise")(propname) except AttributeError: string = "Object: '%s' not found on %s(%s), nor on its typeclass %s." - raise AttributeError(string % (propname, dbobj, - dbobj.dbref, - dbobj.typeclass_path,)) + raise AttributeError(string % (propname, dbobj, dbobj.dbref, dbobj.typeclass_path)) def __setattr__(self, propname, value): """ @@ -135,14 +128,14 @@ class TypeClass(object): if propname in PROTECTED: string = "%s: '%s' is a protected attribute name." string += " (protected: [%s])" % (", ".join(PROTECTED)) - logger.log_errmsg(string % (self.name, propname)) + log_errmsg(string % (self.name, propname)) return try: dbobj = GA(self, 'dbobj') except AttributeError: dbobj = None - logger.log_trace("This is probably due to an unsafe reload.") + log_trace("This is probably due to an unsafe reload.") if dbobj: try: @@ -175,7 +168,7 @@ class TypeClass(object): if propname in PROTECTED: string = "%s: '%s' is a protected attribute name." string += " (protected: [%s])" % (", ".join(PROTECTED)) - logger.log_errmsg(string % (self.name, propname)) + log_errmsg(string % (self.name, propname)) return try: @@ -185,7 +178,7 @@ class TypeClass(object): try: dbobj = GA(self, 'dbobj') except AttributeError: - logger.log_trace("This is probably due to an unsafe reload.") + log_trace("This is probably due to an unsafe reload.") return # ignore delete try: dbobj.del_attribute_raise(propname)