evennia/src/server/serversession.py

242 lines
7.6 KiB
Python
Raw Normal View History

"""
This defines a the Server's generic session object. This object represents
a connection to the outside world but don't know any details about how the
connection actually happens (so it's the same for telnet, web, ssh etc).
It is stored on the Server side (as opposed to protocol-specific sessions which
are stored on the Portal side)
"""
import time
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
IDLE_COMMAND = settings.IDLE_COMMAND
from src.server.session import Session
# i18n
from django.utils.translation import ugettext as _
#------------------------------------------------------------
# Server Session
#------------------------------------------------------------
class ServerSession(Session):
"""
This class represents a player's session and is a template for
individual protocols to communicate with Evennia.
Each player gets a session assigned to them whenever they connect
to the game server. All communication between game and player goes
through their session.
"""
def at_sync(self):
"""
This is called whenever a session has been resynced with the portal.
At this point all relevant attributes have already been set and self.player
been assigned (if applicable).
Since this is often called after a server restart we need to set up
the session as it was.
"""
if not self.logged_in:
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
by the login command (which need to have handled authentication
already before calling this method)
player - the connected player
"""
# actually do the login by assigning session data
self.player = player
self.user = player.user
self.uid = self.user.id
self.uname = self.user.username
self.logged_in = True
self.conn_time = time.time()
# Update account's last login time.
self.user.last_login = datetime.now()
self.user.save()
# player init
#print "at_init() - player"
player.at_init()
# Check if this is the first time the *player* logs in
if player.db.FIRST_LOGIN:
player.at_first_login()
del player.db.FIRST_LOGIN
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
if character.db.FIRST_LOGIN:
character.at_first_login()
del character.db.FIRST_LOGIN
# run character login hook
character.at_pre_login()
self.log(_('Logged in: %(self)s') % {'self': self})
# start (persistent) scripts on this object
ScriptDB.objects.validate(obj=self.player.character)
#add session to connected list
self.sessionhandler.login(self)
# post-login hooks
player.at_post_login()
if character:
character.at_post_login()
def session_disconnect(self):
"""
Clean up the session, removing it from the game and doing some
accounting. This method is used also for non-loggedin
accounts.
"""
if self.logged_in:
player = self.get_player()
character = self.get_character()
if character:
character.at_disconnect()
uaccount = player.user
uaccount.last_login = datetime.now()
uaccount.save()
self.logged_in = False
self.sessionhandler.disconnect(self)
def get_player(self):
"""
Get the player associated with this session
"""
if self.logged_in:
return self.player
else:
return None
def get_character(self):
"""
Returns the in-game character associated with this session.
This returns the typeclass of the object.
"""
player = self.get_player()
if player:
return player.character
return None
def log(self, message, channel=True):
"""
Emits session info to the appropriate outputs and info channels.
"""
if channel:
try:
cchan = settings.CHANNEL_CONNECTINFO
cchan = Channel.objects.get_channel(cchan[0])
cchan.msg("[%s]: %s" % (cchan.key, message))
except Exception:
pass
logger.log_infomsg(message)
def update_session_counters(self, idle=False):
"""
Hit this when the user enters a command in order to update idle timers
and command counters.
"""
# Store the timestamp of the user's last command.
self.cmd_last = time.time()
if not idle:
# Increment the user's command counter.
self.cmd_total += 1
# Player-visible idle time, not used in idle timeout calcs.
self.cmd_last_visible = time.time()
def execute_cmd(self, command_string):
"""
Execute a command string on the server.
"""
# handle the 'idle' command
if str(command_string).strip() == IDLE_COMMAND:
self.update_session_counters(idle=True)
return
# all other inputs, including empty inputs
character = self.get_character()
if character:
character.execute_cmd(command_string)
else:
if self.logged_in:
# 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)
self.update_session_counters()
def data_out(self, msg, data=None):
"""
Send Evennia -> Player
"""
self.sessionhandler.data_out(self, msg, data)
def __eq__(self, other):
return self.address == other.address
def __str__(self):
"""
String representation of the user session class. We use
this a lot in the server logs.
"""
if self.logged_in:
symbol = '#'
else:
symbol = '?'
try:
address = ":".join([str(part) for part in self.address])
except Exception:
address = self.address
return "<%s> %s@%s" % (symbol, self.uname, address)
def __unicode__(self):
"""
Unicode representation
"""
return u"%s" % str(self)
# easy-access functions
def login(self, player):
"alias for at_login"
self.session_login(player)
def disconnect(self):
"alias for session_disconnect"
self.session_disconnect()
def msg(self, string='', data=None):
"alias for at_data_out"
self.data_out(string, data=data)