Trunk: Merged the Devel-branch (branches/griatch) into /trunk. This constitutes a major refactoring of Evennia. Development will now continue in trunk. See the wiki and the past posts to the mailing list for info. /Griatch
This commit is contained in:
parent
df29defbcd
commit
f83c2bddf8
222 changed files with 22304 additions and 14371 deletions
0
src/server/__init__.py
Normal file
0
src/server/__init__.py
Normal file
250
src/server/initial_setup.py
Normal file
250
src/server/initial_setup.py
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
"""
|
||||
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.
|
||||
|
||||
Everything starts at handle_setup()
|
||||
"""
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core import management
|
||||
from django.conf import settings
|
||||
|
||||
from src.config.models import ConfigValue, ConnectScreen
|
||||
from src.objects.models import ObjectDB
|
||||
from src.comms.models import Channel, ChannelConnection
|
||||
from src.players.models import PlayerDB
|
||||
from src.help.models import HelpEntry
|
||||
from src.scripts import scripts
|
||||
from src.utils import create
|
||||
from src.utils import gametime
|
||||
|
||||
def create_config_values():
|
||||
"""
|
||||
Creates the initial config values.
|
||||
"""
|
||||
ConfigValue.objects.conf("default_home", "2")
|
||||
ConfigValue.objects.conf("idle_timeout", "3600")
|
||||
#ConfigValue.objects.conf("money_name_singular", "Credit")
|
||||
#ConfigValue.objects.conf("money_name_plural", "Credits")
|
||||
ConfigValue.objects.conf("site_name", settings.SERVERNAME)
|
||||
|
||||
def create_connect_screens():
|
||||
"""
|
||||
Creates the default connect screen(s).
|
||||
"""
|
||||
|
||||
print " Creating startup screen(s) ..."
|
||||
|
||||
text = "%ch%cb==================================================================%cn"
|
||||
text += "\r\n Welcome to %chEvennia%cn! Please type one of the following to begin:\r\n"
|
||||
text += "\r\n If you have an existing account, connect to it by typing:\r\n "
|
||||
text += "%chconnect <email> <password>%cn\r\n If you need to create an account, "
|
||||
text += "type (without the <>'s):\r\n "
|
||||
text += "%chcreate \"<username>\" <email> <password>%cn\r\n"
|
||||
text += "\r\n Enter %chhelp%cn for more info. %chlook%cn will re-show this screen.\r\n"
|
||||
text += "%ch%cb==================================================================%cn\r\n"
|
||||
ConnectScreen(db_key="Default", db_text=text, db_is_active=True).save()
|
||||
|
||||
def get_god_user():
|
||||
"""
|
||||
Returns the initially created 'god' User object.
|
||||
"""
|
||||
return User.objects.get(id=1)
|
||||
|
||||
def create_objects():
|
||||
"""
|
||||
Creates the #1 player and Limbo room.
|
||||
"""
|
||||
|
||||
print " Creating objects (Player #1 and Limbo room) ..."
|
||||
|
||||
# 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_user = get_god_user()
|
||||
|
||||
# Create a Player 'user profile' object to hold eventual
|
||||
# mud-specific settings for the bog standard User object. This is
|
||||
# accessed by user.get_profile() and can also store attributes.
|
||||
# It also holds mud permissions, but for a superuser these
|
||||
# have no effect anyhow.
|
||||
|
||||
character_typeclass = settings.BASE_CHARACTER_TYPECLASS
|
||||
|
||||
# Create the Player object as well as the in-game god-character
|
||||
# for user #1. We can't set location and home yet since nothing
|
||||
# exists. Also, all properties (name, email, password, is_superuser)
|
||||
# is inherited from the user so we don't specify it again here.
|
||||
|
||||
god_character = create.create_player(god_user.username, None, None,
|
||||
create_character=True,
|
||||
typeclass=character_typeclass,
|
||||
user=god_user)
|
||||
god_character.id = 1
|
||||
god_character.attr('desc', 'You are Player #1.')
|
||||
god_character.save()
|
||||
|
||||
# Limbo is the initial starting room.
|
||||
|
||||
object_typeclass = settings.BASE_OBJECT_TYPECLASS
|
||||
limbo_obj = create.create_object(object_typeclass, 'Limbo')
|
||||
limbo_obj.id = 2
|
||||
string = "Welcome to your new %chEvennia%cn-based game."
|
||||
string += " From here you are ready to begin development."
|
||||
string += " If you should need help or would like to participate"
|
||||
string += " in community discussions, visit http://evennia.com."
|
||||
limbo_obj.attr('desc', string)
|
||||
limbo_obj.save()
|
||||
|
||||
# Now that Limbo exists, set the user up in Limbo.
|
||||
god_character.location = limbo_obj
|
||||
god_character.home = limbo_obj
|
||||
|
||||
def create_channels():
|
||||
"""
|
||||
Creates some sensible default channels.
|
||||
"""
|
||||
print " Creating default channels ..."
|
||||
|
||||
# public channel
|
||||
key, aliases, desc, perms = settings.CHANNEL_PUBLIC
|
||||
pchan = create.create_channel(key, aliases, desc, perms)
|
||||
# mudinfo channel
|
||||
key, aliases, desc, perms = settings.CHANNEL_MUDINFO
|
||||
ichan = create.create_channel(key, aliases, desc, perms)
|
||||
# connectinfo channel
|
||||
key, aliases, desc, perms = settings.CHANNEL_CONNECTINFO
|
||||
cchan = create.create_channel(key, aliases, desc, perms)
|
||||
|
||||
# connect the god user to all these channels by default.
|
||||
goduser = get_god_user()
|
||||
ChannelConnection.objects.create_connection(goduser, pchan)
|
||||
ChannelConnection.objects.create_connection(goduser, ichan)
|
||||
ChannelConnection.objects.create_connection(goduser, cchan)
|
||||
|
||||
def import_MUX_help_files():
|
||||
"""
|
||||
Imports the MUX help files.
|
||||
"""
|
||||
print " Importing MUX help database (devel reference only) ..."
|
||||
management.call_command('loaddata', '../src/help/mux_help_db.json', verbosity=0)
|
||||
# categorize the MUX help files into its own category.
|
||||
default_category = "MUX"
|
||||
print " Moving imported help db to help category '%s'." \
|
||||
% default_category
|
||||
HelpEntry.objects.all_to_category(default_category)
|
||||
|
||||
def create_permission_groups():
|
||||
"""
|
||||
This sets up the default permissions groups
|
||||
by parsing a permission config file.
|
||||
|
||||
Note that we don't catch any exceptions here,
|
||||
this must be debugged until it works.
|
||||
"""
|
||||
|
||||
print " Creating and setting up permissions/groups ..."
|
||||
|
||||
# We try to get the data from config first.
|
||||
setup_path = settings.PERMISSION_SETUP_MODULE
|
||||
if not setup_path:
|
||||
# go with the default
|
||||
setup_path = "src.permissions.default_permissions"
|
||||
module = __import__(setup_path, fromlist=[True])
|
||||
# We have a successful import. Get the dicts.
|
||||
groupdict = module.GROUPS
|
||||
|
||||
# Create groups and populate them
|
||||
for group in groupdict:
|
||||
group = create.create_permission_group(group, desc=group,
|
||||
group_perms=groupdict[group])
|
||||
if not group:
|
||||
print " Creation of Group '%s' failed." % group
|
||||
|
||||
def create_system_scripts():
|
||||
"""
|
||||
Setup the system repeat scripts. They are automatically started
|
||||
by the create_script function.
|
||||
"""
|
||||
|
||||
print " Creating and starting global scripts ..."
|
||||
|
||||
# check so that all sessions are alive.
|
||||
script1 = create.create_script(scripts.CheckSessions)
|
||||
# validate all scripts in script table.
|
||||
script2 = create.create_script(scripts.ValidateScripts)
|
||||
# update the channel handler to make sure it's in sync
|
||||
script3 = create.create_script(scripts.ValidateChannelHandler)
|
||||
if not script1 or not script2 or not script3:
|
||||
print " Error creating system scripts."
|
||||
|
||||
def start_game_time():
|
||||
"""
|
||||
This starts a persistent script that keeps track of the
|
||||
in-game time (in whatever accelerated reference frame), but also
|
||||
the total run time of the server as well as its current uptime
|
||||
(the uptime can also be found directly from the server though).
|
||||
"""
|
||||
print " Starting in-game time ..."
|
||||
|
||||
gametime.init_gametime()
|
||||
|
||||
def handle_setup(last_step):
|
||||
"""
|
||||
Main logic for the module. It allows to restart the initialization
|
||||
if one of the modules should crash.
|
||||
"""
|
||||
|
||||
if last_step < 0:
|
||||
# this means we don't need to handle setup since
|
||||
# it already ran sucessfully once.
|
||||
return
|
||||
elif last_step == None:
|
||||
# config doesn't exist yet. First start of server
|
||||
last_step = 0
|
||||
|
||||
# setting up the list of functions to run
|
||||
setup_queue = [
|
||||
create_config_values,
|
||||
create_connect_screens,
|
||||
create_objects,
|
||||
create_channels,
|
||||
create_permission_groups,
|
||||
create_system_scripts,
|
||||
import_MUX_help_files,
|
||||
start_game_time]
|
||||
|
||||
if not settings.IMPORT_MUX_HELP:
|
||||
# skip importing of the MUX helpfiles, they are
|
||||
# not interesting except for developers.
|
||||
del setup_queue[6]
|
||||
|
||||
#print " Initial setup: %s steps." % (len(setup_queue))
|
||||
|
||||
# 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.
|
||||
#print "%s..." % num
|
||||
|
||||
try:
|
||||
setup_func()
|
||||
except Exception:
|
||||
if last_step + num == 2:
|
||||
for obj in ObjectDB.objects.all():
|
||||
obj.delete()
|
||||
for profile in PlayerDB.objects.all():
|
||||
profile.delete()
|
||||
elif last_step + num == 3:
|
||||
for chan in Channel.objects.all():
|
||||
chan.delete()
|
||||
for conn in ChannelConnection.objects.all():
|
||||
conn.delete()
|
||||
|
||||
|
||||
raise
|
||||
ConfigValue.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.
|
||||
ConfigValue.objects.conf("last_initial_setup_step", -1)
|
||||
155
src/server/server.py
Normal file
155
src/server/server.py
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
"""
|
||||
This module implements the main Evennia
|
||||
server process, the core of the game engine.
|
||||
"""
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
if os.name == 'nt':
|
||||
# For Windows batchfile we need an extra path insertion here.
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(
|
||||
os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from twisted.application import internet, service
|
||||
from twisted.internet import protocol, reactor
|
||||
from django.db import connection
|
||||
from django.conf import settings
|
||||
|
||||
from src.config.models import ConfigValue
|
||||
from src.server.session import SessionProtocol
|
||||
from src.server import sessionhandler
|
||||
from src.server import initial_setup
|
||||
from src.utils import reloads
|
||||
from src.utils.utils import get_evennia_version
|
||||
from src.comms import channelhandler
|
||||
|
||||
class EvenniaService(service.Service):
|
||||
"""
|
||||
The main server service task.
|
||||
"""
|
||||
def __init__(self):
|
||||
# Holds the TCP services.
|
||||
self.service_collection = None
|
||||
self.game_running = True
|
||||
sys.path.append('.')
|
||||
|
||||
# Database-specific startup optimizations.
|
||||
if (settings.DATABASE_ENGINE == "sqlite3"
|
||||
or hasattr(settings, 'DATABASE')
|
||||
and settings.DATABASE.get('ENGINE', None) == 'django.db.backends.sqlite3'):
|
||||
# run sqlite3 preps
|
||||
self.sqlite3_prep()
|
||||
|
||||
# Begin startup debug output.
|
||||
print '\n' + '-'*50
|
||||
|
||||
last_initial_setup_step = \
|
||||
ConfigValue.objects.conf('last_initial_setup_step')
|
||||
|
||||
if not last_initial_setup_step:
|
||||
# None is only returned if the config does not exist,
|
||||
# i.e. this is an empty DB that needs populating.
|
||||
print ' Server started for the first time. Setting defaults.'
|
||||
initial_setup.handle_setup(0)
|
||||
print '-'*50
|
||||
|
||||
elif int(last_initial_setup_step) >= 0:
|
||||
# last_setup_step >= 0 means the setup crashed
|
||||
# on one of its modules and setup will resume, retrying
|
||||
# the last failed module. When all are finished, the step
|
||||
# is set to -1 to show it does not need to be run again.
|
||||
print ' Resuming initial setup from step %s.' % \
|
||||
last_initial_setup_step
|
||||
initial_setup.handle_setup(int(last_initial_setup_step))
|
||||
print '-'*50
|
||||
|
||||
# we have to null this here.
|
||||
sessionhandler.change_session_count(0)
|
||||
|
||||
self.start_time = time.time()
|
||||
|
||||
# initialize channelhandler
|
||||
channelhandler.CHANNELHANDLER.update()
|
||||
# init all global scripts
|
||||
reloads.reload_scripts(init_mode=True)
|
||||
|
||||
# Make output to the terminal.
|
||||
print ' %s (%s) started on port(s):' % \
|
||||
(settings.SERVERNAME, get_evennia_version())
|
||||
for port in settings.GAMEPORTS:
|
||||
print ' * %s' % (port)
|
||||
print '-'*50
|
||||
|
||||
|
||||
|
||||
# Server startup methods
|
||||
|
||||
def sqlite3_prep(self):
|
||||
"""
|
||||
Optimize some SQLite stuff at startup since we
|
||||
can't save it to the database.
|
||||
"""
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("PRAGMA cache_size=10000")
|
||||
cursor.execute("PRAGMA synchronous=OFF")
|
||||
cursor.execute("PRAGMA count_changes=OFF")
|
||||
cursor.execute("PRAGMA temp_store=2")
|
||||
|
||||
|
||||
# General methods
|
||||
|
||||
def shutdown(self, message=None):
|
||||
"""
|
||||
Gracefully disconnect everyone and kill the reactor.
|
||||
"""
|
||||
if not message:
|
||||
message = 'The server has been shutdown. Please check back soon.'
|
||||
sessionhandler.announce_all(message)
|
||||
sessionhandler.disconnect_all_sessions()
|
||||
reactor.callLater(0, reactor.stop)
|
||||
|
||||
def getEvenniaServiceFactory(self):
|
||||
"Retrieve instances of the server"
|
||||
factory = protocol.ServerFactory()
|
||||
factory.protocol = SessionProtocol
|
||||
factory.server = self
|
||||
return factory
|
||||
|
||||
def start_services(self, application):
|
||||
"""
|
||||
Starts all of the TCP services.
|
||||
"""
|
||||
self.service_collection = service.IServiceCollection(application)
|
||||
for port in settings.GAMEPORTS:
|
||||
evennia_server = \
|
||||
internet.TCPServer(port, self.getEvenniaServiceFactory())
|
||||
evennia_server.setName('Evennia%s' %port)
|
||||
evennia_server.setServiceParent(self.service_collection)
|
||||
|
||||
if settings.IMC2_ENABLED:
|
||||
from src.imc2.connection import IMC2ClientFactory
|
||||
from src.imc2 import events as imc2_events
|
||||
imc2_factory = IMC2ClientFactory()
|
||||
svc = internet.TCPClient(settings.IMC2_SERVER_ADDRESS,
|
||||
settings.IMC2_SERVER_PORT,
|
||||
imc2_factory)
|
||||
svc.setName('IMC2')
|
||||
svc.setServiceParent(self.service_collection)
|
||||
imc2_events.add_events()
|
||||
|
||||
if settings.IRC_ENABLED:
|
||||
from src.irc.connection import IRC_BotFactory
|
||||
irc = internet.TCPClient(settings.IRC_NETWORK,
|
||||
settings.IRC_PORT,
|
||||
IRC_BotFactory(settings.IRC_CHANNEL,
|
||||
settings.IRC_NETWORK,
|
||||
settings.IRC_NICKNAME))
|
||||
irc.setName("%s:%s" % ("IRC", settings.IRC_CHANNEL))
|
||||
irc.setServiceParent(self.service_collection)
|
||||
|
||||
|
||||
# Twisted requires us to define an 'application' attribute.
|
||||
application = service.Application('Evennia')
|
||||
# The main mud service. Import this for access to the server methods.
|
||||
mud_service = EvenniaService()
|
||||
mud_service.start_services(application)
|
||||
262
src/server/session.py
Normal file
262
src/server/session.py
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
"""
|
||||
This module contains classes related to Sessions. sessionhandler has the things
|
||||
needed to manage them.
|
||||
"""
|
||||
import time
|
||||
from datetime import datetime
|
||||
from twisted.conch.telnet import StatefulTelnetProtocol
|
||||
from django.conf import settings
|
||||
from src.server import sessionhandler
|
||||
from src.objects.models import ObjectDB
|
||||
from src.comms.models import Channel
|
||||
from src.config.models import ConnectScreen
|
||||
from src.commands import cmdhandler
|
||||
from src.utils import ansi
|
||||
from src.utils import reloads
|
||||
from src.utils import logger
|
||||
from src.utils import utils
|
||||
|
||||
class SessionProtocol(StatefulTelnetProtocol):
|
||||
"""
|
||||
This class represents a player's session. Each player
|
||||
gets a session assigned to them whenever
|
||||
they connect to the game server. All communication
|
||||
between game and player goes through here.
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
String representation of the user session class. We use
|
||||
this a lot in the server logs and stuff.
|
||||
"""
|
||||
if self.logged_in:
|
||||
symbol = '#'
|
||||
else:
|
||||
symbol = '?'
|
||||
return "<%s> %s@%s" % (symbol, self.name, self.address,)
|
||||
|
||||
def connectionMade(self):
|
||||
"""
|
||||
What to do when we get a connection.
|
||||
"""
|
||||
# setup the parameters
|
||||
self.prep_session()
|
||||
# send info
|
||||
logger.log_infomsg('New connection: %s' % self)
|
||||
# add this new session to handler
|
||||
sessionhandler.add_session(self)
|
||||
# show a connect screen
|
||||
self.game_connect_screen()
|
||||
|
||||
def getClientAddress(self):
|
||||
"""
|
||||
Returns the client's address and port in a tuple. For example
|
||||
('127.0.0.1', 41917)
|
||||
"""
|
||||
return self.transport.client
|
||||
|
||||
def prep_session(self):
|
||||
"""
|
||||
This sets up the main parameters of
|
||||
the session. The game will poll these
|
||||
properties to check the status of the
|
||||
connection and to be able to contact
|
||||
the connected player.
|
||||
"""
|
||||
# main server properties
|
||||
self.server = self.factory.server
|
||||
self.address = self.getClientAddress()
|
||||
|
||||
# player setup
|
||||
self.name = None
|
||||
self.uid = None
|
||||
self.logged_in = False
|
||||
|
||||
# The time the user last issued a command.
|
||||
self.cmd_last = time.time()
|
||||
# Player-visible idle time, excluding the IDLE command.
|
||||
self.cmd_last_visible = time.time()
|
||||
# Total number of commands issued.
|
||||
self.cmd_total = 0
|
||||
# The time when the user connected.
|
||||
self.conn_time = time.time()
|
||||
#self.channels_subscribed = {}
|
||||
|
||||
def disconnectClient(self):
|
||||
"""
|
||||
Manually disconnect the client.
|
||||
"""
|
||||
self.transport.loseConnection()
|
||||
|
||||
def connectionLost(self, reason):
|
||||
"""
|
||||
Execute this when a client abruplty loses their connection.
|
||||
"""
|
||||
logger.log_infomsg('Disconnected: %s' % self)
|
||||
self.cemit_info('Disconnected: %s.' % self)
|
||||
self.handle_close()
|
||||
|
||||
def lineReceived(self, raw_string):
|
||||
"""
|
||||
Communication Player -> Evennia
|
||||
Any line return indicates a command for the purpose of the MUD.
|
||||
So we take the user input and pass it to the Player and their currently
|
||||
connected character.
|
||||
"""
|
||||
try:
|
||||
raw_string = utils.to_unicode(raw_string)
|
||||
except Exception, e:
|
||||
self.sendLine(str(e))
|
||||
return
|
||||
self.execute_cmd(raw_string)
|
||||
|
||||
def msg(self, message, markup=True):
|
||||
"""
|
||||
Communication Evennia -> Player
|
||||
Sends a message to the session.
|
||||
|
||||
markup - determines if formatting markup should be
|
||||
parsed or not. Currently this means ANSI
|
||||
colors, but could also be html tags for
|
||||
web connections etc.
|
||||
"""
|
||||
try:
|
||||
message = utils.to_str(message)
|
||||
except Exception, e:
|
||||
self.sendLine(str(e))
|
||||
return
|
||||
if markup:
|
||||
message = ansi.parse_ansi(message)
|
||||
else:
|
||||
message = ansi.clean_ansi(message)
|
||||
self.sendLine(message)
|
||||
|
||||
def get_character(self):
|
||||
"""
|
||||
Returns the in-game character associated with a session.
|
||||
This returns the typeclass of the object.
|
||||
"""
|
||||
if self.logged_in:
|
||||
character = ObjectDB.objects.get_object_with_user(self.uid)
|
||||
if not character:
|
||||
string = "No character match for session uid: %s" % self.uid
|
||||
logger.log_errmsg(string)
|
||||
return None
|
||||
return character[0]
|
||||
return None
|
||||
|
||||
def execute_cmd(self, raw_string):
|
||||
"""
|
||||
Sends a command to this session's
|
||||
character for processing.
|
||||
|
||||
'idle' is a special command that is
|
||||
interrupted already here. It doesn't do
|
||||
anything except silently updates the
|
||||
last-active timer to avoid getting kicked
|
||||
off for idleness.
|
||||
"""
|
||||
# handle the 'idle' command
|
||||
if str(raw_string).strip() == 'idle':
|
||||
self.update_counters(idle=True)
|
||||
return
|
||||
|
||||
# all other inputs, including empty inputs
|
||||
character = self.get_character()
|
||||
if character:
|
||||
# normal operation.
|
||||
character.execute_cmd(raw_string)
|
||||
else:
|
||||
# we are not logged in yet
|
||||
cmdhandler.cmdhandler(self, raw_string, unloggedin=True)
|
||||
# update our command counters and idle times.
|
||||
self.update_counters()
|
||||
|
||||
def update_counters(self, idle=False):
|
||||
"""
|
||||
Hit this when the user enters a command in order to update idle timers
|
||||
and command counters. If silently is True, the public-facing idle time
|
||||
is not updated.
|
||||
"""
|
||||
# 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 handle_close(self):
|
||||
"""
|
||||
Break the connection and do some accounting.
|
||||
"""
|
||||
character = self.get_character()
|
||||
if character:
|
||||
#call hook functions
|
||||
character.at_disconnect()
|
||||
character.player.at_disconnect()
|
||||
uaccount = character.player.user
|
||||
uaccount.last_login = datetime.now()
|
||||
uaccount.save()
|
||||
self.disconnectClient()
|
||||
self.logged_in = False
|
||||
sessionhandler.remove_session(self)
|
||||
|
||||
def game_connect_screen(self):
|
||||
"""
|
||||
Show the banner screen. Grab from the 'connect_screen'
|
||||
config directive. If more than one connect screen is
|
||||
defined in the ConnectScreen attribute, it will be
|
||||
random which screen is used.
|
||||
"""
|
||||
screen = ConnectScreen.objects.get_random_connect_screen()
|
||||
string = ansi.parse_ansi(screen.text)
|
||||
self.msg(string)
|
||||
|
||||
def login(self, player):
|
||||
"""
|
||||
After the user has authenticated, this actually
|
||||
logs them in. At this point the session has
|
||||
a User account tied to it. User is an django
|
||||
object that handles stuff like permissions and
|
||||
access, it has no visible precense in the game.
|
||||
This User object is in turn tied to a game
|
||||
Object, which represents whatever existence
|
||||
the player has in the game world. This is the
|
||||
'character' referred to in this module.
|
||||
"""
|
||||
# set the session properties
|
||||
|
||||
user = player.user
|
||||
self.uid = user.id
|
||||
self.name = user.username
|
||||
self.logged_in = True
|
||||
self.conn_time = time.time()
|
||||
|
||||
if not settings.ALLOW_MULTISESSION:
|
||||
# disconnect previous sessions.
|
||||
sessionhandler.disconnect_duplicate_session(self)
|
||||
|
||||
# start (persistent) scripts on this object
|
||||
reloads.reload_scripts(obj=self.get_character(), init_mode=True)
|
||||
|
||||
logger.log_infomsg("Logged in: %s" % self)
|
||||
self.cemit_info('Logged in: %s' % self)
|
||||
|
||||
# Update their account's last login time.
|
||||
user.last_login = datetime.now()
|
||||
user.save()
|
||||
|
||||
def cemit_info(self, message):
|
||||
"""
|
||||
Channel emits info to the appropriate info channel. By default, this
|
||||
is MUDConnections.
|
||||
"""
|
||||
try:
|
||||
cchan = settings.CHANNEL_CONNECTINFO
|
||||
cchan = Channel.objects.get_channel(cchan[0])
|
||||
cchan.msg("[%s]: %s" % (cchan.key, message))
|
||||
except Exception:
|
||||
logger.log_infomsg(message)
|
||||
|
||||
|
||||
137
src/server/sessionhandler.py
Normal file
137
src/server/sessionhandler.py
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
"""
|
||||
Sessionhandler, stores and handles
|
||||
a list of all player connections (sessions).
|
||||
"""
|
||||
import time
|
||||
from django.contrib.auth.models import User
|
||||
from src.config.models import ConfigValue
|
||||
from src.utils import logger
|
||||
|
||||
# Our list of connected sessions.
|
||||
SESSIONS = []
|
||||
|
||||
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]
|
||||
|
||||
def disconnect_all_sessions():
|
||||
"""
|
||||
Cleanly disconnect all of the connected sessions.
|
||||
"""
|
||||
for sess in get_sessions():
|
||||
sess.handle_close()
|
||||
|
||||
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
|
||||
|
||||
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 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 announce_all(message):
|
||||
"""
|
||||
Announces something to all connected players.
|
||||
"""
|
||||
for session in get_sessions():
|
||||
session.msg('%s' % message)
|
||||
Loading…
Add table
Add a link
Reference in a new issue