mirror of
https://github.com/evennia/evennia.git
synced 2026-03-19 22:36:31 +01:00
Evennia now runs on its own Twisted webserver (no need for testserver or Apache if you don't want to). Evennia now also has an ajax long-polling web client running from Twisted. The web client requires no extra dependencies beyond jQuery which is included. The src/server structure has been r
cleaned up and rewritten to make it easier to add new protocols in the future - all new protocols need to inherit from server.session.Session, whi ch implements a set of hooks that Evennia uses to communicate. The current web client protocol is functional but does not implement any of rcaskey 's suggestions as of yet - it uses a separate data object passed through msg() to communicate between the server and the various protocols. Also the client itself could probably need cleanup and 'prettification'. The fact that the system runs a hybrid of Django and Twisted, getting the best of both worlds should allow for many possibilities in the future. /Griatch
This commit is contained in:
parent
ecefbfac01
commit
251f94aa7a
118 changed files with 9049 additions and 593 deletions
|
|
@ -1,137 +1,184 @@
|
|||
"""
|
||||
Sessionhandler, stores and handles
|
||||
a list of all player connections (sessions).
|
||||
This module handles sessions of users connecting
|
||||
to the server.
|
||||
|
||||
Since Evennia supports several different connection
|
||||
protocols, it is important to have a joint place
|
||||
to store session info. It also makes it easier
|
||||
to dispatch data.
|
||||
|
||||
Whereas server.py handles all setup of the server
|
||||
and database itself, this file handles all that
|
||||
comes after initial startup.
|
||||
|
||||
All new sessions (of whatever protocol) are responsible for
|
||||
registering themselves with this module.
|
||||
|
||||
"""
|
||||
import time
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from src.config.models import ConfigValue
|
||||
from src.utils import logger
|
||||
|
||||
# Our list of connected sessions.
|
||||
SESSIONS = []
|
||||
ALLOW_MULTISESSION = settings.ALLOW_MULTISESSION
|
||||
|
||||
def add_session(session):
|
||||
"""
|
||||
Adds a session to the session list.
|
||||
"""
|
||||
SESSIONS.insert(0, session)
|
||||
change_session_count(1)
|
||||
logger.log_infomsg('Sessions active: %d' % (len(get_sessions(return_unlogged=True),)))
|
||||
|
||||
def get_sessions(return_unlogged=False):
|
||||
"""
|
||||
Lists the connected session objects.
|
||||
"""
|
||||
if return_unlogged:
|
||||
return SESSIONS
|
||||
else:
|
||||
return [sess for sess in SESSIONS if sess.logged_in]
|
||||
|
||||
def get_session_id_list(return_unlogged=False):
|
||||
"""
|
||||
Lists the connected session object ids.
|
||||
"""
|
||||
if return_unlogged:
|
||||
return SESSIONS
|
||||
else:
|
||||
return [sess.uid for sess in SESSIONS if sess.logged_in]
|
||||
#------------------------------------------------------------
|
||||
# SessionHandler class
|
||||
#------------------------------------------------------------
|
||||
|
||||
def disconnect_all_sessions():
|
||||
class SessionHandler(object):
|
||||
"""
|
||||
Cleanly disconnect all of the connected sessions.
|
||||
"""
|
||||
for sess in get_sessions():
|
||||
sess.handle_close()
|
||||
This object holds the stack of sessions active in the game at
|
||||
any time.
|
||||
|
||||
def disconnect_duplicate_session(session):
|
||||
"""
|
||||
Disconnects any existing session under the same object. This is used in
|
||||
connection recovery to help with record-keeping.
|
||||
"""
|
||||
SESSIONS = get_sessions()
|
||||
session_pobj = session.get_character()
|
||||
for other_session in SESSIONS:
|
||||
other_pobject = other_session.get_character()
|
||||
if session_pobj == other_pobject and other_session != session:
|
||||
other_session.msg("Your account has been logged in from elsewhere, disconnecting.")
|
||||
other_session.disconnectClient()
|
||||
return True
|
||||
return False
|
||||
A session register with the handler in two steps, first by
|
||||
registering itself with the connect() method. This indicates an
|
||||
non-authenticated session. Whenever the session is authenticated
|
||||
the session together with the related player is sent to the login()
|
||||
method.
|
||||
|
||||
def check_all_sessions():
|
||||
"""
|
||||
Check all currently connected sessions and see if any are dead.
|
||||
"""
|
||||
idle_timeout = int(ConfigValue.objects.conf('idle_timeout'))
|
||||
|
||||
if len(SESSIONS) <= 0:
|
||||
return
|
||||
|
||||
if idle_timeout <= 0:
|
||||
return
|
||||
|
||||
for sess in get_sessions(return_unlogged=True):
|
||||
if (time.time() - sess.cmd_last) > idle_timeout:
|
||||
sess.msg("Idle timeout exceeded, disconnecting.")
|
||||
sess.handle_close()
|
||||
|
||||
def change_session_count(num):
|
||||
"""
|
||||
Count number of connected users by use of a config value
|
||||
|
||||
num can be a positive or negative value. If 0, the counter
|
||||
will be reset to 0.
|
||||
"""
|
||||
|
||||
if num == 0:
|
||||
# reset
|
||||
ConfigValue.objects.conf('nr_sessions', 0)
|
||||
|
||||
nr = ConfigValue.objects.conf('nr_sessions')
|
||||
if nr == None:
|
||||
nr = 0
|
||||
else:
|
||||
nr = int(nr)
|
||||
nr += num
|
||||
ConfigValue.objects.conf('nr_sessions', str(nr))
|
||||
def __init__(self):
|
||||
"""
|
||||
Init the handler. We track two types of sessions, those
|
||||
who have just connected (unloggedin) and those who have
|
||||
logged in (authenticated).
|
||||
"""
|
||||
self.unloggedin = []
|
||||
self.loggedin = []
|
||||
|
||||
|
||||
def remove_session(session):
|
||||
"""
|
||||
Removes a session from the session list.
|
||||
"""
|
||||
try:
|
||||
SESSIONS.remove(session)
|
||||
change_session_count(-1)
|
||||
logger.log_infomsg('Sessions active: %d' % (len(get_sessions()),))
|
||||
except ValueError:
|
||||
# the session was already removed.
|
||||
logger.log_errmsg("Unable to remove session: %s" % (session,))
|
||||
return
|
||||
|
||||
def find_sessions_from_username(username):
|
||||
"""
|
||||
Given a username, return any matching sessions.
|
||||
"""
|
||||
try:
|
||||
uobj = User.objects.get(username=username)
|
||||
uid = uobj.id
|
||||
return [session for session in SESSIONS if session.uid == uid]
|
||||
except User.DoesNotExist:
|
||||
return None
|
||||
|
||||
def sessions_from_object(targ_object):
|
||||
"""
|
||||
Returns a list of matching session objects, or None if there are no matches.
|
||||
|
||||
targobject: (Object) The object to match.
|
||||
"""
|
||||
return [session for session in SESSIONS
|
||||
if session.get_character() == targ_object]
|
||||
def add_unloggedin_session(self, session):
|
||||
"""
|
||||
Call at first connect. This adds a not-yet authenticated session.
|
||||
"""
|
||||
self.unloggedin.insert(0, session)
|
||||
|
||||
def announce_all(message):
|
||||
"""
|
||||
Announces something to all connected players.
|
||||
"""
|
||||
for session in get_sessions():
|
||||
session.msg('%s' % message)
|
||||
def add_loggedin_session(self, session):
|
||||
"""
|
||||
Log in the previously unloggedin session and the player we by
|
||||
now should know is connected to it. After this point we
|
||||
assume the session to be logged in one way or another.
|
||||
"""
|
||||
# prep the session with player/user info
|
||||
|
||||
|
||||
if not ALLOW_MULTISESSION:
|
||||
# disconnect previous sessions.
|
||||
self.disconnect_duplicate_sessions(session)
|
||||
|
||||
# store/move the session to the right list
|
||||
try:
|
||||
self.unloggedin.remove(session)
|
||||
except ValueError:
|
||||
pass
|
||||
self.loggedin.insert(0, session)
|
||||
self.session_count(1)
|
||||
|
||||
def remove_session(self, session):
|
||||
"""
|
||||
Remove session from the handler
|
||||
"""
|
||||
removed = False
|
||||
try:
|
||||
self.unloggedin.remove(session)
|
||||
except Exception:
|
||||
try:
|
||||
self.loggedin.remove(session)
|
||||
except Exception:
|
||||
return
|
||||
self.session_count(-1)
|
||||
|
||||
def get_sessions(self, include_unloggedin=False):
|
||||
"""
|
||||
Returns the connected session objects.
|
||||
"""
|
||||
if include_unloggedin:
|
||||
return self.loggedin + self.unloggedin
|
||||
else:
|
||||
return self.loggedin
|
||||
|
||||
def disconnect_all_sessions(self, reason=None):
|
||||
"""
|
||||
Cleanly disconnect all of the connected sessions.
|
||||
"""
|
||||
sessions = self.get_sessions(include_unloggedin=True)
|
||||
for session in sessions:
|
||||
session.session_disconnect(reason)
|
||||
self.session_count(0)
|
||||
|
||||
def disconnect_duplicate_sessions(self, session):
|
||||
"""
|
||||
Disconnects any existing sessions with the same game object. This is used in
|
||||
connection recovery to help with record-keeping.
|
||||
"""
|
||||
reason = "Your account has been logged in from elsewhere. Disconnecting."
|
||||
sessions = self.get_sessions()
|
||||
session_character = self.get_character(session)
|
||||
logged_out = 0
|
||||
for other_session in sessions:
|
||||
other_character = self.get_character(other_session)
|
||||
if session_character == other_character and other_session != session:
|
||||
self.remove_session(other_session, reason=reason)
|
||||
logged_out += 1
|
||||
self.session_count(-logged_out)
|
||||
return logged_out
|
||||
|
||||
def validate_sessions(self):
|
||||
"""
|
||||
Check all currently connected sessions (logged in and not)
|
||||
and see if any are dead.
|
||||
"""
|
||||
for session in self.get_sessions(include_unloggedin=True):
|
||||
session.session_validate()
|
||||
|
||||
def session_count(self, num=None):
|
||||
"""
|
||||
Count up/down the number of connected, authenticated users.
|
||||
If num is None, the current number of sessions is returned.
|
||||
|
||||
num can be a positive or negative value to be added to the current count.
|
||||
If 0, the counter will be reset to 0.
|
||||
"""
|
||||
if num == None:
|
||||
# show the current value. This also syncs it.
|
||||
return int(ConfigValue.objects.conf('nr_sessions', default=0))
|
||||
elif num == 0:
|
||||
# reset value to 0
|
||||
ConfigValue.objects.conf('nr_sessions', 0)
|
||||
else:
|
||||
# add/remove session count from value
|
||||
add = int(ConfigValue.objects.conf('nr_sessions', default=0))
|
||||
num = max(0, num + add)
|
||||
ConfigValue.objects.conf('nr_sessions', str(num))
|
||||
|
||||
def sessions_from_player(self, player):
|
||||
"""
|
||||
Given a player, return any matching sessions.
|
||||
"""
|
||||
username = player.user.username
|
||||
try:
|
||||
uobj = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
return None
|
||||
uid = uobj.id
|
||||
return [session for session in self.loggedin if session.uid == uid]
|
||||
|
||||
def sessions_from_character(self, character):
|
||||
"""
|
||||
Given a game character, return any matching sessions.
|
||||
"""
|
||||
player = character.player
|
||||
if player:
|
||||
return self.sessions_from_player(player)
|
||||
return None
|
||||
|
||||
def session_from_suid(self, suid):
|
||||
"""
|
||||
Given a session id, retrieve the session (this is primarily
|
||||
intended to be called by web clients)
|
||||
"""
|
||||
return [sess for sess in self.get_sessions(include_unloggedin=True) if sess.suid and sess.suid == suid]
|
||||
|
||||
SESSIONS = SessionHandler()
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue