diff --git a/evennia/server/portal/portalsessionhandler.py b/evennia/server/portal/portalsessionhandler.py index d93e54465f..9216eacaee 100644 --- a/evennia/server/portal/portalsessionhandler.py +++ b/evennia/server/portal/portalsessionhandler.py @@ -15,12 +15,15 @@ from evennia.utils.logger import log_trace # module import _MOD_IMPORT = None -# throttles +# global throttles _MAX_CONNECTION_RATE = float(settings.MAX_CONNECTION_RATE) +# per-session throttles _MAX_COMMAND_RATE = float(settings.MAX_COMMAND_RATE) _MAX_CHAR_LIMIT = int(settings.MAX_CHAR_LIMIT) -_MIN_TIME_BETWEEN_CONNECTS = 1.0 / float(settings.MAX_CONNECTION_RATE) +_MIN_TIME_BETWEEN_CONNECTS = 1.0 / float(_MAX_CONNECTION_RATE) +_MIN_TIME_BETWEEN_COMMANDS = 1.0 / float(_MAX_COMMAND_RATE) + _ERROR_COMMAND_OVERFLOW = settings.COMMAND_RATE_WARNING _ERROR_MAX_CHAR = settings.MAX_CHAR_LIMIT_WARNING @@ -58,9 +61,6 @@ class PortalSessionHandler(SessionHandler): self.connection_last = self.uptime self.connection_task = None - self.command_counter = 0 - self.command_counter_reset = self.uptime - self.command_overflow = False def at_server_connection(self): """ @@ -354,8 +354,6 @@ class PortalSessionHandler(SessionHandler): Data is serialized before passed on. """ - # from evennia.server.profiling.timetrace import timetrace # DEBUG - # text = timetrace(text, "portalsessionhandler.data_in") # DEBUG try: text = kwargs['text'] if (_MAX_CHAR_LIMIT > 0) and len(text) > _MAX_CHAR_LIMIT: @@ -367,17 +365,25 @@ class PortalSessionHandler(SessionHandler): pass if session: now = time.time() - if self.command_counter > _MAX_COMMAND_RATE > 0: - # data throttle (anti DoS measure) - delta_time = now - self.command_counter_reset - self.command_counter = 0 - self.command_counter_reset = now - self.command_overflow = delta_time < 1.0 - if self.command_overflow: - reactor.callLater(1.0, self.data_in, None) - if self.command_overflow: + + try: + command_counter_reset = session.command_counter_reset + except AttributeError: + command_counter_reset = session.command_counter_reset = now + session.command_counter = 0 + + # global command-rate limit + if max(0, now - command_counter_reset) > 1.0: + # more than a second since resetting the counter. Refresh. + session.command_counter_reset = now + session.command_counter = 0 + + session.command_counter += 1 + + if session.command_counter * _MIN_TIME_BETWEEN_COMMANDS > 1.0: self.data_out(session, text=[[_ERROR_COMMAND_OVERFLOW], {}]) return + if not self.portal.amp_protocol: # this can happen if someone connects before AMP connection # was established (usually on first start) @@ -388,15 +394,9 @@ class PortalSessionHandler(SessionHandler): kwargs = self.clean_senddata(session, kwargs) # relay data to Server - self.command_counter += 1 session.cmd_last = now self.portal.amp_protocol.send_MsgPortal2Server(session, **kwargs) - else: - # called by the callLater callback - if self.command_overflow: - self.command_overflow = False - reactor.callLater(1.0, self.data_in, None) def data_out(self, session, **kwargs): """