diff --git a/CHANGELOG.md b/CHANGELOG.md index 83688edb82..3f2dc126cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,11 +16,17 @@ arguments (InspectorCaracal) - [Fix][issue3476]: Don't ignore EvEditor commands with wrong capitalization (Griatch) +- [Fix][issue3477]: The `at_server_reload_start()` hook was not firing on + a reload (regression). +- [Docs] Added new [Server-Lifecycle][doc-server-lifecycle] page to describe + the hooks called on server start/stop/reload (Griatch) [pull3438]: https://github.com/evennia/evennia/pull/3446 [pull3485]: https://github.com/evennia/evennia/pull/3485 [pull3487]: https://github.com/evennia/evennia/pull/3487 [issue3476]: https://github.com/evennia/evennia/issues/3476 +[issue3477]: https://github.com/evennia/evennia/issues/3477 +[doc-server-lifecycle]: https://www.evennia.com/docs/latest/Concepts/Server-Lifecycle.html ## Evennia 4.1.0 diff --git a/docs/source/Coding/Changelog.md b/docs/source/Coding/Changelog.md index 7e0137fef9..3f2dc126cf 100644 --- a/docs/source/Coding/Changelog.md +++ b/docs/source/Coding/Changelog.md @@ -1,5 +1,34 @@ # Changelog +## Main branch + +- [Fix][pull3438]: Error with 'you' mapping in third-person style of + `msg_contents` (InspectorCaracal) +- [Fix][pull3472]: The new `filter_visible` didn't exclude oneself by default + (InspectorCaracal) +- Fix: `find #dbref` results didn't include the results of + `.get_extra_display_name_info` (the #dbref display by default) (Griatch) +- Fix: Add `DefaultAccount.get_extra_display_name_info` method for API + compliance with `DefaultObject` in commands. (Griatch) +- Fix: Show `XYZRoom` subclass when repr() it. (Griatch) +- [Fix][pull3485]: Typo in `sethome` message (chiizujin) +- [Fix][pull3487]: Fix traceback when using `get`,`drop` and `give` with no + arguments (InspectorCaracal) +- [Fix][issue3476]: Don't ignore EvEditor commands with wrong capitalization + (Griatch) +- [Fix][issue3477]: The `at_server_reload_start()` hook was not firing on + a reload (regression). +- [Docs] Added new [Server-Lifecycle][doc-server-lifecycle] page to describe + the hooks called on server start/stop/reload (Griatch) + +[pull3438]: https://github.com/evennia/evennia/pull/3446 +[pull3485]: https://github.com/evennia/evennia/pull/3485 +[pull3487]: https://github.com/evennia/evennia/pull/3487 +[issue3476]: https://github.com/evennia/evennia/issues/3476 +[issue3477]: https://github.com/evennia/evennia/issues/3477 +[doc-server-lifecycle]: https://www.evennia.com/docs/latest/Concepts/Server-Lifecycle.html + + ## Evennia 4.1.0 April 1, 2024 @@ -49,7 +78,8 @@ April 1, 2024 differentiating from their lower-case alternatives (Griatch) - [Fix][issue3460]: The `menu_login` contrib regression caused it to error out when creating a new character (Griatch) -- Doc: Added Beginner Tutorial lessons for AI, Quests and Procedural dungeon (Griatch) +- Doc: Added Beginner Tutorial lessons for [Monster and NPC AI][docAI], + [Quests][docQuests] and [Making a Procedural dungeon][docDungeon] (Griatch) - Doc fixes (Griatch, InspectorCaracal, homeofpoe) [pull3421]: https://github.com/evennia/evennia/pull/3421 @@ -70,6 +100,9 @@ April 1, 2024 [issue3462]: https://github.com/evennia/evennia/issues/3462 [issue3460]: https://github.com/evennia/evennia/issues/3460 [issue3461]: https://github.com/evennia/evennia/issues/3461 +[docAI]: https://www.evennia.com/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-AI.html +[docQuests]: https://www.evennia.com/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Quests.html +[docDungeon]: https://www.evennia.com/docs/latest/Howtos/Beginner-Tutorial/Part3/Beginner-Tutorial-Dungeon.html ## Evennia 4.0.0 diff --git a/docs/source/Concepts/Concepts-Overview.md b/docs/source/Concepts/Concepts-Overview.md index 84d0aa5ca4..60cb066e7f 100644 --- a/docs/source/Concepts/Concepts-Overview.md +++ b/docs/source/Concepts/Concepts-Overview.md @@ -37,6 +37,7 @@ Banning.md ```{toctree} :maxdepth: 2 +Server-Lifecycle Protocols.md Models.md Zones.md diff --git a/docs/source/Concepts/Server-Lifecycle.md b/docs/source/Concepts/Server-Lifecycle.md new file mode 100644 index 0000000000..2cc8a5a017 --- /dev/null +++ b/docs/source/Concepts/Server-Lifecycle.md @@ -0,0 +1,81 @@ + +# Evennia Server Lifecycle + +As part of your game design you may want to change how Evennia behaves when starting or stopping. A common use case would be to start up some piece of custom code you want to always have available once the server is up. + +Evennia has three main life cycles, all of which you can add custom behavior for: + +- **Database life cycle**: Evennia runs on top of one several [supported databases](../Setup/Choosing-a-Database.md). It's possible to wipe this database to 'start over' without needing to separately download Evennia anew. +- **Reboot life cycle**: From When Evennia starts from zero, to it being fully shut down, which means both Portal and Server are stopped. This kicks all players. +- **Reload life cycle:** This is the main runtime, until a "reload" event. Reloads do not kick any players. + +## When Evennia first starts + +This is the beginning of the **Database life cycle**, just after the database is created and migrated for the first time. After that it won't run again unless you wipe the database and initialize a new one (see [Choosing a Database](../Setup/Choosing-a-Database.md)). + +Hooks called, in sequence: + +1. `evennia.server.initial_setup.handle_setup(last_step=None)`: Evennia's core initialization function. This is what creates the #1 Character (tied to the superuser) and Limbo. It calls the next hook below and also understands to restart at the last failed step if there was some issue. You should normally not override this function unless you _really_ know what you are doing, but in that case you'd do so by setting `settings.INITIAL_SETUP_MODULE` to your own module with a `handle_setup` function in it. +2. `mygame/server/conf/at_initial_setup.py` contains a single function, `at_initial_setup()`, which will be called without arguments. It's called last in the setup sequence by the above function. Use this to add your own custom behavior or to tweak the initialization. If you for example wanted to change the auto-generated Limbo room, you should do it from here. If you want to change where this function is found, you can do so by changing `settings.AT_INITIAL_SETUP_HOOK_MODULE`. + + +## When Evennia starts and shutdowns + +This is the **Reboot life cycle**. Evennia consists of two main processes, the [Portal and the Server](../Components/Portal-And-Server.md). On a reboot or shutdown, both Portal and Server shuts down, which means all players are disconnected. + +Each process call a series of hooks located in `mygame/server/conf/at_server_startstop.py`. You can customize the module used with `settings.AT_SERVER_STARTSTOP_MODULE` - this can even be a list of modules, if so, the appropriately-named functions will be called from each module, in sequence. + +All hooks are called without arguments. + +> The use of the term 'server' in the hook-names indicate the whole of Evennia, not just the `Server` component. + +### Server cold start + +Starting the server from zero, after a full stop. This is done with `evennia start` from the terminal. + +1. `at_server_init()` - Always called first in the startup sequence. +2. `at_server_cold_start()` - Only called on cold starts. +3. `at_server_start()` - Always called last in the startup sequece. + +### Server cold shutdown + +Shutting everything down. Done with `shutdown` in-game or `evennia stop` from the terminal. + +1. `at_server_cold_stop()` - Only called on cold stops. +2. `at_server_stop()` - Always called last in the stopping sequence. + +### Server reboots + +This is done with `evennia reboot` and effectively constitutes an automatic cold shutdown followed by a cold start controlled from the `evennia` launcher. There are no special `reboot` hooks for this, instead it looks like you'd expect: + +1. `at_server_cold_stop()` +2. `at_server_stop()` (after this, both `Server` + `Portal` have both shut down) +3. `at_server_init()` (like a cold start) +4. `at_server_cold_start()` +5. `at_server_start()` + +## When Evennia reloads and resets + +This is the **Reload life cycle**. As mentioned above, Evennia consists of two components, the [Portal and Server](../Components/Portal-And-Server.md). During a reload, only the `Server` component is shut down and restarted. Since the Portal stays up, players are not disconnected. + +All hooks are called without arguments. + +### Server reload + +Reloads are initiated with the `reload` command in-game, or with `evennia reload` from the terminal. + +1. `at_server_reload_stop()` - Only called on reload stops. +2. `at_server_stop` - Always called last in the stopping sequence. +3. `at_server_init()` - Always called first in startup sequence. +4. `at_server_reload_start()` - Only called on a reload (re)start. +5. `at_server_start()` - Always called last in the startup sequence. + +### Server reset + +A 'reset' is a hybrid reload state, where the reload is treated as a cold shutdown only for the sake of running hooks (players are not disconnected). It's run with `reset` in-game or with `evennia reset` from the terminal. + +1. `at_server_cold_stop()` +2. `at_server_stop()` (after this, only `Server` has shut down) +3. `at_server_init()` (`Server` coming back up) +4. `at_server_cold_start()` +5. `at_server_start()` diff --git a/docs/source/Setup/Settings.md b/docs/source/Setup/Settings.md index 4d549f7549..766d24281e 100644 --- a/docs/source/Setup/Settings.md +++ b/docs/source/Setup/Settings.md @@ -1,7 +1,6 @@ # Changing Game Settings -Evennia runs out of the box without any changes to its settings. But there are several important -ways to customize the server and expand it with your own plugins. +Evennia runs out of the box without any changes to its settings. But there are several important ways to customize the server and expand it with your own plugins. All game-specific settings are located in the `mygame/server/conf/` directory. @@ -17,13 +16,9 @@ heavily documented and up-to-date, so you should refer to this file directly for Since `mygame/server/conf/settings.py` is a normal Python module, it simply imports `evennia/settings_default.py` into itself at the top. -This means that if any setting you want to change were to depend on some *other* default setting, -you might need to copy & paste both in order to change them and get the effect you want (for most -commonly changed settings, this is not something you need to worry about). +This means that if any setting you want to change were to depend on some *other* default setting, you might need to copy & paste both in order to change them and get the effect you want (for most commonly changed settings, this is not something you need to worry about). -You should never edit `evennia/settings_default.py`. Rather you should copy&paste the select -variables you want to change into your `settings.py` and edit them there. This will overload the -previously imported defaults. +You should never edit `evennia/settings_default.py`. Rather you should copy&paste the select variables you want to change into your `settings.py` and edit them there. This will overload the previously imported defaults. ```{warning} Don't copy everything! It may be tempting to copy *everything* from `settings_default.py` into your own settings file just to have it all in one place. Don't do this. By copying only what you need, you can easier track what you changed. @@ -41,45 +36,24 @@ In code, the settings is accessed through Each setting appears as a property on the imported `settings` object. You can also explore all possible options with `evennia.settings_full` (this also includes advanced Django defaults that are not touched in default Evennia). -> When importing `settings` into your code like this, it will be *read -only*. You *cannot* edit your settings from your code! The only way to change an Evennia setting is -to edit `mygame/server/conf/settings.py` directly. You will also need to restart the server -(possibly also the Portal) before a changed setting becomes available. +> When importing `settings` into your code like this, it will be *read only*. You *cannot* edit your settings from your code! The only way to change an Evennia setting is to edit `mygame/server/conf/settings.py` directly. You will also need to restart the server (possibly also the Portal) before a changed setting becomes available. ## Other files in the `server/conf` directory Apart from the main `settings.py` file, -- `at_initial_setup.py` - this allows you to add a custom startup method to be called (only) the -very first time Evennia starts (at the same time as user #1 and Limbo is created). It can be made to -start your own global scripts or set up other system/world-related things your game needs to have -running from the start. -- `at_server_startstop.py` - this module contains two functions that Evennia will call every time -the Server starts and stops respectively - this includes stopping due to reloading and resetting as -well as shutting down completely. It's a useful place to put custom startup code for handlers and -other things that must run in your game but which has no database persistence. -- `connection_screens.py` - all global string variables in this module are interpreted by Evennia as -a greeting screen to show when an Account first connects. If more than one string variable is -present in the module a random one will be picked. +- `at_initial_setup.py` - this allows you to add a custom startup method to be called (only) the very first time Evennia starts (at the same time as user #1 and Limbo is created). It can be made to start your own global scripts or set up other system/world-related things your game needs to have running from the start. +- `at_server_startstop.py` - this module contains functions that Evennia will call every time the Server starts and stops respectively - this includes stopping due to reloading and resetting as well as shutting down completely. It's a useful place to put custom startup code for handlers and other things that must run in your game but which has no database persistence. +- `connection_screens.py` - all global string variables in this module are interpreted by Evennia as a greeting screen to show when an Account first connects. If more than one string variable is present in the module a random one will be picked. - `inlinefuncs.py` - this is where you can define custom [FuncParser functions](../Components/FuncParser.md). -- `inputfuncs.py` - this is where you define custom [Input functions](../Components/Inputfuncs.md) to handle data -from the client. -- `lockfuncs.py` - this is one of many possible modules to hold your own "safe" *lock functions* to -make available to Evennia's [Locks](../Components/Locks.md). -- `mssp.py` - this holds meta information about your game. It is used by MUD search engines (which -you often have to register with) in order to display what kind of game you are running along with - statistics such as number of online accounts and online status. +- `inputfuncs.py` - this is where you define custom [Input functions](../Components/Inputfuncs.md) to handle data from the client. +- `lockfuncs.py` - this is one of many possible modules to hold your own "safe" *lock functions* to make available to Evennia's [Locks](../Components/Locks.md). +- `mssp.py` - this holds meta information about your game. It is used by MUD search engines (which you often have to register with) in order to display what kind of game you are running along with statistics such as number of online accounts and online status. - `oobfuncs.py` - in here you can define custom [OOB functions](../Concepts/OOB.md). -- `portal_services_plugin.py` - this allows for adding your own custom services/protocols to the -Portal. It must define one particular function that will be called by Evennia at startup. There can -be any number of service plugin modules, all will be imported and used if defined. More info can be -found [here](https://code.google.com/p/evennia/wiki/SessionProtocols#Adding_custom_Protocols). -- `server_services_plugin.py` - this is equivalent to the previous one, but used for adding new -services to the Server instead. More info can be found -[here](https://code.google.com/p/evennia/wiki/SessionProtocols#Adding_custom_Protocols). +- `portal_services_plugin.py` - this allows for adding your own custom services/protocols to the Portal. It must define one particular function that will be called by Evennia at startup. There can be any number of service plugin modules, all will be imported and used if defined. More info can be found [here](https://code.google.com/p/evennia/wiki/SessionProtocols#Adding_custom_Protocols). +- `server_services_plugin.py` - this is equivalent to the previous one, but used for adding new services to the Server instead. More info can be found [here](https://code.google.com/p/evennia/wiki/SessionProtocols#Adding_custom_Protocols). -Some other Evennia systems can be customized by plugin modules but has no explicit template in -`conf/`: +Some other Evennia systems can be customized by plugin modules but has no explicit template in `conf/`: - *cmdparser.py* - a custom module can be used to totally replace Evennia's default command parser. All this does is to split the incoming string into "command name" and "the rest". It also handles things like error messages for no-matches and multiple-matches among other things that makes this more complex than it sounds. The default parser is *very* generic, so you are most often best served by modifying things further down the line (on the command parse level) than here. - *at_search.py* - this allows for replacing the way Evennia handles search results. It allows to change how errors are echoed and how multi-matches are resolved and reported (like how the default understands that "2-ball" should match the second "ball" object if there are two of them in the room). \ No newline at end of file diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index 594424f269..89137ff672 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -16,13 +16,14 @@ import time import typing from random import getrandbits -import evennia from django.conf import settings from django.contrib.auth import authenticate, password_validation from django.core.exceptions import ImproperlyConfigured, ValidationError from django.utils import timezone from django.utils.module_loading import import_string from django.utils.translation import gettext as _ + +import evennia from evennia.accounts.manager import AccountManager from evennia.accounts.models import AccountDB from evennia.commands.cmdsethandler import CmdSetHandler @@ -30,17 +31,24 @@ from evennia.comms.models import ChannelDB from evennia.objects.models import ObjectDB from evennia.scripts.scripthandler import ScriptHandler from evennia.server.models import ServerConfig -from evennia.server.signals import (SIGNAL_ACCOUNT_POST_CREATE, - SIGNAL_ACCOUNT_POST_LOGIN_FAIL, - SIGNAL_OBJECT_POST_PUPPET, - SIGNAL_OBJECT_POST_UNPUPPET) +from evennia.server.signals import ( + SIGNAL_ACCOUNT_POST_CREATE, + SIGNAL_ACCOUNT_POST_LOGIN_FAIL, + SIGNAL_OBJECT_POST_PUPPET, + SIGNAL_OBJECT_POST_UNPUPPET, +) from evennia.server.throttle import Throttle from evennia.typeclasses.attributes import ModelAttributeBackend, NickHandler from evennia.typeclasses.models import TypeclassBase from evennia.utils import class_from_module, create, logger from evennia.utils.optionhandler import OptionHandler -from evennia.utils.utils import (is_iter, lazy_property, make_iter, to_str, - variable_from_module) +from evennia.utils.utils import ( + is_iter, + lazy_property, + make_iter, + to_str, + variable_from_module, +) __all__ = ("DefaultAccount", "DefaultGuest") diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index 871cb5272c..558d96b3b5 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -5,13 +5,13 @@ Building and world design commands import re import typing -import evennia from django.conf import settings from django.core.paginator import Paginator from django.db.models import Max, Min, Q + +import evennia from evennia import InterruptCommand -from evennia.commands.cmdhandler import (generate_cmdset_providers, - get_and_merge_cmdsets) +from evennia.commands.cmdhandler import generate_cmdset_providers, get_and_merge_cmdsets from evennia.locks.lockhandler import LockException from evennia.objects.models import ObjectDB from evennia.prototypes import menus as olc_menus @@ -24,10 +24,18 @@ from evennia.utils.dbserialize import deserialize from evennia.utils.eveditor import EvEditor from evennia.utils.evmore import EvMore from evennia.utils.evtable import EvTable -from evennia.utils.utils import (class_from_module, crop, dbref, display_len, - format_grid, get_all_typeclasses, - inherits_from, interactive, list_to_string, - variable_from_module) +from evennia.utils.utils import ( + class_from_module, + crop, + dbref, + display_len, + format_grid, + get_all_typeclasses, + inherits_from, + interactive, + list_to_string, + variable_from_module, +) COMMAND_DEFAULT_CLASS = class_from_module(settings.COMMAND_DEFAULT_CLASS) @@ -3274,11 +3282,15 @@ class CmdFind(COMMAND_DEFAULT_CLASS): string += f"\n |RNo match found for '{searchstring}' in #dbref interval.|n" else: result = result[0] - string += (f"\n|g {result.get_display_name(caller)}" - f"{result.get_extra_display_name_info(caller)} - {result.path}|n") + string += ( + f"\n|g {result.get_display_name(caller)}" + f"{result.get_extra_display_name_info(caller)} - {result.path}|n" + ) if "loc" in self.switches and not is_account and result.location: - string += (f" (|wlocation|n: |g{result.location.get_display_name(caller)}" - f"{result.get_extra_display_name_info(caller)}|n)") + string += ( + f" (|wlocation|n: |g{result.location.get_display_name(caller)}" + f"{result.get_extra_display_name_info(caller)}|n)" + ) else: # Not an account/dbref search but a wider search; build a queryset. # Searches for key and aliases diff --git a/evennia/commands/default/general.py b/evennia/commands/default/general.py index 5b27318a03..06aae08f75 100644 --- a/evennia/commands/default/general.py +++ b/evennia/commands/default/general.py @@ -4,8 +4,9 @@ General Character commands usually available to all characters import re -import evennia from django.conf import settings + +import evennia from evennia.typeclasses.attributes import NickTemplateInvalid from evennia.utils import utils diff --git a/evennia/contrib/grid/xyzgrid/xymap_legend.py b/evennia/contrib/grid/xyzgrid/xymap_legend.py index 29be2cca66..973f96a1d5 100644 --- a/evennia/contrib/grid/xyzgrid/xymap_legend.py +++ b/evennia/contrib/grid/xyzgrid/xymap_legend.py @@ -20,11 +20,11 @@ import uuid from collections import defaultdict from django.core import exceptions as django_exceptions + from evennia.prototypes import spawner from evennia.utils.utils import class_from_module -from .utils import (BIGVAL, MAPSCAN, REVERSE_DIRECTIONS, MapError, - MapParserError) +from .utils import BIGVAL, MAPSCAN, REVERSE_DIRECTIONS, MapError, MapParserError NodeTypeclass = None ExitTypeclass = None @@ -331,7 +331,8 @@ class MapNode: raise MapError( f"Multiple objects found: {NodeTypeclass.objects.filter_xyz(xyz=xyz)}. " "This may be due to manual creation of XYZRooms at this position. " - "Delete duplicates.", self + "Delete duplicates.", + self, ) else: self.log(f" updating existing room (if changed) at xyz={xyz}") diff --git a/evennia/contrib/grid/xyzgrid/xyzroom.py b/evennia/contrib/grid/xyzgrid/xyzroom.py index 3577d98b67..21de4aa874 100644 --- a/evennia/contrib/grid/xyzgrid/xyzroom.py +++ b/evennia/contrib/grid/xyzgrid/xyzroom.py @@ -9,6 +9,7 @@ used as stand-alone XYZ-coordinate-aware rooms. from django.conf import settings from django.db.models import Q + from evennia.objects.manager import ObjectManager from evennia.objects.objects import DefaultExit, DefaultRoom @@ -307,8 +308,7 @@ class XYZRoom(DefaultRoom): def xyzgrid(self): global GET_XYZGRID if not GET_XYZGRID: - from evennia.contrib.grid.xyzgrid.xyzgrid import \ - get_xyzgrid as GET_XYZGRID + from evennia.contrib.grid.xyzgrid.xyzgrid import get_xyzgrid as GET_XYZGRID return GET_XYZGRID() @property @@ -532,8 +532,7 @@ class XYZExit(DefaultExit): def xyzgrid(self): global GET_XYZGRID if not GET_XYZGRID: - from evennia.contrib.grid.xyzgrid.xyzgrid import \ - get_xyzgrid as GET_XYZGRID + from evennia.contrib.grid.xyzgrid.xyzgrid import get_xyzgrid as GET_XYZGRID return GET_XYZGRID() @property diff --git a/evennia/contrib/tutorials/evadventure/chargen.py b/evennia/contrib/tutorials/evadventure/chargen.py index 6c773a0116..a1b429e3d1 100644 --- a/evennia/contrib/tutorials/evadventure/chargen.py +++ b/evennia/contrib/tutorials/evadventure/chargen.py @@ -4,6 +4,7 @@ EvAdventure character generation. """ from django.conf import settings + from evennia.objects.models import ObjectDB from evennia.prototypes.spawner import spawn from evennia.utils.create import create_object diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 0e613e8482..8d04e3849c 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -10,10 +10,11 @@ import time import typing from collections import defaultdict -import evennia import inflect from django.conf import settings from django.utils.translation import gettext as _ + +import evennia from evennia.commands import cmdset from evennia.commands.cmdsethandler import CmdSetHandler from evennia.objects.manager import ObjectManager @@ -23,9 +24,17 @@ from evennia.server.signals import SIGNAL_EXIT_TRAVERSED from evennia.typeclasses.attributes import ModelAttributeBackend, NickHandler from evennia.typeclasses.models import TypeclassBase from evennia.utils import ansi, create, funcparser, logger, search -from evennia.utils.utils import (class_from_module, compress_whitespace, dbref, - is_iter, iter_to_str, lazy_property, - make_iter, to_str, variable_from_module) +from evennia.utils.utils import ( + class_from_module, + compress_whitespace, + dbref, + is_iter, + iter_to_str, + lazy_property, + make_iter, + to_str, + variable_from_module, +) _INFLECT = inflect.engine() _MULTISESSION_MODE = settings.MULTISESSION_MODE @@ -1425,7 +1434,8 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase): return [ obj for obj in obj_list - if obj != looker and (obj.access(looker, "view") and obj.access(looker, "search", default=True)) + if obj != looker + and (obj.access(looker, "view") and obj.access(looker, "search", default=True)) ] # name and return_appearance hooks diff --git a/evennia/server/evennia_launcher.py b/evennia/server/evennia_launcher.py index 60ed76ab03..7578da9e52 100644 --- a/evennia/server/evennia_launcher.py +++ b/evennia/server/evennia_launcher.py @@ -642,6 +642,8 @@ def send_instruction(operation, arguments, callback=None, errback=None): """ global AMP_CONNECTION, REACTOR_RUN + # print("launcher: Sending to portal: {} + {}".format(ord(operation), arguments)) + if None in (AMP_HOST, AMP_PORT, AMP_INTERFACE): print(ERROR_AMP_UNCONFIGURED) sys.exit() diff --git a/evennia/server/portal/amp_server.py b/evennia/server/portal/amp_server.py index 67fbd1d1e7..007bddd7cb 100644 --- a/evennia/server/portal/amp_server.py +++ b/evennia/server/portal/amp_server.py @@ -197,8 +197,6 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol): if process and not _is_windows(): # avoid zombie-process on Unix/BSD process.wait() - # unset the reset-mode flag on the portal - self.factory.portal.server_restart_mode = None return def wait_for_disconnect(self, callback, *args, **kwargs): @@ -232,11 +230,18 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol): """ if mode == "reload": - self.send_AdminPortal2Server(amp.DUMMYSESSION, operation=amp.SRELOAD) + self.send_AdminPortal2Server( + amp.DUMMYSESSION, operation=amp.SRELOAD, server_restart_mode=mode + ) elif mode == "reset": - self.send_AdminPortal2Server(amp.DUMMYSESSION, operation=amp.SRESET) + self.send_AdminPortal2Server( + amp.DUMMYSESSION, operation=amp.SRESET, server_restart_mode=mode + ) elif mode == "shutdown": - self.send_AdminPortal2Server(amp.DUMMYSESSION, operation=amp.SSHUTD) + self.send_AdminPortal2Server( + amp.DUMMYSESSION, operation=amp.SSHUTD, server_restart_mode=mode + ) + # store the mode for use once server comes back up again self.factory.portal.server_restart_mode = mode # sending amp data @@ -326,7 +331,6 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol): _, server_connected, _, _, _, _ = self.get_status() # logger.log_msg("Evennia Launcher->Portal operation %s:%s received" % (ord(operation), arguments)) - # logger.log_msg("operation == amp.SSTART: {}: {}".format(operation == amp.SSTART, amp.loads(arguments))) if operation == amp.SSTART: # portal start #15 @@ -405,11 +409,11 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol): sessid, kwargs = self.data_in(packed_data) - # logger.log_msg("Evennia Server->Portal admin data %s:%s received" % (sessid, kwargs)) - operation = kwargs.pop("operation") portal_sessionhandler = evennia.PORTAL_SESSION_HANDLER + # logger.log_msg(f"Evennia Server->Portal admin data operation {ord(operation)}") + if operation == amp.SLOGIN: # server_session_login # a session has authenticated; sync it. session = portal_sessionhandler.get(sessid) @@ -427,22 +431,28 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol): portal_sessionhandler.server_disconnect_all(reason=kwargs.get("reason")) elif operation == amp.SRELOAD: # server reload + # set up callback to restart server once it has disconnected self.factory.server_connection.wait_for_disconnect( self.start_server, self.factory.portal.server_twistd_cmd ) + # tell server to reload self.stop_server(mode="reload") elif operation == amp.SRESET: # server reset + # set up callback to restart server once it has disconnected self.factory.server_connection.wait_for_disconnect( self.start_server, self.factory.portal.server_twistd_cmd ) + # tell server to reset self.stop_server(mode="reset") elif operation == amp.SSHUTD: # server-only shutdown self.stop_server(mode="shutdown") elif operation == amp.PSHUTD: # full server+server shutdown + # set up callback to shut down portal once server has disconnected self.factory.server_connection.wait_for_disconnect(self.factory.portal.shutdown) + # tell server to shut down self.stop_server(mode="shutdown") elif operation == amp.PSYNC: # portal sync @@ -451,6 +461,7 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol): self.factory.portal.server_process_id = kwargs.get("spid", None) # this defaults to 'shutdown' or whatever value set in server_stop server_restart_mode = self.factory.portal.server_restart_mode + # print("Server has connected. Sending session data to Server ... mode: {}".format(server_restart_mode)) sessdata = evennia.PORTAL_SESSION_HANDLER.get_all_sync_data() self.send_AdminPortal2Server( @@ -461,6 +472,7 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol): portal_start_time=self.factory.portal.start_time, ) evennia.PORTAL_SESSION_HANDLER.at_server_connection() + self.factory.portal.server_restart_mode = None if self.factory.server_connection: # this is an indication the server has successfully connected, so @@ -480,7 +492,7 @@ class AMPServerProtocol(amp.AMPMultiConnectionProtocol): ) # set a flag in case we are about to shut down soon - self.factory.server_restart_mode = True + self.factory.server_restart_mode = "shutdown" elif operation == amp.SCONN: # server_force_connection (for irc/etc) portal_sessionhandler.server_connect(**kwargs) diff --git a/evennia/utils/eveditor.py b/evennia/utils/eveditor.py index 6175ada54f..9101fdcac6 100644 --- a/evennia/utils/eveditor.py +++ b/evennia/utils/eveditor.py @@ -44,6 +44,7 @@ import re from django.conf import settings from django.utils.translation import gettext as _ + from evennia import CmdSet from evennia.commands import cmdhandler from evennia.utils import dedent, fill, is_iter, justify, logger, to_str, utils