Merge branch 'master' into develop

This commit is contained in:
Griatch 2018-01-27 14:20:13 +01:00
commit 4376058ee9
11 changed files with 201 additions and 111 deletions

View file

@ -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):
"""

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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)