diff --git a/evennia/accounts/accounts.py b/evennia/accounts/accounts.py index 7e896e1c1d..a12cb61128 100644 --- a/evennia/accounts/accounts.py +++ b/evennia/accounts/accounts.py @@ -190,7 +190,7 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)): # session-related methods - def disconnect_session_from_account(self, session): + def disconnect_session_from_account(self, session, reason=None): """ Access method for disconnecting a given session from the account (connection happens automatically in the @@ -198,12 +198,13 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)): Args: session (Session): Session to disconnect. + reason (str, optional): Eventual reason for the disconnect. """ global _SESSIONS if not _SESSIONS: from evennia.server.sessionhandler import SESSIONS as _SESSIONS - _SESSIONS.disconnect(session) + _SESSIONS.disconnect(session, reason) # puppeting operations @@ -789,8 +790,8 @@ class DefaultAccount(with_metaclass(TypeclassBase, AccountDB)): """ - reason = reason and "(%s)" % reason or "" - self._send_to_connect_channel("|R%s disconnected %s|n" % (self.key, reason)) + reason = " (%s)" % reason if reason else "" + self._send_to_connect_channel("|R%s disconnected%s|n" % (self.key, reason)) def at_post_disconnect(self, **kwargs): """ diff --git a/evennia/commands/default/account.py b/evennia/commands/default/account.py index ced83c6e2f..912a2a0a2b 100644 --- a/evennia/commands/default/account.py +++ b/evennia/commands/default/account.py @@ -657,10 +657,12 @@ class CmdQuit(COMMAND_DEFAULT_CLASS): if 'all' in self.switches: account.msg("|RQuitting|n all sessions. Hope to see you soon again.", session=self.session) + reason = "quit/all" for session in account.sessions.all(): - account.disconnect_session_from_account(session) + account.disconnect_session_from_account(session, reason) else: nsess = len(account.sessions.all()) + reason = "quit" if nsess == 2: account.msg("|RQuitting|n. One session is still connected.", session=self.session) elif nsess > 2: @@ -668,7 +670,7 @@ class CmdQuit(COMMAND_DEFAULT_CLASS): else: # we are quitting the last available session account.msg("|RQuitting|n. Hope to see you again, soon.", session=self.session) - account.disconnect_session_from_account(self.session) + account.disconnect_session_from_account(self.session, reason) class CmdColorTest(COMMAND_DEFAULT_CLASS): diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index 582ab1f90f..06da867ced 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -1735,9 +1735,9 @@ class CmdLock(ObjManipCommand): assign a lock definition to an object Usage: - @lock [ = ] + @lock [ = ] or - @lock[/switch] / + @lock[/switch] / Switch: del - delete given access type @@ -1779,16 +1779,19 @@ class CmdLock(ObjManipCommand): if '/' in self.lhs: # call on the form @lock obj/access_type objname, access_type = [p.strip() for p in self.lhs.split('/', 1)] - obj = caller.search(objname) - if not obj: + if objname.startswith("*"): + obj = caller.search_account(objname.lstrip('*')) + if not obj: + obj = caller.search(objname) + if not obj: + return + if not (obj.access(caller, 'control') or obj.access(caller, "edit")): + caller.msg("You are not allowed to do that.") return lockdef = obj.locks.get(access_type) string = "" if lockdef: if 'del' in self.switches: - if not (obj.access(caller, 'control') or obj.access(caller, "edit")): - caller.msg("You are not allowed to do that.") - return obj.locks.delete(access_type) string = "deleted lock %s" % lockdef else: @@ -1808,9 +1811,12 @@ class CmdLock(ObjManipCommand): return objname, lockdef = self.lhs, self.rhs - obj = caller.search(objname) - if not obj: - return + if objname.startswith("*"): + obj = caller.search_account(objname.lstrip('*')) + if not obj: + obj = caller.search(objname) + if not obj: + return if not (obj.access(caller, 'control') or obj.access(caller, "edit")): caller.msg("You are not allowed to do that.") return diff --git a/evennia/server/portal/portal.py b/evennia/server/portal/portal.py index f8db12b9e2..ccfb7b02eb 100644 --- a/evennia/server/portal/portal.py +++ b/evennia/server/portal/portal.py @@ -73,33 +73,6 @@ AMP_INTERFACE = settings.AMP_INTERFACE AMP_ENABLED = AMP_HOST and AMP_PORT and AMP_INTERFACE -# Maintenance function - this is called repeatedly by the portal. - -_IDLE_TIMEOUT = settings.IDLE_TIMEOUT - - -def _portal_maintenance(): - """ - The maintenance function handles repeated checks and updates that - the server needs to do. It is called every minute. - - """ - # check for idle sessions - now = time.time() - - reason = "Idle timeout exceeded, disconnecting." - for session in [sess for sess in PORTAL_SESSIONS.values() - if (now - sess.cmd_last) > _IDLE_TIMEOUT]: - session.disconnect(reason=reason) - PORTAL_SESSIONS.disconnect(session) - - -if _IDLE_TIMEOUT > 0: - # only start the maintenance task if we care about idling. - _maintenance_task = LoopingCall(_portal_maintenance) - _maintenance_task.start(60) # called every minute - - # ------------------------------------------------------------- # Portal Service object # ------------------------------------------------------------- diff --git a/evennia/server/portal/portalsessionhandler.py b/evennia/server/portal/portalsessionhandler.py index 7da1dd1ec1..171216f8d8 100644 --- a/evennia/server/portal/portalsessionhandler.py +++ b/evennia/server/portal/portalsessionhandler.py @@ -268,7 +268,6 @@ class PortalSessionHandler(SessionHandler): data (dict): The session sync data. """ - print("server_logged_in: %s: %s" % (session, data)) session.load_sync_data(data) session.at_login() diff --git a/evennia/server/portal/ssh.py b/evennia/server/portal/ssh.py index 7f82faf129..e993e74bc3 100644 --- a/evennia/server/portal/ssh.py +++ b/evennia/server/portal/ssh.py @@ -213,6 +213,12 @@ class SshProtocol(Manhole, session.Session): # session-general method hooks + def at_login(self): + """ + Called when this session gets authenticated by the server. + """ + pass + def disconnect(self, reason="Connection closed. Goodbye for now."): """ Disconnect from server. diff --git a/evennia/server/portal/telnet.py b/evennia/server/portal/telnet.py index 39c236e9e9..4112d85e2a 100644 --- a/evennia/server/portal/telnet.py +++ b/evennia/server/portal/telnet.py @@ -116,6 +116,12 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session): # do the sync self.sessionhandler.sync(self) + def at_login(self): + """ + Called when this session gets authenticated by the server. + """ + pass + def enableRemote(self, option): """ This sets up the remote-activated options we allow for this protocol. diff --git a/evennia/server/portal/webclient_ajax.py b/evennia/server/portal/webclient_ajax.py index 993e0a011f..4ec78e3eb7 100644 --- a/evennia/server/portal/webclient_ajax.py +++ b/evennia/server/portal/webclient_ajax.py @@ -107,6 +107,12 @@ class WebClient(resource.Resource): self.keep_alive.stop() self.keep_alive = None + def at_login(self): + """ + Called when this session gets authenticated by the server. + """ + pass + def lineSend(self, csessid, data): """ This adds the data to the buffer and/or sends it to the client diff --git a/evennia/server/server.py b/evennia/server/server.py index 65afc4d873..18b1220c43 100644 --- a/evennia/server/server.py +++ b/evennia/server/server.py @@ -16,7 +16,7 @@ import os from twisted.web import static from twisted.application import internet, service from twisted.internet import reactor, defer -from twisted.internet.task import LoopingCall, deferLater +from twisted.internet.task import LoopingCall import django django.setup() @@ -36,6 +36,8 @@ from evennia.utils.utils import get_evennia_version, mod_import, make_iter from evennia.comms import channelhandler from evennia.server.sessionhandler import SESSIONS +from django.utils.translation import ugettext as _ + _SA = object.__setattr__ SERVER_PIDFILE = "" @@ -89,11 +91,13 @@ _FLUSH_CACHE = None _IDMAPPER_CACHE_MAXSIZE = settings.IDMAPPER_CACHE_MAXSIZE _GAMETIME_MODULE = None +_IDLE_TIMEOUT = settings.IDLE_TIMEOUT + def _server_maintenance(): """ This maintenance function handles repeated checks and updates that - the server needs to do. It is called every 5 minutes. + the server needs to do. It is called every minute. """ global EVENNIA, _MAINTENANCE_COUNT, _FLUSH_CACHE, _GAMETIME_MODULE if not _FLUSH_CACHE: @@ -124,6 +128,14 @@ def _server_maintenance(): # validate channels off-sync with scripts evennia.CHANNEL_HANDLER.update() + # handle idle timeouts + reason = _("idle timeout exceeded") + for session in (sess for sess in SESSIONS.values() + if (now - sess.cmd_last) > _IDLE_TIMEOUT): + if not session.account or not \ + session.account.access(session.account, "no_idle_disconnect", default=False): + SESSIONS.disconnect(session, reason=reason) + # Commenting this out, it is probably not needed # with CONN_MAX_AGE set. Keeping it as a reminder # if database-gone-away errors appears again /Griatch diff --git a/evennia/server/serversession.py b/evennia/server/serversession.py index e84f5cf7bd..47591383cd 100644 --- a/evennia/server/serversession.py +++ b/evennia/server/serversession.py @@ -9,7 +9,6 @@ are stored on the Portal side) from builtins import object import weakref -import importlib import time from django.utils import timezone from django.conf import settings @@ -232,7 +231,7 @@ class ServerSession(Session): # add the session-level cmdset self.cmdset = CmdSetHandler(self, True) - def at_disconnect(self): + def at_disconnect(self, reason=None): """ Hook called by sessionhandler when disconnecting this session. @@ -245,7 +244,7 @@ class ServerSession(Session): uaccount.last_login = timezone.now() uaccount.save() # calling account hook - account.at_disconnect() + account.at_disconnect(reason) self.logged_in = False if not self.sessionhandler.sessions_from_account(account): # no more sessions connected to this account diff --git a/evennia/server/sessionhandler.py b/evennia/server/sessionhandler.py index 33b04d1386..825cf6da30 100644 --- a/evennia/server/sessionhandler.py +++ b/evennia/server/sessionhandler.py @@ -500,11 +500,12 @@ class ServerSessionHandler(SessionHandler): if hasattr(session, "account") and session.account: # only log accounts logging off nsess = len(self.sessions_from_account(session.account)) - 1 - string = "Logged out: {account} {address} ({nsessions} sessions(s) remaining)" - string = string.format(account=session.account, address=session.address, nsessions=nsess) + sreason = " ({})".format(reason) if reason else "" + string = "Logged out: {account} {address} ({nsessions} sessions(s) remaining){reason}" + string = string.format(reason=sreason, account=session.account, address=session.address, nsessions=nsess) session.log(string) - session.at_disconnect() + session.at_disconnect(reason) sessid = session.sessid if sessid in self and not hasattr(self, "_disconnect_all"): del self[sessid]