merge.
This commit is contained in:
commit
ce0e3c4857
32 changed files with 1208 additions and 166 deletions
|
|
@ -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
|
||||
|
|
@ -52,11 +50,17 @@ COMMAND_PARSER = utils.mod_import(*settings.COMMAND_PARSER.rsplit('.', 1))
|
|||
# allow for custom behaviour when the command handler hits
|
||||
# special situations -- it then calls a normal Command
|
||||
# that you can customize!
|
||||
# Import these variables and use them rather than trying
|
||||
# to remember the actual string constants.
|
||||
|
||||
CMD_NOINPUT = "__noinput_command"
|
||||
CMD_NOMATCH = "__nomatch_command"
|
||||
CMD_MULTIMATCH = "__multimatch_command"
|
||||
CMD_CHANNEL = "__send_to_channel"
|
||||
CMD_CHANNEL = "__send_to_channel_command"
|
||||
# this is the name of the command the engine calls when the player
|
||||
# connects. It is expected to show the login screen.
|
||||
CMD_LOGINSTART = "__unloggedin_look_command"
|
||||
|
||||
|
||||
class NoCmdSets(Exception):
|
||||
"No cmdsets found. Critical error."
|
||||
|
|
@ -115,13 +119,14 @@ def get_and_merge_cmdsets(caller):
|
|||
try:
|
||||
player_cmdset = caller.player.cmdset.current
|
||||
except AttributeError:
|
||||
player_cmdset = None
|
||||
player_cmdset = None
|
||||
|
||||
cmdsets = [caller_cmdset] + [player_cmdset] + [channel_cmdset] + local_objects_cmdsets
|
||||
# weed out all non-found sets
|
||||
cmdsets = [cmdset for cmdset in cmdsets if cmdset]
|
||||
# sort cmdsets after reverse priority (highest prio are merged in last)
|
||||
cmdsets = sorted(cmdsets, key=lambda x: x.priority)
|
||||
|
||||
if cmdsets:
|
||||
# Merge all command sets into one, beginning with the lowest-prio one
|
||||
cmdset = cmdsets.pop(0)
|
||||
|
|
@ -131,7 +136,7 @@ def get_and_merge_cmdsets(caller):
|
|||
cmdset = merging_cmdset + cmdset
|
||||
else:
|
||||
cmdset = None
|
||||
|
||||
|
||||
for cset in (cset for cset in local_objects_cmdsets if cset):
|
||||
cset.duplicates = cset.old_duplicates
|
||||
|
||||
|
|
@ -140,27 +145,21 @@ 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
|
||||
# print cmdset
|
||||
if not cmdset:
|
||||
# this is bad and shouldn't happen.
|
||||
raise NoCmdSets
|
||||
|
|
@ -171,12 +170,10 @@ def cmdhandler(caller, raw_string, unloggedin=False, testing=False):
|
|||
syscmd = cmdset.get(CMD_NOINPUT)
|
||||
sysarg = ""
|
||||
raise ExecSystemCommand(syscmd, sysarg)
|
||||
|
||||
# Parse the input string and match to available cmdset.
|
||||
# This also checks for permissions, so all commands in match
|
||||
# are commands the caller is allowed to call.
|
||||
matches = COMMAND_PARSER(raw_string, cmdset, caller)
|
||||
|
||||
# Deal with matches
|
||||
if not matches:
|
||||
# No commands match our entered command
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
|
|||
matches.extend([create_match(cmdname, raw_string, cmd)
|
||||
for cmdname in [cmd.key] + cmd.aliases
|
||||
if cmdname and l_raw_string.startswith(cmdname.lower())])
|
||||
|
||||
if not matches:
|
||||
# no matches found.
|
||||
if '-' in raw_string:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
cmd.obj = self.cmdsetobj
|
||||
try:
|
||||
ic = self.commands.index(cmd)
|
||||
self.commands[ic] = cmd # replace
|
||||
except ValueError:
|
||||
self.commands.append(cmd)
|
||||
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
|
||||
|
|
|
|||
|
|
@ -121,7 +121,8 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
|
|||
logger.log_trace()
|
||||
if emit_to_obj and not ServerConfig.objects.conf("server_starting_mode"):
|
||||
object.__getattribute__(emit_to_obj, "msg")(errstring)
|
||||
#raise # have to raise, or we will not see any errors in some situations!
|
||||
logger.log_errmsg("Error: %s" % errstring)
|
||||
raise # have to raise, or we will not see any errors in some situations!
|
||||
|
||||
# classes
|
||||
|
||||
|
|
@ -246,8 +247,8 @@ class CmdSetHandler(object):
|
|||
def add(self, cmdset, emit_to_obj=None, permanent=False):
|
||||
"""
|
||||
Add a cmdset to the handler, on top of the old ones.
|
||||
Default is to not make this permanent (i.e. no script
|
||||
will be added to add the cmdset every server start/login).
|
||||
Default is to not make this permanent, i.e. the set
|
||||
will not survive a server reset.
|
||||
|
||||
cmdset - can be a cmdset object or the python path to
|
||||
such an object.
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -616,7 +616,7 @@ class CmdOOCLook(CmdLook):
|
|||
self.character = None
|
||||
if utils.inherits_from(self.caller, "src.objects.objects.Object"):
|
||||
# An object of some type is calling. Convert to player.
|
||||
print self.caller, self.caller.__class__
|
||||
#print self.caller, self.caller.__class__
|
||||
self.character = self.caller
|
||||
if hasattr(self.caller, "player"):
|
||||
self.caller = self.caller.player
|
||||
|
|
@ -685,6 +685,15 @@ class CmdIC(MuxCommand):
|
|||
if caller.swap_character(new_character):
|
||||
new_character.msg("\n{gYou become {c%s{n.\n" % new_character.name)
|
||||
caller.db.last_puppet = old_char
|
||||
if not new_character.location:
|
||||
# this might be due to being hidden away at logout; check
|
||||
loc = new_character.db.prelogout_location
|
||||
if not loc: # still no location; use home
|
||||
loc = new_character.home
|
||||
new_character.location = loc
|
||||
if new_character.location:
|
||||
new_character.location.msg_contents("%s has entered the game." % new_character.key, exclude=[new_character])
|
||||
new_character.location.at_object_receive(new_character, new_character.location)
|
||||
new_character.execute_cmd("look")
|
||||
else:
|
||||
caller.msg("{rYou cannot become {C%s{n." % new_character.name)
|
||||
|
|
@ -720,11 +729,15 @@ class CmdOOC(MuxCommand):
|
|||
return
|
||||
|
||||
caller.db.last_puppet = caller.character
|
||||
# save location as if we were disconnecting from the game entirely.
|
||||
if caller.character.location:
|
||||
caller.character.location.msg_contents("%s has left the game." % caller.character.key, exclude=[caller.character])
|
||||
caller.character.db.prelogout_location = caller.character.location
|
||||
caller.character.location = None
|
||||
|
||||
# disconnect
|
||||
caller.character.player = None
|
||||
caller.character = None
|
||||
|
||||
|
||||
caller.msg("\n{GYou go OOC.{n\n")
|
||||
caller.execute_cmd("look")
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from src.comms.models import Channel
|
|||
|
||||
from src.utils import create, logger, utils, ansi
|
||||
from src.commands.default.muxcommand import MuxCommand
|
||||
from src.commands.cmdhandler import CMD_LOGINSTART
|
||||
|
||||
CONNECTION_SCREEN_MODULE = settings.CONNECTION_SCREEN_MODULE
|
||||
|
||||
|
|
@ -169,20 +170,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(new_player)
|
||||
|
||||
# join the new player to the public channel
|
||||
pchanneldef = settings.CHANNEL_PUBLIC
|
||||
|
|
@ -191,10 +182,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.
|
||||
|
|
@ -221,10 +222,12 @@ class CmdQuit(MuxCommand):
|
|||
class CmdUnconnectedLook(MuxCommand):
|
||||
"""
|
||||
This is an unconnected version of the look command for simplicity.
|
||||
All it does is re-show the connect screen.
|
||||
|
||||
This is called by the server and kicks everything in gear.
|
||||
All it does is display the connect screen.
|
||||
"""
|
||||
key = "look"
|
||||
aliases = "l"
|
||||
key = CMD_LOGINSTART
|
||||
aliases = ["look", "l"]
|
||||
locks = "cmd:all()"
|
||||
|
||||
def func(self):
|
||||
|
|
|
|||
|
|
@ -68,6 +68,13 @@ class ObjectManager(TypedObjectManager):
|
|||
# use the id to find the player
|
||||
return self.get_object_with_user(dbref)
|
||||
|
||||
@returns_typeclass_list
|
||||
def get_objs_with_key_and_typeclass(self, oname, otypeclass_path):
|
||||
"""
|
||||
Returns objects based on simultaneous key and typeclass match.
|
||||
"""
|
||||
return self.filter(db_key__iexact=oname).filter(db_typeclass_path__exact=otypeclass_path)
|
||||
|
||||
# attr/property related
|
||||
|
||||
@returns_typeclass_list
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ class ObjectDB(TypedObject):
|
|||
string += "%s is not a valid home."
|
||||
self.msg(string % home)
|
||||
logger.log_trace(string)
|
||||
raise
|
||||
#raise
|
||||
self.save()
|
||||
#@home.deleter
|
||||
def home_del(self):
|
||||
|
|
|
|||
|
|
@ -409,6 +409,34 @@ class Character(Object):
|
|||
def at_after_move(self, source_location):
|
||||
"Default is to look around after a move."
|
||||
self.execute_cmd('look')
|
||||
|
||||
def at_disconnect(self):
|
||||
"""
|
||||
We stove away the character when logging off, otherwise the character object will
|
||||
remain in the room also after the player logged off ("headless", so to say).
|
||||
"""
|
||||
if self.location: # have to check, in case of multiple connections closing
|
||||
self.location.msg_contents("%s has left the game." % self.name, exclude=[self])
|
||||
self.db.prelogout_location = self.location
|
||||
self.location = None
|
||||
|
||||
def at_post_login(self):
|
||||
"""
|
||||
This recovers the character again after having been "stoved away" at disconnect.
|
||||
"""
|
||||
if self.db.prelogout_location:
|
||||
# try to recover
|
||||
self.location = self.db.prelogout_location
|
||||
if self.location == None:
|
||||
# make sure location is never None (home should always exist)
|
||||
self.location = self.home
|
||||
# save location again to be sure
|
||||
self.db.prelogout_location = self.location
|
||||
|
||||
self.location.msg_contents("%s has entered the game." % self.name, exclude=[self])
|
||||
self.location.at_object_receive(self, self.location)
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Base Room object
|
||||
|
|
|
|||
|
|
@ -34,10 +34,6 @@ SERVER_RESTART = os.path.join(settings.GAME_DIR, "server.restart")
|
|||
# i18n
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
# Signals
|
||||
|
||||
|
||||
|
||||
|
||||
def get_restart_mode(restart_file):
|
||||
"""
|
||||
|
|
@ -141,6 +137,24 @@ class MsgServer2Portal(amp.Command):
|
|||
errors = [(Exception, 'EXCEPTION')]
|
||||
response = []
|
||||
|
||||
class OOBPortal2Server(amp.Command):
|
||||
"""
|
||||
OOB data portal -> server
|
||||
"""
|
||||
arguments = [('sessid', amp.Integer()),
|
||||
('data', amp.String())]
|
||||
errors = [(Exception, "EXCEPTION")]
|
||||
response = []
|
||||
|
||||
class OOBServer2Portal(amp.Command):
|
||||
"""
|
||||
OOB data server -> portal
|
||||
"""
|
||||
arguments = [('sessid', amp.Integer()),
|
||||
('data', amp.String())]
|
||||
errors = [(Exception, "EXCEPTION")]
|
||||
response = []
|
||||
|
||||
class ServerAdmin(amp.Command):
|
||||
"""
|
||||
Portal -> Server
|
||||
|
|
@ -168,6 +182,8 @@ class PortalAdmin(amp.Command):
|
|||
errors = [(Exception, 'EXCEPTION')]
|
||||
response = []
|
||||
|
||||
dumps = lambda data: utils.to_str(pickle.dumps(data, pickle.HIGHEST_PROTOCOL))
|
||||
loads = lambda data: pickle.loads(utils.to_str(data))
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Core AMP protocol for communication Server <-> Portal
|
||||
|
|
@ -220,7 +236,7 @@ class AMPProtocol(amp.AMP):
|
|||
Relays message to server. This method is executed on the Server.
|
||||
"""
|
||||
#print "msg portal -> server (server side):", sessid, msg
|
||||
self.factory.server.sessions.data_in(sessid, msg, pickle.loads(utils.to_str(data)))
|
||||
self.factory.server.sessions.data_in(sessid, msg, loads(data))
|
||||
return {}
|
||||
MsgPortal2Server.responder(amp_msg_portal2server)
|
||||
|
||||
|
|
@ -232,7 +248,7 @@ class AMPProtocol(amp.AMP):
|
|||
self.callRemote(MsgPortal2Server,
|
||||
sessid=sessid,
|
||||
msg=msg,
|
||||
data=utils.to_str(pickle.dumps(data))).addErrback(self.errback, "MsgPortal2Server")
|
||||
data=dumps(data)).addErrback(self.errback, "MsgPortal2Server")
|
||||
|
||||
# Server -> Portal message
|
||||
|
||||
|
|
@ -241,7 +257,7 @@ class AMPProtocol(amp.AMP):
|
|||
Relays message to Portal. This method is executed on the Portal.
|
||||
"""
|
||||
#print "msg server->portal (portal side):", sessid, msg
|
||||
self.factory.portal.sessions.data_out(sessid, msg, pickle.loads(utils.to_str(data)))
|
||||
self.factory.portal.sessions.data_out(sessid, msg, loads(data))
|
||||
return {}
|
||||
MsgServer2Portal.responder(amp_msg_server2portal)
|
||||
|
||||
|
|
@ -253,8 +269,50 @@ class AMPProtocol(amp.AMP):
|
|||
self.callRemote(MsgServer2Portal,
|
||||
sessid=sessid,
|
||||
msg=utils.to_str(msg),
|
||||
data=utils.to_str(pickle.dumps(data))).addErrback(self.errback, "MsgServer2Portal")
|
||||
data=dumps(data)).addErrback(self.errback, "OOBServer2Portal")
|
||||
|
||||
# OOB Portal -> Server
|
||||
|
||||
# Portal -> Server Msg
|
||||
|
||||
def amp_oob_portal2server(self, sessid, data):
|
||||
"""
|
||||
Relays out-of-band data to server. This method is executed on the Server.
|
||||
"""
|
||||
#print "oob portal -> server (server side):", sessid, loads(data)
|
||||
self.factory.server.sessions.oob_data_in(sessid, loads(data))
|
||||
return {}
|
||||
OOBPortal2Server.responder(amp_oob_portal2server)
|
||||
|
||||
def call_remote_OOBPortal2Server(self, sessid, data=""):
|
||||
"""
|
||||
Access method called by the Portal and executed on the Portal.
|
||||
"""
|
||||
#print "oob portal->server (portal side):", sessid, data
|
||||
self.callRemote(OOBPortal2Server,
|
||||
sessid=sessid,
|
||||
data=dumps(data)).addErrback(self.errback, "OOBPortal2Server")
|
||||
|
||||
# Server -> Portal message
|
||||
|
||||
def amp_oob_server2portal(self, sessid, data):
|
||||
"""
|
||||
Relays out-of-band data to Portal. This method is executed on the Portal.
|
||||
"""
|
||||
#print "oob server->portal (portal side):", sessid, data
|
||||
self.factory.portal.sessions.oob_data_out(sessid, loads(data))
|
||||
return {}
|
||||
OOBServer2Portal.responder(amp_oob_server2portal)
|
||||
|
||||
def call_remote_OOBServer2Portal(self, sessid, data=""):
|
||||
"""
|
||||
Access method called by the Server and executed on the Server.
|
||||
"""
|
||||
#print "oob server->portal (server side):", sessid, data
|
||||
self.callRemote(OOBServer2Portal,
|
||||
sessid=sessid,
|
||||
data=dumps(data)).addErrback(self.errback, "OOBServer2Portal")
|
||||
|
||||
|
||||
# Server administration from the Portal side
|
||||
|
||||
|
|
@ -264,7 +322,7 @@ class AMPProtocol(amp.AMP):
|
|||
operations on the server. This is executed on the Server.
|
||||
|
||||
"""
|
||||
data = pickle.loads(utils.to_str(data))
|
||||
data = loads(data)
|
||||
|
||||
#print "serveradmin (server side):", sessid, operation, data
|
||||
|
||||
|
|
@ -276,7 +334,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)
|
||||
|
||||
|
|
@ -315,7 +373,7 @@ class AMPProtocol(amp.AMP):
|
|||
Access method called by the Portal and Executed on the Portal.
|
||||
"""
|
||||
#print "serveradmin (portal side):", sessid, operation, data
|
||||
data = utils.to_str(pickle.dumps(data))
|
||||
data = dumps(data)
|
||||
|
||||
self.callRemote(ServerAdmin,
|
||||
sessid=sessid,
|
||||
|
|
@ -329,7 +387,7 @@ class AMPProtocol(amp.AMP):
|
|||
This allows the server to perform admin
|
||||
operations on the portal. This is executed on the Portal.
|
||||
"""
|
||||
data = pickle.loads(utils.to_str(data))
|
||||
data = loads(data)
|
||||
|
||||
#print "portaladmin (portal side):", sessid, operation, data
|
||||
if operation == 'SLOGIN': # 'server_session_login'
|
||||
|
|
@ -376,7 +434,7 @@ class AMPProtocol(amp.AMP):
|
|||
Access method called by the server side.
|
||||
"""
|
||||
#print "portaladmin (server side):", sessid, operation, data
|
||||
data = utils.to_str(pickle.dumps(data))
|
||||
data = dumps(data)
|
||||
|
||||
self.callRemote(PortalAdmin,
|
||||
sessid=sessid,
|
||||
|
|
|
|||
|
|
@ -59,8 +59,7 @@ def create_objects():
|
|||
character_typeclass=character_typeclass)
|
||||
|
||||
if not god_character:
|
||||
print _("#1 could not be created. Check the Player/Character typeclass for bugs.")
|
||||
raise Exception
|
||||
raise Exception(_("#1 could not be created. Check the Player/Character typeclass for bugs."))
|
||||
|
||||
god_character.id = 1
|
||||
god_character.db.desc = _('This is User #1.')
|
||||
|
|
|
|||
|
|
@ -12,12 +12,16 @@ from datetime import datetime
|
|||
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.utils import logger, utils
|
||||
from src.commands import cmdhandler, cmdsethandler
|
||||
from src.server.session import Session
|
||||
|
||||
IDLE_COMMAND = settings.IDLE_COMMAND
|
||||
|
||||
from src.server.session import Session
|
||||
|
||||
# load optional out-of-band function module
|
||||
OOB_FUNC_MODULE = settings.OOB_FUNC_MODULE
|
||||
if OOB_FUNC_MODULE:
|
||||
OOB_FUNC_MODULE = utils.mod_import(settings.OOB_FUNC_MODULE)
|
||||
|
||||
# i18n
|
||||
from django.utils.translation import ugettext as _
|
||||
|
|
@ -37,7 +41,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 +51,18 @@ 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 session_login(self, player):
|
||||
"""
|
||||
Startup mechanisms that need to run at login. This is called
|
||||
|
|
@ -87,11 +96,10 @@ class ServerSession(Session):
|
|||
player.at_pre_login()
|
||||
|
||||
character = player.character
|
||||
#print "at_init() - character"
|
||||
character.at_init()
|
||||
if character:
|
||||
# this player has a character. Check if it's the
|
||||
# first time *this character* logs in
|
||||
character.at_init()
|
||||
if character.db.FIRST_LOGIN:
|
||||
character.at_first_login()
|
||||
del character.db.FIRST_LOGIN
|
||||
|
|
@ -181,7 +189,7 @@ class ServerSession(Session):
|
|||
if str(command_string).strip() == IDLE_COMMAND:
|
||||
self.update_session_counters(idle=True)
|
||||
return
|
||||
|
||||
|
||||
# all other inputs, including empty inputs
|
||||
character = self.get_character()
|
||||
|
||||
|
|
@ -193,8 +201,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):
|
||||
|
|
@ -203,9 +212,57 @@ class ServerSession(Session):
|
|||
"""
|
||||
self.sessionhandler.data_out(self, msg, data)
|
||||
|
||||
|
||||
def oob_data_in(self, data):
|
||||
"""
|
||||
This receives out-of-band data from the Portal.
|
||||
|
||||
This method parses the data input (a dict) and uses
|
||||
it to launch correct methods from those plugged into
|
||||
the system.
|
||||
|
||||
data = {funcname: ( [args], {kwargs]),
|
||||
funcname: ( [args], {kwargs}), ...}
|
||||
|
||||
example:
|
||||
data = {"get_hp": ([], {}),
|
||||
"update_counter", (["counter1"], {"now":True}) }
|
||||
"""
|
||||
|
||||
print "server: "
|
||||
outdata = {}
|
||||
|
||||
entity = self.get_character()
|
||||
if not entity:
|
||||
entity = self.get_player()
|
||||
if not entity:
|
||||
entity = self
|
||||
|
||||
for funcname, argtuple in data.items():
|
||||
# loop through the data, calling available functions.
|
||||
func = OOB_FUNC_MODULE.__dict__.get(funcname, None)
|
||||
if func:
|
||||
try:
|
||||
outdata[funcname] = func(entity, *argtuple[0], **argtuple[1])
|
||||
except Exception:
|
||||
logger.log_trace()
|
||||
else:
|
||||
logger.log_errmsg("oob_data_in error: funcname '%s' not found in OOB_FUNC_MODULE." % funcname)
|
||||
if outdata:
|
||||
self.oob_data_out(outdata)
|
||||
|
||||
|
||||
def oob_data_out(self, data):
|
||||
"""
|
||||
This sends data from Server to the Portal across the AMP connection.
|
||||
"""
|
||||
self.sessionhandler.oob_data_out(self, data)
|
||||
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.address == other.address
|
||||
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
String representation of the user session class. We use
|
||||
|
|
@ -239,3 +296,57 @@ class ServerSession(Session):
|
|||
def msg(self, string='', data=None):
|
||||
"alias for at_data_out"
|
||||
self.data_out(string, data=data)
|
||||
|
||||
|
||||
# Dummy API hooks for use a non-loggedin operation
|
||||
|
||||
def at_cmdset_get(self):
|
||||
"dummy hook all objects with cmdsets need to have"
|
||||
pass
|
||||
|
||||
# Mock db/ndb properties for allowing easy storage on the session
|
||||
# (note that no databse is involved at all here. session.db.attr =
|
||||
# value just saves a normal property in memory, just like ndb).
|
||||
|
||||
#@property
|
||||
def ndb_get(self):
|
||||
"""
|
||||
A non-persistent store (ndb: NonDataBase). Everything stored
|
||||
to this is guaranteed to be cleared when a server is shutdown.
|
||||
Syntax is same as for the _get_db_holder() method and
|
||||
property, e.g. obj.ndb.attr = value etc.
|
||||
"""
|
||||
try:
|
||||
return self._ndb_holder
|
||||
except AttributeError:
|
||||
class NdbHolder(object):
|
||||
"Holder for storing non-persistent attributes."
|
||||
def all(self):
|
||||
return [val for val in self.__dict__.keys()
|
||||
if not val.startswith['_']]
|
||||
def __getattribute__(self, key):
|
||||
# return None if no matching attribute was found.
|
||||
try:
|
||||
return object.__getattribute__(self, key)
|
||||
except AttributeError:
|
||||
return None
|
||||
self._ndb_holder = NdbHolder()
|
||||
return self._ndb_holder
|
||||
#@ndb.setter
|
||||
def ndb_set(self, value):
|
||||
"Stop accidentally replacing the db object"
|
||||
string = "Cannot assign directly to ndb object! "
|
||||
string = "Use ndb.attr=value instead."
|
||||
raise Exception(string)
|
||||
#@ndb.deleter
|
||||
def ndb_del(self):
|
||||
"Stop accidental deletion."
|
||||
raise Exception("Cannot delete the ndb object!")
|
||||
ndb = property(ndb_get, ndb_set, ndb_del)
|
||||
db = property(ndb_get, ndb_set, ndb_del)
|
||||
|
||||
# Mock access method for the session (there is no lock info
|
||||
# at this stage, so we just present a uniform API)
|
||||
def access(self, *args, **kwargs):
|
||||
"Dummy method."
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -123,3 +123,20 @@ class Session(object):
|
|||
"""
|
||||
pass
|
||||
|
||||
def oob_data_out(self, data):
|
||||
"""
|
||||
for Portal, this receives out-of-band data from Server across the AMP.
|
||||
for Server, this sends out-of-band data to Portal.
|
||||
|
||||
data is a dictionary
|
||||
"""
|
||||
pass
|
||||
|
||||
def oob_data_in(self, data):
|
||||
"""
|
||||
for Portal, this sends out-of-band requests to Server over the AMP.
|
||||
for Server, this receives data from Portal.
|
||||
|
||||
data is a dictionary
|
||||
"""
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ from django.contrib.auth.models import User
|
|||
from src.server.models import ServerConfig
|
||||
from src.utils import utils
|
||||
|
||||
from src.commands.cmdhandler import CMD_LOGINSTART
|
||||
|
||||
# i18n
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
|
|
@ -94,7 +96,7 @@ class ServerSessionHandler(SessionHandler):
|
|||
Creates a new, unlogged-in game session.
|
||||
"""
|
||||
self.sessions[sessid] = session
|
||||
session.execute_cmd('look')
|
||||
session.execute_cmd(CMD_LOGINSTART)
|
||||
|
||||
def portal_disconnect(self, sessid):
|
||||
"""
|
||||
|
|
@ -293,6 +295,20 @@ class ServerSessionHandler(SessionHandler):
|
|||
# to put custom effects on the server due to data input, e.g.
|
||||
# from a custom client.
|
||||
|
||||
def oob_data_in(self, sessid, data):
|
||||
"""
|
||||
OOB (Out-of-band) Data Portal -> Server
|
||||
"""
|
||||
session = self.sessions.get(sessid, None)
|
||||
if session:
|
||||
session.oob_data_in(data)
|
||||
|
||||
def oob_data_out(self, session, data):
|
||||
"""
|
||||
OOB (Out-of-band) Data Server -> Portal
|
||||
"""
|
||||
self.server.amp_protocol.call_remote_OOBServer2Portal(session.sessid,
|
||||
data=data)
|
||||
|
||||
#------------------------------------------------------------
|
||||
# Portal-SessionHandler class
|
||||
|
|
@ -390,5 +406,20 @@ class PortalSessionHandler(SessionHandler):
|
|||
if session:
|
||||
session.data_out(string, data=data)
|
||||
|
||||
def oob_data_in(self, session, data):
|
||||
"""
|
||||
OOB (Out-of-band) data Portal -> Server
|
||||
"""
|
||||
self.portal.amp_protocol.call_remote_OOBPortal2Server(session.sessid,
|
||||
data=data)
|
||||
|
||||
def oob_data_out(self, sessid, data):
|
||||
"""
|
||||
OOB (Out-of-band) data Server -> Portal
|
||||
"""
|
||||
session = self.sessions.get(sessid, None)
|
||||
if session:
|
||||
session.oob_data_out(data)
|
||||
|
||||
SESSIONS = ServerSessionHandler()
|
||||
PORTAL_SESSIONS = PortalSessionHandler()
|
||||
|
|
|
|||
|
|
@ -167,6 +167,12 @@ AT_INITIAL_SETUP_HOOK_MODULE = "game.gamesrc.world.at_initial_setup"
|
|||
###################################################
|
||||
# Default command sets
|
||||
###################################################
|
||||
# Note that with the exception of the unloggedin set (which is not
|
||||
# stored anywhere), changing these paths will only affect NEW created
|
||||
# characters, not those already in play. So if you plan to change
|
||||
# this, it's recommended you do it on a pristine setup only. To
|
||||
# dynamically add new commands to a running server, extend/overload
|
||||
# these existing sets instead.
|
||||
|
||||
# Command set used before player has logged in
|
||||
CMDSET_UNLOGGEDIN = "game.gamesrc.commands.basecmdset.UnloggedinCmdSet"
|
||||
|
|
@ -246,6 +252,8 @@ PERMISSION_PLAYER_DEFAULT = "Players"
|
|||
# Tuple of modules implementing lock functions. All callable functions
|
||||
# inside these modules will be available as lock functions.
|
||||
LOCK_FUNC_MODULES = ("src.locks.lockfuncs",)
|
||||
# Module holding server-side functions for out-of-band protocols to call.
|
||||
OOB_FUNC_MODULE = ""
|
||||
|
||||
|
||||
###################################################
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ def create_object(typeclass, key=None, location=None,
|
|||
# this will either load the typeclass or the default one
|
||||
new_object = new_db_object.typeclass
|
||||
|
||||
|
||||
if not object.__getattribute__(new_db_object, "is_typeclass")(typeclass, exact=True):
|
||||
# this will fail if we gave a typeclass as input and it still gave us a default
|
||||
SharedMemoryModel.delete(new_db_object)
|
||||
|
|
@ -105,6 +106,10 @@ def create_object(typeclass, key=None, location=None,
|
|||
# perform a move_to in order to display eventual messages.
|
||||
if home:
|
||||
new_object.home = home
|
||||
else:
|
||||
new_object.home = settings.CHARACTER_DEFAULT_HOME
|
||||
|
||||
|
||||
if location:
|
||||
new_object.move_to(location, quiet=True)
|
||||
else:
|
||||
|
|
@ -389,6 +394,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:
|
||||
|
|
|
|||
|
|
@ -542,7 +542,7 @@ def has_parent(basepath, obj):
|
|||
|
||||
def mod_import(mod_path, propname=None):
|
||||
"""
|
||||
Takes filename of a module, converts it to a python path
|
||||
Takes filename of a module (a python path or a full pathname)
|
||||
and imports it. If property is given, return the named
|
||||
property from this module instead of the module itself.
|
||||
"""
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue