diff --git a/src/players/manager.py b/src/players/manager.py index 0e022af56d..26bbb6f340 100644 --- a/src/players/manager.py +++ b/src/players/manager.py @@ -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 - 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 diff --git a/src/players/migrations/0011_addin_is_connected_field.py b/src/players/migrations/0011_addin_is_connected_field.py new file mode 100644 index 0000000000..4489d89ec9 --- /dev/null +++ b/src/players/migrations/0011_addin_is_connected_field.py @@ -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'] \ No newline at end of file diff --git a/src/players/models.py b/src/players/models.py index dd51c67d20..ed33e21ba2 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -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" diff --git a/src/server/server.py b/src/server/server.py index 4bb18d2989..84c325f608 100644 --- a/src/server/server.py +++ b/src/server/server.py @@ -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()] diff --git a/src/server/serversession.py b/src/server/serversession.py index 73e97bc256..95b0b3f76c 100644 --- a/src/server/serversession.py +++ b/src/server/serversession.py @@ -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): """ diff --git a/src/server/sessionhandler.py b/src/server/sessionhandler.py index 562f977e32..e236f24ef7 100644 --- a/src/server/sessionhandler.py +++ b/src/server/sessionhandler.py @@ -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 diff --git a/src/settings_default.py b/src/settings_default.py index f65b803d0d..571b2b518a 100644 --- a/src/settings_default.py +++ b/src/settings_default.py @@ -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. diff --git a/src/web/media/css/prosimii-screen.css b/src/web/media/css/prosimii-screen.css index 67b1eb9b11..f4a2712d59 100644 --- a/src/web/media/css/prosimii-screen.css +++ b/src/web/media/css/prosimii-screen.css @@ -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

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; -} \ No newline at end of file +} diff --git a/src/web/templates/prosimii/index.html b/src/web/templates/prosimii/index.html index 844b3f4a45..c4b9d377bc 100644 --- a/src/web/templates/prosimii/index.html +++ b/src/web/templates/prosimii/index.html @@ -8,28 +8,30 @@ {% block content %}

-
+ +

Welcome!

Welcome to your new installation of Evennia, 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 admin interface 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 - online client!

+ online client!

{% endif %} For more info, take your time to peruse our extensive online documentation.

Should you have any questions, concerns, bug reports, or - if you want to help out, don't hesitate to come join the - Evennia community 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 + mailing list or to come say hi in the developer chatroom. If you find bugs, please report them to our Issue tracker.

-
+ + +

-
+

+
@@ -48,8 +50,7 @@

Players

- There are currently {{num_players_connected}} connected, - and a total of {{num_players_registered}} registered. Of these, {{num_players_registered_recent}} were created this week, and {{num_players_connected_recent}} have connected within the last seven days. + There's currently {{num_players_connected}} connected out of a total of {{num_players_registered}} players registered. Of these, {{num_players_registered_recent}} were created this week, and {{num_players_connected_recent}} have connected within the last seven days.

@@ -66,15 +67,16 @@

Database Stats

    -
  • {{num_players_registered}} players
  • -
  • {{num_rooms}} rooms ({{num_exits}} exits)
  • -
  • {{num_objects}} objects total
  • +
  • {{num_players_registered}} players (+ {{num_characters}} characters)
  • +
  • {{num_rooms}} rooms (+ {{num_exits}} exits)
  • +
  • {{num_others}} other objects
  • +

Evennia

-

Evennia is MUD server built in +

Evennia is an open-source MUD server built in Python, on top of the Twisted and Django frameworks. This diff --git a/src/web/website/views.py b/src/web/website/views.py index 42b3f0644b..a0af399ddc 100644 --- a/src/web/website/views.py +++ b/src/web/website/views.py @@ -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)