Merge branch 'master' into develop
This commit is contained in:
commit
4376058ee9
11 changed files with 201 additions and 111 deletions
|
|
@ -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,30 +365,38 @@ 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)
|
||||
reactor.callLater(1.0, self.data_in, session, **kwargs)
|
||||
return
|
||||
|
||||
# scrub data
|
||||
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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -227,26 +227,45 @@ class TelnetOOB(object):
|
|||
GMCP messages will be outgoing on the following
|
||||
form (the non-JSON cmdname at the start is what
|
||||
IRE games use, supposedly, and what clients appear
|
||||
to have adopted):
|
||||
to have adopted). A cmdname without Package will end
|
||||
up in the Core package, while Core package names will
|
||||
be stripped on the Evennia side.
|
||||
|
||||
[cmdname, [], {}] -> cmdname
|
||||
[cmdname, [arg], {}] -> cmdname arg
|
||||
[cmdname, [args],{}] -> cmdname [args]
|
||||
[cmdname, [], {kwargs}] -> cmdname {kwargs}
|
||||
[cmdname, [args, {kwargs}] -> cmdname [[args],{kwargs}]
|
||||
[cmd.name, [], {}] -> Cmd.Name
|
||||
[cmd.name, [arg], {}] -> Cmd.Name arg
|
||||
[cmd.name, [args],{}] -> Cmd.Name [args]
|
||||
[cmd.name, [], {kwargs}] -> Cmd.Name {kwargs}
|
||||
[cmdname, [args, {kwargs}] -> Core.Cmdname [[args],{kwargs}]
|
||||
|
||||
Notes:
|
||||
There are also a few default mappings between evennia outputcmds and
|
||||
GMCP:
|
||||
client_options -> Core.Supports.Get
|
||||
get_inputfuncs -> Core.Commands.Get
|
||||
get_value -> Char.Value.Get
|
||||
repeat -> Char.Repeat.Update
|
||||
monitor -> Char.Monitor.Update
|
||||
|
||||
"""
|
||||
|
||||
if cmdname in EVENNIA_TO_GMCP:
|
||||
gmcp_cmdname = EVENNIA_TO_GMCP[cmdname]
|
||||
elif "_" in cmdname:
|
||||
gmcp_cmdname = ".".join(word.capitalize() for word in cmdname.split("_"))
|
||||
else:
|
||||
gmcp_cmdname = "Core.%s" % cmdname.capitalize()
|
||||
|
||||
if not (args or kwargs):
|
||||
gmcp_string = cmdname
|
||||
gmcp_string = gmcp_cmdname
|
||||
elif args:
|
||||
if len(args) == 1:
|
||||
args = args[0]
|
||||
if kwargs:
|
||||
gmcp_string = "%s %s" % (cmdname, json.dumps([args, kwargs]))
|
||||
gmcp_string = "%s %s" % (gmcp_cmdname, json.dumps([args, kwargs]))
|
||||
else:
|
||||
gmcp_string = "%s %s" % (cmdname, json.dumps(args))
|
||||
gmcp_string = "%s %s" % (gmcp_cmdname, json.dumps(args))
|
||||
else: # only kwargs
|
||||
gmcp_string = "%s %s" % (cmdname, json.dumps(kwargs))
|
||||
gmcp_string = "%s %s" % (gmcp_cmdname, json.dumps(kwargs))
|
||||
|
||||
# print("gmcp string", gmcp_string) # DEBUG
|
||||
return gmcp_string
|
||||
|
|
@ -398,14 +417,9 @@ class TelnetOOB(object):
|
|||
kwargs.pop("options", None)
|
||||
|
||||
if self.MSDP:
|
||||
msdp_cmdname = cmdname
|
||||
encoded_oob = self.encode_msdp(msdp_cmdname, *args, **kwargs)
|
||||
encoded_oob = self.encode_msdp(cmdname, *args, **kwargs)
|
||||
self.protocol._write(IAC + SB + MSDP + encoded_oob + IAC + SE)
|
||||
|
||||
if self.GMCP:
|
||||
if cmdname in EVENNIA_TO_GMCP:
|
||||
gmcp_cmdname = EVENNIA_TO_GMCP[cmdname]
|
||||
else:
|
||||
gmcp_cmdname = "Custom.Cmd"
|
||||
encoded_oob = self.encode_gmcp(gmcp_cmdname, *args, **kwargs)
|
||||
encoded_oob = self.encode_gmcp(cmdname, *args, **kwargs)
|
||||
self.protocol._write(IAC + SB + GMCP + encoded_oob + IAC + SE)
|
||||
|
|
|
|||
|
|
@ -129,6 +129,10 @@ def _server_maintenance():
|
|||
if _MAINTENANCE_COUNT % 3700 == 0:
|
||||
# validate channels off-sync with scripts
|
||||
evennia.CHANNEL_HANDLER.update()
|
||||
if _MAINTENANCE_COUNT % (3600 * 7) == 0:
|
||||
# drop database connection every 7 hrs to avoid default timeouts on MySQL
|
||||
# (see https://github.com/evennia/evennia/issues/1376)
|
||||
connection.close()
|
||||
|
||||
# handle idle timeouts
|
||||
if _IDLE_TIMEOUT > 0:
|
||||
|
|
@ -139,11 +143,6 @@ def _server_maintenance():
|
|||
session.account.access(session.account, "noidletimeout", 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
|
||||
# if _MAINTENANCE_COUNT % 18000 == 0:
|
||||
# connection.close()
|
||||
maintenance_task = LoopingCall(_server_maintenance)
|
||||
maintenance_task.start(60, now=True) # call every minute
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ class ServerSession(Session):
|
|||
if not _ObjectDB:
|
||||
from evennia.objects.models import ObjectDB as _ObjectDB
|
||||
|
||||
super(ServerSession, self).at_sync()
|
||||
if not self.logged_in:
|
||||
# assign the unloggedin-command set.
|
||||
self.cmdset_storage = settings.CMDSET_UNLOGGEDIN
|
||||
|
|
|
|||
|
|
@ -132,7 +132,8 @@ class Session(object):
|
|||
on uid etc).
|
||||
|
||||
"""
|
||||
self.protocol_flags.update(self.account.attributs.get("_saved_protocol_flags"), {})
|
||||
if self.account:
|
||||
self.protocol_flags.update(self.account.attributes.get("_saved_protocol_flags", {}))
|
||||
|
||||
# access hooks
|
||||
|
||||
|
|
|
|||
|
|
@ -614,10 +614,14 @@ class ServerSessionHandler(SessionHandler):
|
|||
|
||||
"""
|
||||
uid = curr_session.uid
|
||||
# we can't compare sessions directly since this will compare addresses and
|
||||
# mean connecting from the same host would not catch duplicates
|
||||
sid = id(curr_session)
|
||||
doublet_sessions = [sess for sess in self.values()
|
||||
if sess.logged_in and
|
||||
sess.uid == uid and
|
||||
sess != curr_session]
|
||||
id(sess) != sid]
|
||||
|
||||
for session in doublet_sessions:
|
||||
self.disconnect(session, reason)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue