Run Migrations! Added a is_connected field to Players to be able to more conveniently access online status from out-of-process (resolves issue 251). Also cleaned up and added features to the default website.

This commit is contained in:
Griatch 2012-09-17 19:19:20 +02:00
parent c53a9b5770
commit 21137cc830
10 changed files with 189 additions and 75 deletions

View file

@ -96,7 +96,7 @@ class PlayerManager(TypedObjectManager):
"""
Returns a list of player objects with currently connected users/players.
"""
return [player for player in self.all() if player.sessions]
return self.filter(db_is_connected=True)
@returns_typeclass_list
@returns_player_list
@ -116,6 +116,8 @@ class PlayerManager(TypedObjectManager):
"""
Returns a QuerySet containing the player User accounts that have been
connected within the last <days> days.
days - number of days backwards to check
"""
end_date = datetime.datetime.now()
tdelta = datetime.timedelta(days)
@ -166,7 +168,6 @@ class PlayerManager(TypedObjectManager):
return matches
return self.filter(user__username__iexact=ostring)
def swap_character(self, player, new_character, delete_old_character=False):
"""
This disconnects a player from the current character (if any) and connects

View file

@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'PlayerDB.db_is_connected'
db.add_column('players_playerdb', 'db_is_connected',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)
def backwards(self, orm):
# Deleting field 'PlayerDB.db_is_connected'
db.delete_column('players_playerdb', 'db_is_connected')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerattribute': {
'Meta': {'object_name': 'PlayerAttribute'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']"}),
'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_cmdset_storage': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_is_connected': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_lock_storage': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True', 'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
},
'players.playernick': {
'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'PlayerNick'},
'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']"}),
'db_real': ('django.db.models.fields.TextField', [], {}),
'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
}
}
complete_apps = ['players']

View file

@ -161,7 +161,9 @@ class PlayerDB(TypedObject):
# Use the property 'obj' to access.
db_obj = models.ForeignKey("objects.ObjectDB", null=True, blank=True,
verbose_name="character", help_text='In-game object.')
# store a connected flag here too, not just in sessionhandler.
# This makes it easier to track from various out-of-process locations
db_is_connected = models.BooleanField(default=False, verbose_name="is_connected", help_text="If player is connected to game or not")
# database storage of persistant cmdsets.
db_cmdset_storage = models.CharField('cmdset', max_length=255, null=True,
help_text="optional python path to a cmdset class. If creating a Character, this will default to settings.CMDSET_DEFAULT.")
@ -251,6 +253,21 @@ class PlayerDB(TypedObject):
self.save()
cmdset_storage = property(cmdset_storage_get, cmdset_storage_set, cmdset_storage_del)
#@property
def is_connected_get(self):
"Getter. Allows for value = self.is_connected"
return _get_cache(self, "is_connected")
#@is_connected.setter
def is_connected_set(self, value):
"Setter. Allows for self.is_connected = value"
print "set_is_connected:", self, value
_set_cache(self, "is_connected", value)
#@is_connected.deleter
def is_connected_del(self):
"Deleter. Allows for del is_connected"
_set_cache(self, "is_connected", False)
is_connected = property(is_connected_get, is_connected_set, is_connected_del)
class Meta:
"Define Django meta options"
verbose_name = "Player"

View file

@ -21,6 +21,7 @@ import django
from django.db import connection
from django.conf import settings
from src.players.models import PlayerDB
from src.scripts.models import ScriptDB
from src.server.models import ServerConfig
from src.server import initial_setup
@ -29,6 +30,8 @@ from src.utils.utils import get_evennia_version, mod_import
from src.comms import channelhandler
from src.server.sessionhandler import SESSIONS
_SA = object.__setattr__
if os.name == 'nt':
# For Windows we need to handle pid files manually.
SERVER_PIDFILE = os.path.join(settings.GAME_DIR, 'server.pid')
@ -285,6 +288,7 @@ class Evennia(object):
# don't call disconnect hooks on reset
yield [(o.typeclass, o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
else: # shutdown
yield [_SA(p, "is_connected", False) for p in PlayerDB.get_all_cached_instances()]
yield [(o.typeclass, o.at_disconnect(), o.at_server_shutdown()) for o in ObjectDB.get_all_cached_instances()]
yield [(p.typeclass, p.at_server_shutdown()) for p in PlayerDB.get_all_cached_instances()]

View file

@ -71,6 +71,12 @@ class ServerSession(Session):
player - the connected player
"""
# we have to check this first before uid has been assigned
# this session.
if not self.sessionhandler.sessions_from_player(player):
player.is_connected = True
# actually do the login by assigning session data
self.player = player
@ -134,6 +140,8 @@ class ServerSession(Session):
uaccount.save()
self.logged_in = False
self.sessionhandler.disconnect(self)
if not self.sessionhandler.sessions_from_player(player):
player.is_connected = False
def get_player(self):
"""

View file

@ -16,7 +16,7 @@ import time
from django.conf import settings
from src.commands.cmdhandler import CMD_LOGINSTART
_ServerConfig = None
_PLAYERDB = None
# AMP signals
PCONN = chr(1) # portal session connect
@ -153,8 +153,6 @@ class ServerSessionHandler(SessionHandler):
self.server.amp_protocol.call_remote_PortalAdmin(sessid,
operation=SDISCONN,
data=reason)
self.session_count(-1)
def login(self, session):
"""
@ -168,7 +166,6 @@ class ServerSessionHandler(SessionHandler):
# disconnect previous sessions.
self.disconnect_duplicate_sessions(session)
session.logged_in = True
self.session_count(1)
# sync the portal to this session
sessdata = session.get_sync_data()
self.server.amp_protocol.call_remote_PortalAdmin(session.sessid,
@ -193,7 +190,6 @@ class ServerSessionHandler(SessionHandler):
for session in self.sessions:
del session
self.session_count(0)
# tell portal to disconnect all sessions
self.server.amp_protocol.call_remote_PortalAdmin(0,
operation=SDISCONNALL,
@ -204,14 +200,12 @@ class ServerSessionHandler(SessionHandler):
Disconnects any existing sessions with the same game object.
"""
curr_char = curr_session.get_character()
doublet_sessions = [sess for sess in self.sessions
doublet_sessions = [sess for sess in self.sessions.values()
if sess.logged_in
and sess.get_character() == curr_char
and sess != curr_session]
for session in doublet_sessions:
self.disconnect(session, reason)
self.session_count(-1)
def validate_sessions(self):
"""
@ -224,31 +218,6 @@ class ServerSessionHandler(SessionHandler):
if session.logged_in and IDLE_TIMEOUT > 0
and (tcurr - session.cmd_last) > IDLE_TIMEOUT):
self.disconnect(session, reason=reason)
self.session_count(-1)
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.
"""
global _ServerConfig
if not _ServerConfig:
from src.server.models import ServerConfig as _ServerConfig
if num == None:
# show the current value. This also syncs it.
return int(_ServerConfig.objects.conf('nr_sessions', default=0))
elif num == 0:
# reset value to 0
_ServerConfig.objects.conf('nr_sessions', 0)
else:
# add/remove session count from value
add = int(_ServerConfig.objects.conf('nr_sessions', default=0))
num = max(0, num + add)
_ServerConfig.objects.conf('nr_sessions', str(num))
def player_count(self):
"""
@ -274,7 +243,6 @@ class ServerSessionHandler(SessionHandler):
return self.sessions_from_player(player)
return None
def announce_all(self, message):
"""
Send message to all connected sessions

View file

@ -64,7 +64,7 @@ SSL_INTERFACES = ['0.0.0.0']
# All feedback from the game will be echoed to all sessions.
# If false, only one session is allowed, all other are logged off
# when a new connects.
ALLOW_MULTISESSION = True
ALLOW_MULTISESSION = False
# The path that contains this settings.py file (no trailing slash).
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Path to the src directory containing the bulk of the codebase's code.

View file

@ -1,5 +1,5 @@
/***************************************
* TITLE: Prosimii Screen Stylesheet *
* TITLE: Prosimii Screen Stylesheet *
* URI : prosimii/prosimii-screen.css *
* MODIF: 2004-Apr-28 21:43 +0800 *
***************************************/
@ -312,7 +312,7 @@ dd {
.filler { /* use with an empty <p> element to add padding to the end of a text box */
border: 1px solid white;
}
.noBorderOnLeft {
border-left: none;
}
@ -356,4 +356,4 @@ dd {
#footer a:hover {
text-decoration: none;
}
}

View file

@ -8,28 +8,30 @@
{% block content %}
<div class="rowOfBoxes">
<div class="twoThirds noBorderOnLeft">
<!--div class="twoThirds noBorderOnLeft"-->
<div class="noBorderOnLeft">
<h1>Welcome!</h1>
<p>Welcome to your new installation of <a href="http://evennia.com">Evennia</a>, your friendly
neighborhood next-generation MUD server. You are looking at Evennia's web
neighborhood next-generation MUD development system and server. You are looking at Evennia's web
presence, which can be expanded to a full-fledged site as
needed. Through the <a href="/admin">admin interface</a> you can view and edit the
database without logging into the game.
{% if webclient_enabled %}
database without logging into the game.
{% if webclient_enabled %}
You can also connect to the game directly from your browser using our
<a href='/webclient'>online client</a>!<br></br>
<a href='/webclient'>online client</a>!<br></br>
{% endif %}
For more info, take your time to
peruse our extensive online <a href="http://code.google.com/p/evennia/wiki/Index">documentation</a>.
<p>
Should you have any questions, concerns, bug reports, or
if you want to help out, don't hesitate to come join the
<a href="http://evennia.com/discussions">Evennia community</a> and get
your voice heard!
if you want to help out, don't hesitate to join the Evennia community to make your voice heard! Drop a mail to the
<a href="http://evennia.com/discussions">mailing list</a> or to come say hi in the <a href="http://webchat.freenode.net/?channels=evennia">developer chatroom</a>. If you find bugs, please report them to our <a href="http://code.google.com/p/evennia/issues/list">Issue tracker</a>.
</p>
</div>
<div class="oneThird">
<!-- news app commented out since there are no news in the database originally -->
<!--div class="oneThird">
<h1>Latest News</h1>
{% for entry in news_entries %}
<a href="/news/show/{{entry.id}}" class="newsHeading">{{entry.topic.name}}: {{entry.title}}</a>
@ -39,8 +41,8 @@
<div class="more"><a href="/news/archive">More News &raquo;</a></div>
<p class="filler"><!-- Filler para to extend left vertical line --></p>
</div>
<p class="filler"><!-- Filler para to extend left vertical line--></p>
</div-->
</div>
<div class="rowOfBoxes dividingBorderAbove">
@ -48,8 +50,7 @@
<div class="quarter noBorderOnLeft">
<h1>Players</h1>
<p>
There are currently <strong>{{num_players_connected}}</strong> connected,
and a total of <strong>{{num_players_registered}}</strong> registered. Of these, <strong>{{num_players_registered_recent}}</strong> were created this week, and <strong>{{num_players_connected_recent}}</strong> have connected within the last seven days.
There's currently <strong>{{num_players_connected}}</strong> connected out of a total of <strong>{{num_players_registered}}</strong> players registered. Of these, <strong>{{num_players_registered_recent}}</strong> were created this week, and <strong>{{num_players_connected_recent}}</strong> have connected within the last seven days.
</p>
</div>
@ -66,15 +67,16 @@
<div class="quarter">
<h1>Database Stats</h1>
<ul>
<li>{{num_players_registered}} players</li>
<li>{{num_rooms}} rooms ({{num_exits}} exits) </li>
<li>{{num_objects}} objects total</li>
<li>{{num_players_registered}} players (+ {{num_characters}} characters)</li>
<li>{{num_rooms}} rooms (+ {{num_exits}} exits)</li>
<li>{{num_others}} other objects</li>
</ul>
</div>
<div class="quarter">
<h1>Evennia</h1>
<p><a href="http://evennia.com">Evennia</a> is MUD server built in
<p><a href="http://evennia.com">Evennia</a> is an open-source MUD server built in
<a href="http://python.org">Python</a>, on top of the
<a href="http://twistedmatrix.com">Twisted</a> and
<a href="http://djangoproject.com">Django</a> frameworks. This

View file

@ -1,50 +1,59 @@
"""
This file contains the generic, assorted views that don't fall under one of
the other applications. Views are django's way of processing e.g. html
the other applications. Views are django's way of processing e.g. html
templates on the fly.
"""
from django.shortcuts import render_to_response, get_object_or_404
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.contrib.auth.models import User
#from django.contrib.auth.models import User
from django.conf import settings
from src.server.models import ServerConfig
from src.objects.models import ObjectDB
from src.typeclasses.models import TypedObject
#from src.typeclasses.models import TypedObject
from src.players.models import PlayerDB
from src.web.news.models import NewsEntry
_BASE_CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
def page_index(request):
"""
Main root page.
"""
# Some misc. configurable stuff.
# TODO: Move this to either SQL or settings.py based configuration.
fpage_player_limit = 4
fpage_player_limit = 4
fpage_news_entries = 2
# A QuerySet of recent news entries.
news_entries = NewsEntry.objects.all().order_by('-date_posted')[:fpage_news_entries]
# A QuerySet of the most recently connected players.
recent_users = PlayerDB.objects.get_recently_connected_players()[:fpage_player_limit]
nplyrs_conn_recent = len(recent_users) or "none"
nplyrs = PlayerDB.objects.num_total_players() or "none"
nplyrs_reg_recent = len(PlayerDB.objects.get_recently_created_players()) or "none"
nsess = len(PlayerDB.objects.get_connected_players()) or "noone"
exits = ObjectDB.objects.filter(db_destination__isnull=False)
rooms = [room for room in ObjectDB.objects.filter(db_home__isnull=True) if room not in exits]
nobjs = ObjectDB.objects.all().count()
nrooms = ObjectDB.objects.filter(db_location__isnull=True).exclude(db_typeclass_path=_BASE_CHAR_TYPECLASS).count()
nexits = ObjectDB.objects.filter(db_location__isnull=False, db_destination__isnull=False).count()
nchars = ObjectDB.objects.filter(db_typeclass_path=_BASE_CHAR_TYPECLASS).count()
nothers = nobjs - nrooms - nchars - nexits
pagevars = {
"page_title": "Front Page",
"news_entries": news_entries,
"players_connected_recent": recent_users,
"num_players_connected": ServerConfig.objects.conf('nr_sessions'),#len(PlayerDB.objects.get_connected_players()),
"num_players_registered": PlayerDB.objects.num_total_players(),
"num_players_connected_recent": len(PlayerDB.objects.get_recently_connected_players()),
"num_players_registered_recent": len(PlayerDB.objects.get_recently_created_players()),
"num_rooms": len(rooms),
"num_exits": len(exits),
"num_objects" : ObjectDB.objects.all().count()
"num_players_connected": nsess or "noone",
"num_players_registered": nplyrs or "no",
"num_players_connected_recent": nplyrs_conn_recent or "no",
"num_players_registered_recent": nplyrs_reg_recent or "noone",
"num_rooms": nrooms or "none",
"num_exits": nexits or "no",
"num_objects" : nobjs or "none",
"num_characters": nchars or "no",
"num_others": nothers or "no"
}
context_instance = RequestContext(request)