diff --git a/evennia/scripts/tickerhandler.py b/evennia/scripts/tickerhandler.py index 14e7c6a3a1..942d1cbdf1 100644 --- a/evennia/scripts/tickerhandler.py +++ b/evennia/scripts/tickerhandler.py @@ -73,7 +73,7 @@ from evennia.scripts.scripts import ExtendedLoopingCall from evennia.server.models import ServerConfig from evennia.utils.logger import log_trace, log_err from evennia.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj -from evennia.utils import variable_from_module +from evennia.utils import variable_from_module, inherits_from _GA = object.__getattribute__ _SA = object.__setattr__ @@ -319,7 +319,6 @@ class TickerHandler(object): callback (function or method): This is either a stand-alone function or class method on a typeclassed entitye (that is, an entity that can be saved to the database). - Returns: ret (tuple): This is a tuple of the form `(obj, path, callfunc)`, where `obj` is the database object the callback is defined on @@ -327,6 +326,8 @@ class TickerHandler(object): the python-path to the stand-alone function (`None` if a method). The `callfunc` is either the name of the method to call or the callable function object itself. + Raises: + TypeError: If the callback is of an unsupported type. """ outobj, outpath, outcallfunc = None, None, None @@ -337,8 +338,15 @@ class TickerHandler(object): elif inspect.isfunction(callback): outpath = "%s.%s" % (callback.__module__, callback.__name__) outcallfunc = callback + else: + raise TypeError(f"{callback} is not a method or function.") else: - raise TypeError("%s is not a callable function or method." % callback) + raise TypeError(f"{callback} is not a callable function or method.") + + if outobj and not inherits_from(outobj, "evennia.typeclasses.models.TypedObject"): + raise TypeError(f"{callback} is a method on a normal object - it must " + "be either a method on a typeclass, or a stand-alone function.") + return outobj, outpath, outcallfunc def _store_key(self, obj, path, interval, callfunc, idstring="", persistent=True): @@ -504,12 +512,6 @@ class TickerHandler(object): when wanting to modify/remove the ticker later. """ - if isinstance(callback, int): - raise RuntimeError( - "TICKER_HANDLER.add has changed: " - "the interval is now the first argument, callback the second." - ) - obj, path, callfunc = self._get_callback(callback) store_key = self._store_key(obj, path, interval, callfunc, idstring, persistent) kwargs["_obj"] = obj diff --git a/evennia/server/portal/telnet.py b/evennia/server/portal/telnet.py index a6a8263dda..9043620463 100644 --- a/evennia/server/portal/telnet.py +++ b/evennia/server/portal/telnet.py @@ -83,7 +83,11 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, _BASE_SESSION_CLASS): of incoming data. """ # print(f"telnet dataReceived: {data}") - super().dataReceived(data) + try: + super().dataReceived(data) + except ValueError as err: + from evennia.utils import logger + logger.log_err(f"Malformed telnet input: {err}") def connectionMade(self): """ diff --git a/evennia/server/portal/webclient.py b/evennia/server/portal/webclient.py index d1097e17d8..d12d93b3e7 100644 --- a/evennia/server/portal/webclient.py +++ b/evennia/server/portal/webclient.py @@ -22,6 +22,7 @@ from evennia.utils.utils import mod_import, class_from_module from evennia.utils.ansi import parse_ansi from evennia.utils.text2html import parse_html from autobahn.twisted.websocket import WebSocketServerProtocol +from autobahn.exception import Disconnected _RE_SCREENREADER_REGEX = re.compile( r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE @@ -150,7 +151,7 @@ class WebSocketClient(WebSocketServerProtocol, _BASE_SESSION_CLASS): # in case anyone wants to expose this functionality later. # # sendClose() under autobahn/websocket/interfaces.py - self.sendClose(CLOSE_NORMAL, reason) + ret = self.sendClose(CLOSE_NORMAL, reason) def onClose(self, wasClean, code=None, reason=None): """ @@ -191,7 +192,12 @@ class WebSocketClient(WebSocketServerProtocol, _BASE_SESSION_CLASS): line (str): Text to send. """ - return self.sendMessage(line.encode()) + try: + return self.sendMessage(line.encode()) + except Disconnected: + # this can happen on an unclean close of certain browsers. + # it means this link is actually already closed. + self.disconnect(reason="Browser already closed.") def at_login(self): csession = self.get_client_session() @@ -249,8 +255,6 @@ class WebSocketClient(WebSocketServerProtocol, _BASE_SESSION_CLASS): return else: return - # just to be sure - text = to_str(text) flags = self.protocol_flags diff --git a/evennia/server/webserver.py b/evennia/server/webserver.py index ff7f61baa0..c2b3cad014 100644 --- a/evennia/server/webserver.py +++ b/evennia/server/webserver.py @@ -102,7 +102,8 @@ class EvenniaReverseProxyResource(ReverseProxyResource): """ request.notifyFinish().addErrback( - lambda f: logger.log_trace("%s\nCaught errback in webserver.py:75." % f) + lambda f: 0 + # lambda f: logger.log_trace("%s\nCaught errback in webserver.py" % f) ) return EvenniaReverseProxyResource( self.host, self.port, self.path + "/" + urlquote(path, safe=""), self.reactor @@ -139,9 +140,9 @@ class EvenniaReverseProxyResource(ReverseProxyResource): clientFactory.noisy = False self.reactor.connectTCP(self.host, self.port, clientFactory) # don't trigger traceback if connection is lost before request finish. - request.notifyFinish().addErrback( - lambda f: logger.log_trace("%s\nCaught errback in webserver.py:75." % f) - ) + request.notifyFinish().addErrback(lambda f: 0) + # request.notifyFinish().addErrback( + # lambda f:logger.log_trace("Caught errback in webserver.py: %s" % f) return NOT_DONE_YET @@ -207,6 +208,11 @@ class DjangoWebRoot(resource.Resource): path0 = request.prepath.pop(0) request.postpath.insert(0, path0) + request.notifyFinish().addErrback( + lambda f: 0 + # lambda f: logger.log_trace("%s\nCaught errback in webserver.py:" % f) + ) + deferred = request.notifyFinish() self._pending_requests[deferred] = deferred deferred.addBoth(self._decrement_requests, deferred=deferred)