Make tutorial-world buildable by Developer.

Make use of .ndb.batch_batchmode to avoid affecting the builder (document this). Resolve #3688.
Clean up OnDemandTasks' categories before saving, in case it was an object that was deleted
This commit is contained in:
Griatch 2025-01-18 14:37:18 +01:00
parent 825eb32995
commit a921660fd0
5 changed files with 52 additions and 14 deletions

View file

@ -19,7 +19,11 @@
- [Fix][pull3707]: Correct links in `about` command (0xDEADFED5)
- [Fix][pull3710]: Clean reduntant session clearin in `at_server_cold_start` (InspectorCaracal)
- [Fix][pull3711]: Usability improvements in the Discord integration (InspectorCaracal)
- [Fix][issue3688]: Made TutorialWorld possible to build cleanly without being a superuser (Griatch)
- Fix: Make `\\` properly preserve one backlash in funcparser (Griatch)
- Fix: When an object was used as an On-Demand Task's category, and that object was then deleted,
it caused an OnDemandHandler save error on reload. Will now clean up on save. (Griatch)
used as the task's category (Griatch)
- [Docs]: Fixes from InspectorCaracal, Griatch
@ -33,6 +37,7 @@
[pull3707]: https://github.com/evennia/evennia/pull/3707
[pull3710]: https://github.com/evennia/evennia/pull/3710
[pull3711]: https://github.com/evennia/evennia/pull/3711
[issue3688]: https://github.com/evennia/evennia/issues/3688
## Evennia 4.5.0

View file

@ -111,18 +111,25 @@ Use `nn` and `bb` (next and back) to step through the file; e.g. `nn 12` will ju
## Limitations and Caveats
The batch-command processor is great for automating smaller builds or for testing new commands and objects repeatedly without having to write so much. There are several caveats you have to be aware of when using the batch-command processor for building larger, complex worlds though.
The main issue with batch-command builds is that when you run a batch-command script you (*you*, as in your character) are actually moving around in the game creating and building rooms in sequence, just as if you had been entering those commands manually, one by one.
The main issue is that when you run a batch-command script you (*you*, as in your superuser
character) are actually moving around in the game creating and building rooms in sequence, just as if you had been entering those commands manually, one by one. You have to take this into account when creating the file, so that you can 'walk' (or teleport) to the right places in order.
You have to take this into account when creating the file, so that you can 'walk' (or teleport) to the right places in order. It also means that you may be affected by the things you create, such as mobs attacking you or traps immediately hurting you.
This also means there are several pitfalls when designing and adding certain types of objects. Here are some examples:
If you know that your rooms and objects are going to be deployed via a batch-command script, you can plan for this beforehand. To help with this, you can use the fact that the non-persistent Attribute `batch_batchmode` is _only_ set while the batch-processor is running. Here's an example of how to use it:
- *Rooms that change your [Command Set](./Command-Sets.md)*: Imagine that you build a 'dark' room, which severely limits the cmdsets of those entering it (maybe you have to find the light switch to proceed). In your batch script you would create this room, then teleport to it - and promptly be shifted into the dark state where none of your normal build commands work ...
- *Auto-teleportation*: Rooms that automatically teleport those that enter them to another place (like a trap room, for example). You would be teleported away too.
- *Mobiles*: If you add aggressive mobs, they might attack you, drawing you into combat. If they have AI they might even follow you around when building - or they might move away from you before you've had time to finish describing and equipping them!
```python
class HorribleTrapRoom(Room):
# ...
def at_object_receive(self, received_obj, source_location, **kwargs):
"""Apply the horrible traps the moment the room is entered!"""
if received_obj.ndb.batch_batchmode:
# skip if we are currently building the room
return
# commence horrible trap code
```
So we skip the hook if we are currently building the room. This can work for anything, including making sure mobs don't start attacking you while you are creating them.
The solution to all these is to plan ahead. Make sure that superusers are never affected by whatever effects are in play. Add an on/off switch to objects and make sure it's always set to *off* upon creation. It's all doable, one just needs to keep it in mind.
There are other strategies, such as adding an on/off switch to actiive objects and make sure it's always set to *off* upon creation.
## Editor highlighting for .ev files

View file

@ -705,7 +705,7 @@ class CmdCreate(ObjManipCommand):
)
if errors:
self.msg(errors)
if not obj:
continue

View file

@ -280,7 +280,11 @@ class TutorialRoom(DefaultRoom):
source_location (Object): the previous location of new_arrival.
"""
if new_arrival.has_account and not new_arrival.is_superuser:
if new_arrival.ndb.batch_batchmode:
# currently running batchcommand
return
if new_arrival.has_account and not new_arrival.ndb.batch_batchmode:
# this is a character
for obj in self.contents_get(exclude=new_arrival):
if hasattr(obj, "at_new_arrival"):
@ -465,6 +469,10 @@ class IntroRoom(TutorialRoom):
Assign properties on characters
"""
if character.ndb.batch_batchmode:
# currently running batchcommand
return
# setup character for the tutorial
health = self.db.char_health or 20
@ -476,8 +484,8 @@ class IntroRoom(TutorialRoom):
string = "-" * 78 + SUPERUSER_WARNING + "-" * 78
character.msg("|r%s|n" % string.format(name=character.key, quell="|wquell|r"))
else:
# quell user
if character.account:
# quell user if they have account and is not currently running the batch processor
if character.account and not character.ndb.batch_batchmode:
character.account.execute_cmd("quell")
character.msg("(Auto-quelling while in tutorial-world)")
@ -784,6 +792,10 @@ class BridgeRoom(WeatherRoom):
This hook is called by the engine whenever the player is moved
into this room.
"""
if character.ndb.batch_batchmode:
# currently running batchcommand
return
if character.has_account:
# we only run this if the entered object is indeed a player object.
# check so our east/west exits are correctly defined.
@ -1007,6 +1019,7 @@ class DarkRoom(TutorialRoom):
"""
return (
obj.is_superuser
or obj.ndb.batch_batchmode
or obj.db.is_giving_light
or any(o for o in obj.contents if o.db.is_giving_light)
)
@ -1051,6 +1064,10 @@ class DarkRoom(TutorialRoom):
"""
Called when an object enters the room.
"""
if obj.ndb.batch_batchmode:
# currently running batchcommand
self.check_light_state() # this should remove the DarkCmdSet
if obj.has_account:
# a puppeted object, that is, a Character
self._heal(obj)
@ -1117,9 +1134,10 @@ class TeleportRoom(TutorialRoom):
This hook is called by the engine whenever the player is moved into
this room.
"""
if not character.has_account:
# only act on player characters.
if not character.has_account or character.ndb.batch_batchmode:
# only act on player characters or when not building.
return
# determine if the puzzle is a success or not
is_success = str(character.db.puzzle_clue) == str(self.db.puzzle_value)
teleport_to = self.db.success_teleport_to if is_success else self.db.failure_teleport_to
@ -1180,6 +1198,10 @@ class OutroRoom(TutorialRoom):
"""
Do cleanup.
"""
if character.ndb.batch_batchmode:
# currently running batchcommand
return
if character.has_account:
del character.db.health_max
del character.db.health

View file

@ -398,6 +398,10 @@ class OnDemandHandler:
Save the on-demand timers to ServerConfig storage. Should be called when Evennia shuts down.
"""
for key, category in list(self.tasks.keys()):
# in case an object was used for categories, and were since deleted, drop the task
if hasattr(category, "id") and category.id is None:
self.tasks.pop((key, category))
ServerConfig.objects.conf(ONDEMAND_HANDLER_SAVE_NAME, self.tasks)
def _build_key(self, key, category):