mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Introduced a trottling mechanism in the portalsessionhandler, limiting the number of new connections per second. Also fixed a bug in deleting a puppeted object.
This commit is contained in:
parent
b015f4802a
commit
9ac3296b04
5 changed files with 64 additions and 22 deletions
|
|
@ -57,6 +57,7 @@ class SessidHandler(object):
|
|||
def get(self):
|
||||
"Returns a list of one or more session ids"
|
||||
return self._cache
|
||||
all = get # alias
|
||||
|
||||
def add(self, sessid):
|
||||
"Add sessid to handler"
|
||||
|
|
@ -812,7 +813,8 @@ class DefaultObject(ObjectDB):
|
|||
# no need to disconnect, Player just jumps to OOC mode.
|
||||
# sever the connection (important!)
|
||||
if self.player:
|
||||
self.player.character = None
|
||||
for sessid in self.sessid.all():
|
||||
self.player.unpuppet_object(sessid)
|
||||
self.player = None
|
||||
|
||||
for script in _ScriptDB.objects.get_all_scripts_on_obj(self):
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
Sessionhandler for portal sessions
|
||||
"""
|
||||
import time
|
||||
from twisted.internet import reactor
|
||||
from evennia.server.sessionhandler import SessionHandler, PCONN, PDISCONN, PCONNSYNC
|
||||
|
||||
_CONNECTION_RATE = 5.0
|
||||
_MIN_TIME_BETWEEN_CONNECTS = 1.0 / _CONNECTION_RATE
|
||||
_MOD_IMPORT = None
|
||||
|
||||
#------------------------------------------------------------
|
||||
|
|
@ -30,6 +33,7 @@ class PortalSessionHandler(SessionHandler):
|
|||
self.latest_sessid = 0
|
||||
self.uptime = time.time()
|
||||
self.connection_time = 0
|
||||
self.time_last_connect = time.time()
|
||||
|
||||
def at_server_connection(self):
|
||||
"""
|
||||
|
|
@ -43,25 +47,56 @@ class PortalSessionHandler(SessionHandler):
|
|||
"""
|
||||
Called by protocol at first connect. This adds a not-yet
|
||||
authenticated session using an ever-increasing counter for sessid.
|
||||
|
||||
We implement a throttling mechanism here to limit the speed at which
|
||||
new connections are accepted - this is both a stop against DoS attacks
|
||||
as well as helps using the Dummyrunner tester with a large number of
|
||||
connector dummies.
|
||||
|
||||
"""
|
||||
self.latest_sessid += 1
|
||||
sessid = self.latest_sessid
|
||||
session.sessid = sessid
|
||||
sessdata = session.get_sync_data()
|
||||
self.sessions[sessid] = session
|
||||
session.server_connected = False
|
||||
|
||||
if not session.sessid:
|
||||
# only assign if we were not delayed
|
||||
self.latest_sessid += 1
|
||||
session.sessid = self.latest_sessid
|
||||
|
||||
now = time.time()
|
||||
current_rate = 1.0 / (now - self.time_last_connect)
|
||||
|
||||
if current_rate > _CONNECTION_RATE:
|
||||
# we have too many connections per second. Delay.
|
||||
#print " delaying connecting", session.sessid
|
||||
reactor.callLater(_MIN_TIME_BETWEEN_CONNECTS, self.connect, session)
|
||||
return
|
||||
|
||||
if not self.portal.amp_protocol:
|
||||
# if amp is not yet ready (usually because the server is
|
||||
# booting up), try again a little later
|
||||
reactor.CallLater(0.5, self.connect, session)
|
||||
return
|
||||
|
||||
# sync with server-side
|
||||
if self.portal.amp_protocol: # this is a timing issue
|
||||
self.portal.amp_protocol.call_remote_ServerAdmin(sessid,
|
||||
|
||||
self.time_last_connect = now
|
||||
sessdata = session.get_sync_data()
|
||||
self.sessions[session.sessid] = session
|
||||
session.server_connected = True
|
||||
#print "connecting", session.sessid, " number:", len(self.sessions)
|
||||
self.portal.amp_protocol.call_remote_ServerAdmin(session.sessid,
|
||||
operation=PCONN,
|
||||
data=sessdata)
|
||||
|
||||
def sync(self, session):
|
||||
"""
|
||||
Called by the protocol of an already connected session. This
|
||||
can be used to sync the session info in a delayed manner,
|
||||
such as when negotiation and handshakes are delayed.
|
||||
"""
|
||||
if session.sessid:
|
||||
# only use if session already has sessid (i.e. has already connected)
|
||||
if session.sessid and session.server_connected:
|
||||
# only use if session already has sessid and has already connected
|
||||
# once to the server - if so we must re-sync woth the server, otherwise
|
||||
# we skip this step.
|
||||
sessdata = session.get_sync_data()
|
||||
if self.portal.amp_protocol:
|
||||
# we only send sessdata that should not have changed
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ ERROR_NO_MIXIN = \
|
|||
reason, you can change their values manually after the import, or
|
||||
add DUMMYRUNNER_MIXIN=True to your settings file to avoid this
|
||||
error completely.
|
||||
|
||||
Warning: Don't run dummyrunner on a production database! It will
|
||||
create a lot of spammy objects and player accounts!
|
||||
"""
|
||||
|
||||
|
||||
|
|
@ -200,6 +203,7 @@ class DummyClient(telnet.StatefulTelnetProtocol):
|
|||
self.exits = [] # exit names created
|
||||
self.objs = [] # obj names created
|
||||
|
||||
self._connected = False
|
||||
self._loggedin = False
|
||||
self._logging_out = False
|
||||
self._report = ""
|
||||
|
|
@ -210,16 +214,17 @@ class DummyClient(telnet.StatefulTelnetProtocol):
|
|||
|
||||
reactor.addSystemEventTrigger('before', 'shutdown', self.logout)
|
||||
|
||||
# start client tick
|
||||
d = LoopingCall(self.step)
|
||||
# dissipate exact step by up to +/- 0.5 second
|
||||
timestep = TIMESTEP + (-0.5 + (random.random()*1.0))
|
||||
d.start(timestep, now=True).addErrback(self.error)
|
||||
|
||||
|
||||
def dataReceived(self, data):
|
||||
"Echo incoming data to stdout"
|
||||
pass
|
||||
"Wait to start stepping until the server actually responds"
|
||||
if not self._connected and not data.startswith(chr(255)):
|
||||
# wait until we actually get text back (not just telnet
|
||||
# negotiation)
|
||||
self._connected = True
|
||||
# start client tick
|
||||
d = LoopingCall(self.step)
|
||||
# dissipate exact step by up to +/- 0.5 second
|
||||
timestep = TIMESTEP + (-0.5 + (random.random()*1.0))
|
||||
d.start(timestep, now=True).addErrback(self.error)
|
||||
|
||||
def connectionLost(self, reason):
|
||||
"loosing the connection"
|
||||
|
|
|
|||
|
|
@ -58,12 +58,12 @@ TIMESTEP = 2
|
|||
|
||||
# Chance of a dummy actually performing an action on a given tick.
|
||||
# This spreads out usage randomly, like it would be in reality.
|
||||
CHANCE_OF_ACTION = 0.20
|
||||
CHANCE_OF_ACTION = 0.5
|
||||
|
||||
# Chance of a currently unlogged-in dummy performing its login
|
||||
# action every tick. This emulates not all players logging in
|
||||
# at exactly the same time.
|
||||
CHANCE_OF_LOGIN = 0.10
|
||||
CHANCE_OF_LOGIN = 1.0#0.5
|
||||
|
||||
# Which telnet port to connect to. If set to None, uses the first
|
||||
# default telnet port of the running server.
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ class ServerSessionHandler(SessionHandler):
|
|||
if session:
|
||||
# since some of the session properties may have had
|
||||
# a chance to change already before the portal gets here
|
||||
# the portal doesn't send all sessiondata here, but only
|
||||
# the portal doesn't send all sessiondata but only
|
||||
# ones which should only be changed from portal (like
|
||||
# protocol_flags etc)
|
||||
session.load_sync_data(portalsessiondata)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue