Changed the way unloggedin commands work. Rather than the cmdhander having a special state for unlogged-in commands, the session itself simply stores the cmdset from settings.CMDSET_UNLOGGEDIN. Clean and efficient and also gives a lot more freedom for creating custom login mechanisms (notably it opens the door to using menu systems).

This commit is contained in:
Griatch 2011-11-06 17:38:29 +01:00
parent 0ed692c19c
commit 2b2d27ed39
10 changed files with 119 additions and 77 deletions

View file

@ -8,9 +8,7 @@ command line. The process is as follows:
2) The system checks the state of the caller - loggedin or not
3) If no command string was supplied, we search the merged cmdset for system command CMD_NOINPUT
and branches to execute that. --> Finished
4) Depending on the login/not state, it collects cmdsets from different sources:
not logged in - uses the single cmdset defined as settings.CMDSET_UNLOGGEDIN
normal - gathers command sets from many different sources (shown in dropping priority):
4) Cmdsets are gathered from different sources (in order of dropping priority):
channels - all available channel names are auto-created into a cmdset, to allow
for giving the channel name and have the following immediately
sent to the channel. The sending is performed by the CMD_CHANNEL
@ -140,26 +138,20 @@ def get_and_merge_cmdsets(caller):
# Main command-handler function
def cmdhandler(caller, raw_string, unloggedin=False, testing=False):
def cmdhandler(caller, raw_string, testing=False):
"""
This is the main function to handle any string sent to the engine.
caller - calling object
raw_string - the command string given on the command line
unloggedin - if caller is an authenticated user or not
testing - if we should actually execute the command or not.
if True, the command instance will be returned instead.
"""
try: # catch bugs in cmdhandler itself
try: # catch special-type commands
if unloggedin:
# not logged in, so it's just one cmdset we are interested in
cmdset = import_cmdset(settings.CMDSET_UNLOGGEDIN, caller)
else:
# We are logged in, collect all relevant cmdsets and merge
cmdset = get_and_merge_cmdsets(caller)
cmdset = get_and_merge_cmdsets(caller)
#print cmdset
if not cmdset:
# this is bad and shouldn't happen.

View file

@ -221,7 +221,7 @@ class CmdSet(object):
are made, rather later added commands will simply replace
existing ones to make a unique set.
"""
if inherits_from(cmd, "src.commands.cmdset.CmdSet"):
# this is a command set so merge all commands in that set
# to this one. We are not protecting against recursive
@ -235,19 +235,19 @@ class CmdSet(object):
string += "make sure they are not themself cyclically added to the new cmdset somewhere in the chain."
raise RuntimeError(string % (cmd, self.__class__))
cmds = cmd.commands
elif not is_iter(cmd):
cmds = [instantiate(cmd)]
elif is_iter(cmd):
cmds = [instantiate(c) for c in cmd]
else:
cmds = instantiate(cmd)
cmds = [instantiate(cmd)]
for cmd in cmds:
# add all commands
if not hasattr(cmd, 'obj'):
cmd.obj = self.cmdsetobj
try:
ic = self.commands.index(cmd)
self.commands[ic] = cmd # replace
except ValueError:
self.commands.append(cmd)
cmd.obj = self.cmdsetobj
ic = [i for i, oldcmd in enumerate(self.commands) if oldcmd.match(cmd)]
if ic:
self.commands[ic[0]] = cmd # replace
else:
self.commands.append(cmd)
# extra run to make sure to avoid doublets
self.commands = list(set(self.commands))
#print "In cmdset.add(cmd):", self.key, cmd

View file

@ -129,7 +129,7 @@ class Command(object):
previously extracted from the raw string by the system.
cmdname is always lowercase when reaching this point.
"""
return (cmdname == self.key) or (cmdname in self.aliases)
return cmdname and ((cmdname == self.key) or (cmdname in self.aliases))
def access(self, srcobj, access_type="cmd", default=False):
"""

View file

@ -169,20 +169,10 @@ its and @/./+/-/_ only.") # this echoes the restrictions made by django's auth m
session.msg("There was an error creating the default Character/Player. This error was logged. Contact an admin.")
return
new_player = new_character.player
# character safety features
new_character.locks.delete("get")
new_character.locks.add("get:perm(Wizards)")
# allow the character itself and the player to puppet this character.
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" %
(new_character.id, new_player.id))
# set a default description
new_character.db.desc = "This is a Player."
new_character.db.FIRST_LOGIN = True
new_player = new_character.player
new_player.db.FIRST_LOGIN = True
# This needs to be called so the engine knows this player is logging in for the first time.
# (so it knows to call the right hooks during login later)
utils.init_new_player(player)
# join the new player to the public channel
pchanneldef = settings.CHANNEL_PUBLIC
@ -191,10 +181,20 @@ its and @/./+/-/_ only.") # this echoes the restrictions made by django's auth m
if not pchannel.connect_to(new_player):
string = "New player '%s' could not connect to public channel!" % new_player.key
logger.log_errmsg(string)
# allow only the character itself and the player to puppet this character (and Immortals).
new_character.locks.add("puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals)" %
(new_character.id, new_player.id))
# set a default description
new_character.db.desc = "This is a Player."
# tell the caller everything went well.
string = "A new account '%s' was created with the email address %s. Welcome!"
string += "\n\nYou can now log with the command 'connect %s <your password>'."
session.msg(string % (playername, email, email))
except Exception:
# We are in the middle between logged in and -not, so we have to handle tracebacks
# ourselves at this point. If we don't, we won't see any errors at all.

View file

@ -276,7 +276,7 @@ class AMPProtocol(amp.AMP):
if sess.logged_in and sess.uid:
# this can happen in the case of auto-authenticating protocols like SSH
sess.player = PlayerDB.objects.get_player_from_uid(sess.uid)
sess.at_sync() # this runs initialization without acr
sess.at_sync() # this runs initialization without acr
self.factory.server.sessions.portal_connect(sessid, sess)

View file

@ -13,7 +13,7 @@ from django.conf import settings
from src.scripts.models import ScriptDB
from src.comms.models import Channel
from src.utils import logger
from src.commands import cmdhandler
from src.commands import cmdhandler, cmdsethandler
IDLE_COMMAND = settings.IDLE_COMMAND
@ -37,7 +37,6 @@ class ServerSession(Session):
through their session.
"""
def at_sync(self):
"""
This is called whenever a session has been resynced with the portal.
@ -48,12 +47,22 @@ class ServerSession(Session):
the session as it was.
"""
if not self.logged_in:
# assign the unloggedin-command set.
self.cmdset = cmdsethandler.CmdSetHandler(self)
self.cmdset_storage = [settings.CMDSET_UNLOGGEDIN]
self.cmdset.update(init_mode=True)
self.cmdset.update(init_mode=True)
return
character = self.get_character()
if character:
# start (persistent) scripts on this object
ScriptDB.objects.validate(obj=character)
def at_cmdset_get(self):
"dummy hook all objects with cmdsets need to have"
pass
def session_login(self, player):
"""
Startup mechanisms that need to run at login. This is called
@ -193,8 +202,9 @@ class ServerSession(Session):
# there is no character, but we are logged in. Use player instead.
self.get_player().execute_cmd(command_string)
else:
# we are not logged in. Use special unlogged-in call.
cmdhandler.cmdhandler(self, command_string, unloggedin=True)
# we are not logged in. Use the session directly
# (it uses the settings.UNLOGGEDIN cmdset)
cmdhandler.cmdhandler(self, command_string)
self.update_session_counters()
def data_out(self, msg, data=None):

View file

@ -389,6 +389,8 @@ def create_player(name, email, password,
from src.players.models import PlayerDB
from src.players.player import Player
if not email:
email = "dummy@dummy.com"
if user:
new_user = user
else:

View file

@ -624,3 +624,15 @@ def string_from_module(modpath, variable=None):
if not mvars:
return None
return mvars[random.randint(0, len(mvars)-1)]
def init_new_player(player):
"""
Helper method to call all hooks, set flags etc on a newly created
player (and potentially their character, if it exists already)
"""
# the FIRST_LOGIN flags are necessary for the system to call
# the relevant first-login hooks.
if player.character:
player.character.db.FIRST_LOGIN = True
player.db.FIRST_LOGIN = True