Fix/refactor initial_setup function. Resolve #2438.

This commit is contained in:
Griatch 2021-08-06 00:16:12 +02:00
parent d3cc7cf630
commit b9771e701f
4 changed files with 135 additions and 126 deletions

View file

@ -1,7 +1,7 @@
"""
This module handles initial database propagation, which is only run the first
time the game starts. It will create some default channels, objects, and
other things.
This module handles initial database propagation, which is only run the first time the game starts.
It will create some default objects (notably give #1 its evennia-specific properties, and create the
Limbo room). It will also hooks, and then perform an initial restart.
Everything starts at handle_setup()
"""
@ -41,16 +41,22 @@ WARNING_POSTGRESQL_FIX = """
"""
def get_god_account():
def _get_superuser_account():
"""
Creates the god user and don't take no for an answer.
Get the superuser (created at the command line) and don't take no for an answer.
Returns:
Account: The first superuser (User #1).
Raises:
AccountDB.DoesNotExist: If the superuser couldn't be found.
"""
try:
god_account = AccountDB.objects.get(id=1)
superuser = AccountDB.objects.get(id=1)
except AccountDB.DoesNotExist:
raise AccountDB.DoesNotExist(ERROR_NO_SUPERUSER)
return god_account
return superuser
def create_objects():
@ -63,84 +69,68 @@ def create_objects():
# Set the initial User's account object's username on the #1 object.
# This object is pure django and only holds name, email and password.
god_account = get_god_account()
superuser = _get_superuser_account()
from evennia.objects.models import ObjectDB
# Create an Account 'user profile' object to hold eventual
# mud-specific settings for the AccountDB object.
account_typeclass = settings.BASE_ACCOUNT_TYPECLASS
# run all creation hooks on god_account (we must do so manually
# run all creation hooks on superuser (we must do so manually
# since the manage.py command does not)
god_account.swap_typeclass(account_typeclass, clean_attributes=True)
god_account.basetype_setup()
god_account.at_account_creation()
god_account.locks.add(
superuser.swap_typeclass(account_typeclass, clean_attributes=True)
superuser.basetype_setup()
superuser.at_account_creation()
superuser.locks.add(
"examine:perm(Developer);edit:false();delete:false();boot:false();msg:all()"
)
# this is necessary for quelling to work correctly.
god_account.permissions.add("Developer")
superuser.permissions.add("Developer")
# Limbo is the default "nowhere" starting room
# Create the in-game god-character for account #1 and set
# it to exist in Limbo.
character_typeclass = settings.BASE_CHARACTER_TYPECLASS
god_character = create.create_object(character_typeclass, key=god_account.username, nohome=True)
try:
superuser_character = ObjectDB.objects.get(id=1)
except ObjectDB.DoesNotExist:
superuser_character = create.create_object(
character_typeclass, key=superuser.username, nohome=True)
god_character.id = 1
god_character.save()
god_character.db.desc = _("This is User #1.")
god_character.locks.add(
superuser_character.db_typeclass_path = character_typeclass
superuser_character.db.desc = _("This is User #1.")
superuser_character.locks.add(
"examine:perm(Developer);edit:false();delete:false();boot:false();msg:all();puppet:false()"
)
# we set this low so that quelling is more useful
god_character.permissions.add("Player")
superuser_character.permissions.add("Player")
superuser_character.save()
god_account.attributes.add("_first_login", True)
god_account.attributes.add("_last_puppet", god_character)
superuser.attributes.add("_first_login", True)
superuser.attributes.add("_last_puppet", superuser_character)
try:
god_account.db._playable_characters.append(god_character)
superuser.db._playable_characters.append(superuser_character)
except AttributeError:
god_account.db_playable_characters = [god_character]
superuser.db_playable_characters = [superuser_character]
room_typeclass = settings.BASE_ROOM_TYPECLASS
limbo_obj = create.create_object(room_typeclass, _("Limbo"), nohome=True)
limbo_obj.id = 2
limbo_obj.save()
try:
limbo_obj = ObjectDB.objects.get(id=2)
except ObjectDB.DoesNotExist:
limbo_obj = create.create_object(room_typeclass, _("Limbo"), nohome=True)
limbo_obj.db_typeclass_path = room_typeclass
limbo_obj.db.desc = LIMBO_DESC.strip()
limbo_obj.save()
# Now that Limbo exists, try to set the user up in Limbo (unless
# the creation hooks already fixed this).
if not god_character.location:
god_character.location = limbo_obj
if not god_character.home:
god_character.home = limbo_obj
def create_channels():
"""
Creates some sensible default channels.
"""
logger.log_info("Initial setup: Creating default channels ...")
goduser = get_god_account()
channel_mudinfo = settings.CHANNEL_MUDINFO
if channel_mudinfo:
channel = create.create_channel(**channel_mudinfo)
channel.connect(goduser)
channel_connectinfo = settings.CHANNEL_CONNECTINFO
if channel_connectinfo:
channel = create.create_channel(**channel_connectinfo)
for channeldict in settings.DEFAULT_CHANNELS:
channel = create.create_channel(**channeldict)
channel.connect(goduser)
if not superuser_character.location:
superuser_character.location = limbo_obj
if not superuser_character.home:
superuser_character.home = limbo_obj
def at_initial_setup():
"""
@ -188,49 +178,46 @@ def reset_server():
SESSIONS.portal_reset_server()
def handle_setup(last_step):
def handle_setup(last_step=None):
"""
Main logic for the module. It allows for restarting the
initialization at any point if one of the modules should crash.
Args:
last_step (int): The last stored successful step, for starting
over on errors. If `< 0`, initialization has finished and no
steps need to be redone.
last_step (str, None): The last stored successful step, for starting
over on errors. None if starting from scratch. If this is 'done',
the function will exit immediately.
"""
if last_step < 0:
if last_step in('done', -1):
# this means we don't need to handle setup since
# it already ran sucessfully once.
# it already ran sucessfully once. -1 is the legacy
# value for existing databases.
return
# if None, set it to 0
last_step = last_step or 0
# setting up the list of functions to run
setup_queue = [create_objects, create_channels, at_initial_setup, collectstatic, reset_server]
# setup sequence
setup_sequence = {
'create_objects': create_objects,
'at_initial_setup': at_initial_setup,
'collectstatic': collectstatic,
'done': reset_server,
}
# step through queue, from last completed function
for num, setup_func in enumerate(setup_queue[last_step:]):
# run the setup function. Note that if there is a
# traceback we let it stop the system so the config
# step is not saved.
# determine the sequence so we can skip ahead
steps = list(setup_sequence)
steps = steps[steps.index(last_step) + 1 if last_step is not None else 0:]
# step through queue from last completed function. Once completed,
# the 'done' key should be set.
for stepname in steps:
try:
setup_func()
setup_sequence[stepname]()
except Exception:
if last_step + num == 1:
from evennia.objects.models import ObjectDB
for obj in ObjectDB.objects.all():
obj.delete()
elif last_step + num == 2:
from evennia.comms.models import ChannelDB
ChannelDB.objects.all().delete()
# we re-raise to make sure to stop startup
raise
# save this step
ServerConfig.objects.conf("last_initial_setup_step", last_step + num + 1)
# We got through the entire list. Set last_step to -1 so we don't
# have to run this again.
ServerConfig.objects.conf("last_initial_setup_step", -1)
else:
# save the step
ServerConfig.objects.conf("last_initial_setup_step", stepname)
if stepname == 'done':
# always exit on 'done'
break