Merge branch 'spikeDrops'

This commit is contained in:
Uriziel 2013-11-07 14:20:54 +01:00
commit a919cfe49a
3 changed files with 131 additions and 93 deletions

View File

@ -224,24 +224,24 @@ class SyncplayClient(object):
madeChangeOnPlayer = self._serverPaused(setBy) madeChangeOnPlayer = self._serverPaused(setBy)
return madeChangeOnPlayer return madeChangeOnPlayer
def _executePlaystateHooks(self, position, paused, doSeek, setBy, latency): def _executePlaystateHooks(self, position, paused, doSeek, setBy, messageAge):
if(self.userlist.hasRoomStateChanged() and not paused): if(self.userlist.hasRoomStateChanged() and not paused):
self._warnings.checkWarnings() self._warnings.checkWarnings()
self.userlist.roomStateConfirmed() self.userlist.roomStateConfirmed()
self._malUpdater.playingHook(position, paused) self._malUpdater.playingHook(position, paused)
def updateGlobalState(self, position, paused, doSeek, setBy, latency): def updateGlobalState(self, position, paused, doSeek, setBy, messageAge):
if(self.__getUserlistOnLogon): if(self.__getUserlistOnLogon):
self.__getUserlistOnLogon = False self.__getUserlistOnLogon = False
self.getUserList() self.getUserList()
madeChangeOnPlayer = False madeChangeOnPlayer = False
if(not paused): if(not paused):
position += latency position += messageAge
if(self._player): if(self._player):
madeChangeOnPlayer = self._changePlayerStateAccordingToGlobalState(position, paused, doSeek, setBy) madeChangeOnPlayer = self._changePlayerStateAccordingToGlobalState(position, paused, doSeek, setBy)
if(madeChangeOnPlayer): if(madeChangeOnPlayer):
self.askPlayer() self.askPlayer()
self._executePlaystateHooks(position, paused, doSeek, setBy, latency) self._executePlaystateHooks(position, paused, doSeek, setBy, messageAge)
def getUserOffset(self): def getUserOffset(self):
return self._userOffset return self._userOffset

View File

@ -1,12 +1,14 @@
#coding:utf8 # coding:utf8
from twisted.protocols.basic import LineReceiver from twisted.protocols.basic import LineReceiver
import json import json
import syncplay import syncplay
from functools import wraps from functools import wraps
import time import time
from syncplay.messages import getMessage from syncplay.messages import getMessage
from syncplay.constants import PING_MOVING_AVERAGE_WEIGHT
class JSONCommandProtocol(LineReceiver):
class JSONCommandProtocol(LineReceiver):
def handleMessages(self, messages): def handleMessages(self, messages):
for message in messages.iteritems(): for message in messages.iteritems():
command = message[0] command = message[0]
@ -21,21 +23,12 @@ class JSONCommandProtocol(LineReceiver):
elif command == "Error": elif command == "Error":
self.handleError(message[1]) self.handleError(message[1])
else: else:
self.dropWithError(getMessage("en", "unknown-command-server-error").format(message[1])) #TODO: log, not drop self.dropWithError(getMessage("en", "unknown-command-server-error").format(message[1])) # TODO: log, not drop
def printReceived(self, line): #TODO: remove
#print ">>i", line
pass
def printSent(self, line):
#print "o<<", line
pass
def lineReceived(self, line): def lineReceived(self, line):
line = line.strip() line = line.strip()
if not line: if not line:
return return
self.printReceived(line)
try: try:
messages = json.loads(line) messages = json.loads(line)
except: except:
@ -45,11 +38,10 @@ class JSONCommandProtocol(LineReceiver):
else: else:
self.dropWithError(getMessage("en", "not-json-server-error").format(line)) self.dropWithError(getMessage("en", "not-json-server-error").format(line))
return return
self.handleMessages(messages) self.handleMessages(messages)
def sendMessage(self, dict_): def sendMessage(self, dict_):
line = json.dumps(dict_) line = json.dumps(dict_)
self.printSent(line)
self.sendLine(line) self.sendLine(line)
def drop(self): def drop(self):
@ -58,25 +50,27 @@ class JSONCommandProtocol(LineReceiver):
def dropWithError(self, error): def dropWithError(self, error):
raise NotImplementedError() raise NotImplementedError()
class SyncClientProtocol(JSONCommandProtocol): class SyncClientProtocol(JSONCommandProtocol):
def __init__(self, client): def __init__(self, client):
self._client = client self._client = client
self.clientIgnoringOnTheFly = 0 self.clientIgnoringOnTheFly = 0
self.serverIgnoringOnTheFly = 0 self.serverIgnoringOnTheFly = 0
self.logged = False self.logged = False
self._pingService = PingService()
def connectionMade(self): def connectionMade(self):
self._client.initProtocol(self) self._client.initProtocol(self)
self.sendHello() self.sendHello()
def connectionLost(self, reason): def connectionLost(self, reason):
self._client.destroyProtocol() self._client.destroyProtocol()
def dropWithError(self, error): def dropWithError(self, error):
self._client.ui.showErrorMessage(error) self._client.ui.showErrorMessage(error)
self._client.protocolFactory.stopRetrying() self._client.protocolFactory.stopRetrying()
self.drop() self.drop()
def _extractHelloArguments(self, hello): def _extractHelloArguments(self, hello):
username = hello["username"] if hello.has_key("username") else None username = hello["username"] if hello.has_key("username") else None
roomName = hello["room"]["name"] if hello.has_key("room") else None roomName = hello["room"]["name"] if hello.has_key("room") else None
@ -90,7 +84,7 @@ class SyncClientProtocol(JSONCommandProtocol):
self.dropWithError(getMessage("en", "hello-server-error").format(hello)) self.dropWithError(getMessage("en", "hello-server-error").format(hello))
elif(version.split(".")[0:2] != syncplay.version.split(".")[0:2]): elif(version.split(".")[0:2] != syncplay.version.split(".")[0:2]):
self.dropWithError(getMessage("en", "version-mismatch-server-error".format(hello))) self.dropWithError(getMessage("en", "version-mismatch-server-error".format(hello)))
else: else:
self._client.setUsername(username) self._client.setUsername(username)
self._client.setRoom(roomName) self._client.setRoom(roomName)
self.logged = True self.logged = True
@ -98,22 +92,22 @@ class SyncClientProtocol(JSONCommandProtocol):
self._client.ui.showMessage(motd, True, True) self._client.ui.showMessage(motd, True, True)
self._client.ui.showMessage(getMessage("en", "connected-successful-notification")) self._client.ui.showMessage(getMessage("en", "connected-successful-notification"))
self._client.sendFile() self._client.sendFile()
def sendHello(self): def sendHello(self):
hello = {} hello = {}
hello["username"] = self._client.getUsername() hello["username"] = self._client.getUsername()
password = self._client.getPassword() password = self._client.getPassword()
if(password): hello["password"] = password if(password): hello["password"] = password
room = self._client.getRoom() room = self._client.getRoom()
if(room): hello["room"] = {"name" :room} if(room): hello["room"] = {"name" :room}
hello["version"] = syncplay.version hello["version"] = syncplay.version
self.sendMessage({"Hello": hello}) self.sendMessage({"Hello": hello})
def _SetUser(self, users): def _SetUser(self, users):
for user in users.iteritems(): for user in users.iteritems():
username = user[0] username = user[0]
settings = user[1] settings = user[1]
room = settings["room"]["name"] if settings.has_key("room") else None room = settings["room"]["name"] if settings.has_key("room") else None
file_ = settings["file"] if settings.has_key("file") else None file_ = settings["file"] if settings.has_key("file") else None
if(settings.has_key("event")): if(settings.has_key("event")):
if(settings["event"].has_key("joined")): if(settings["event"].has_key("joined")):
@ -122,7 +116,7 @@ class SyncClientProtocol(JSONCommandProtocol):
self._client.removeUser(username) self._client.removeUser(username)
else: else:
self._client.userlist.modUser(username, room, file_) self._client.userlist.modUser(username, room, file_)
def handleSet(self, settings): def handleSet(self, settings):
for set_ in settings.iteritems(): for set_ in settings.iteritems():
command = set_[0] command = set_[0]
@ -140,7 +134,7 @@ class SyncClientProtocol(JSONCommandProtocol):
setting["name"] = roomName setting["name"] = roomName
if(password): setting["password"] = password if(password): setting["password"] = password
self.sendSet({"room": setting}) self.sendSet({"room": setting})
def sendFileSetting(self, file_): def sendFileSetting(self, file_):
self.sendSet({"file": file_}) self.sendSet({"file": file_})
self.sendList() self.sendList()
@ -155,10 +149,10 @@ class SyncClientProtocol(JSONCommandProtocol):
position = user[1]['position'] position = user[1]['position']
self._client.userlist.addUser(userName, roomName, file_, position, noMessage=True) self._client.userlist.addUser(userName, roomName, file_, position, noMessage=True)
self._client.userlist.showUserList() self._client.userlist.showUserList()
def sendList(self): def sendList(self):
self.sendMessage({"List": None}) self.sendMessage({"List": None})
def _extractStatePlaystateArguments(self, state): def _extractStatePlaystateArguments(self, state):
position = state["playstate"]["position"] if state["playstate"].has_key("position") else 0 position = state["playstate"]["position"] if state["playstate"].has_key("position") else 0
paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None
@ -167,49 +161,54 @@ class SyncClientProtocol(JSONCommandProtocol):
return position, paused, doSeek, setBy return position, paused, doSeek, setBy
def _handleStatePing(self, state): def _handleStatePing(self, state):
yourLatency = state["ping"]["yourLatency"] if state["ping"].has_key("yourLatency") else 0
senderLatency = state["ping"]["senderLatency"] if state["ping"].has_key("senderLatency") else 0
if (state["ping"].has_key("latencyCalculation")): if (state["ping"].has_key("latencyCalculation")):
latencyCalculation = state["ping"]["latencyCalculation"] latencyCalculation = state["ping"]["latencyCalculation"]
return yourLatency, senderLatency, latencyCalculation if ("clientLatencyCalculation" in state["ping"]):
timestamp = state["ping"]["clientLatencyCalculation"]
senderRtt = state["ping"]["serverRtt"]
self._pingService.receiveMessage(timestamp, senderRtt)
messageAge = self._pingService.getLastForwardDelay()
return messageAge, latencyCalculation
def handleState(self, state): def handleState(self, state):
position, paused, doSeek, setBy = None, None, None, None position, paused, doSeek, setBy = None, None, None, None
yourLatency, senderLatency = 0, 0 messageAge = 0
if(state.has_key("ignoringOnTheFly")): if(state.has_key("ignoringOnTheFly")):
ignore = state["ignoringOnTheFly"] ignore = state["ignoringOnTheFly"]
if(ignore.has_key("server")): if(ignore.has_key("server")):
self.serverIgnoringOnTheFly = ignore["server"] self.serverIgnoringOnTheFly = ignore["server"]
self.clientIgnoringOnTheFly = 0 self.clientIgnoringOnTheFly = 0
elif(ignore.has_key("client")): elif(ignore.has_key("client")):
if(ignore['client']) == self.clientIgnoringOnTheFly: if(ignore['client']) == self.clientIgnoringOnTheFly:
self.clientIgnoringOnTheFly = 0 self.clientIgnoringOnTheFly = 0
if(state.has_key("playstate")): if(state.has_key("playstate")):
position, paused, doSeek, setBy = self._extractStatePlaystateArguments(state) position, paused, doSeek, setBy = self._extractStatePlaystateArguments(state)
if(state.has_key("ping")): if(state.has_key("ping")):
yourLatency, senderLatency, latencyCalculation = self._handleStatePing(state) messageAge, latencyCalculation = self._handleStatePing(state)
if(position is not None and paused is not None and not self.clientIgnoringOnTheFly): if(position is not None and paused is not None and not self.clientIgnoringOnTheFly):
latency = yourLatency + senderLatency self._client.updateGlobalState(position, paused, doSeek, setBy, messageAge)
self._client.updateGlobalState(position, paused, doSeek, setBy, latency)
position, paused, doSeek, stateChange = self._client.getLocalState() position, paused, doSeek, stateChange = self._client.getLocalState()
self.sendState(position, paused, doSeek, latencyCalculation, stateChange) self.sendState(position, paused, doSeek, latencyCalculation, stateChange)
def handleHttpRequest(self, request): def handleHttpRequest(self, request):
pass pass
def sendState(self, position, paused, doSeek, latencyCalculation, stateChange = False): def sendState(self, position, paused, doSeek, latencyCalculation, stateChange=False):
state = {} state = {}
positionAndPausedIsSet = position is not None and paused is not None positionAndPausedIsSet = position is not None and paused is not None
clientIgnoreIsNotSet = self.clientIgnoringOnTheFly == 0 or self.serverIgnoringOnTheFly != 0 clientIgnoreIsNotSet = self.clientIgnoringOnTheFly == 0 or self.serverIgnoringOnTheFly != 0
if(clientIgnoreIsNotSet and positionAndPausedIsSet): if(clientIgnoreIsNotSet and positionAndPausedIsSet):
state["playstate"] = {} state["playstate"] = {}
state["playstate"]["position"] = position state["playstate"]["position"] = position
state["playstate"]["paused"] = paused state["playstate"]["paused"] = paused
if(doSeek): state["playstate"]["doSeek"] = doSeek if(doSeek): state["playstate"]["doSeek"] = doSeek
state["ping"] = {}
if(latencyCalculation): if(latencyCalculation):
state["ping"] = {"latencyCalculation": latencyCalculation} state["ping"]["latencyCalculation"] = latencyCalculation
state["ping"]["clientLatencyCalculation"] = self._pingService.newTimestamp()
state["ping"]["clientRtt"] = self._pingService.getRtt()
if(stateChange): if(stateChange):
self.clientIgnoringOnTheFly += 1 self.clientIgnoringOnTheFly += 1
if(self.serverIgnoringOnTheFly or self.clientIgnoringOnTheFly): if(self.serverIgnoringOnTheFly or self.clientIgnoringOnTheFly):
state["ignoringOnTheFly"] = {} state["ignoringOnTheFly"] = {}
if(self.serverIgnoringOnTheFly): if(self.serverIgnoringOnTheFly):
@ -218,42 +217,45 @@ class SyncClientProtocol(JSONCommandProtocol):
if(self.clientIgnoringOnTheFly): if(self.clientIgnoringOnTheFly):
state["ignoringOnTheFly"]["client"] = self.clientIgnoringOnTheFly state["ignoringOnTheFly"]["client"] = self.clientIgnoringOnTheFly
self.sendMessage({"State": state}) self.sendMessage({"State": state})
def handleError(self, error): def handleError(self, error):
self.dropWithError(error["message"]) #TODO: more processing and fallbacking self.dropWithError(error["message"]) # TODO: more processing and fallbacking
def sendError(self, message): def sendError(self, message):
self.sendMessage({"Error": {"message": message}}) self.sendMessage({"Error": {"message": message}})
class SyncServerProtocol(JSONCommandProtocol): class SyncServerProtocol(JSONCommandProtocol):
def __init__(self, factory): def __init__(self, factory):
self._factory = factory self._factory = factory
self._logged = False self._logged = False
self.clientIgnoringOnTheFly = 0 self.clientIgnoringOnTheFly = 0
self.serverIgnoringOnTheFly = 0 self.serverIgnoringOnTheFly = 0
self._pingService = PingService()
self._clientLatencyCalculation = 0
self._clientLatencyCalculationArrivalTime = 0
def __hash__(self): def __hash__(self):
return hash('|'.join(( return hash('|'.join((
self.transport.getPeer().host, self.transport.getPeer().host,
str(id(self)), str(id(self)),
))) )))
def requireLogged(f): #@NoSelf def requireLogged(f): # @NoSelf
@wraps(f) @wraps(f)
def wrapper(self, *args, **kwds): def wrapper(self, *args, **kwds):
if(not self._logged): if(not self._logged):
self.dropWithError(getMessage("en", "not-known-server-error")) self.dropWithError(getMessage("en", "not-known-server-error"))
return f(self, *args, **kwds) return f(self, *args, **kwds)
return wrapper return wrapper
def dropWithError(self, error): def dropWithError(self, error):
print getMessage("en", "client-drop-server-error").format(self.transport.getPeer().host, error) print getMessage("en", "client-drop-server-error").format(self.transport.getPeer().host, error)
self.sendError(error) self.sendError(error)
self.drop() self.drop()
def connectionLost(self, reason): def connectionLost(self, reason):
self._factory.removeWatcher(self) self._factory.removeWatcher(self)
def _extractHelloArguments(self, hello): def _extractHelloArguments(self, hello):
roomName, roomPassword = None, None roomName, roomPassword = None, None
@ -277,7 +279,7 @@ class SyncServerProtocol(JSONCommandProtocol):
self.dropWithError(getMessage("en", "wrong-password-server-error")) self.dropWithError(getMessage("en", "wrong-password-server-error"))
return False return False
return True return True
def handleHello(self, hello): def handleHello(self, hello):
username, serverPassword, roomName, roomPassword, version = self._extractHelloArguments(hello) username, serverPassword, roomName, roomPassword, version = self._extractHelloArguments(hello)
if(not username or not roomName or not version): if(not username or not roomName or not version):
@ -301,7 +303,7 @@ class SyncServerProtocol(JSONCommandProtocol):
hello["version"] = syncplay.version hello["version"] = syncplay.version
hello["motd"] = self._factory.getMotd(userIp, username, room) hello["motd"] = self._factory.getMotd(userIp, username, room)
self.sendMessage({"Hello": hello}) self.sendMessage({"Hello": hello})
@requireLogged @requireLogged
def handleSet(self, settings): def handleSet(self, settings):
for set_ in settings.iteritems(): for set_ in settings.iteritems():
@ -314,10 +316,10 @@ class SyncServerProtocol(JSONCommandProtocol):
def sendSet(self, setting): def sendSet(self, setting):
self.sendMessage({"Set": setting}) self.sendMessage({"Set": setting})
def sendRoomSetting(self, roomName): def sendRoomSetting(self, roomName):
self.sendSet({"room": {"name": roomName}}) self.sendSet({"room": {"name": roomName}})
def sendUserSetting(self, username, roomName, file_, event): def sendUserSetting(self, username, roomName, file_, event):
room = {"name": roomName} room = {"name": roomName}
user = {} user = {}
@ -328,7 +330,7 @@ class SyncServerProtocol(JSONCommandProtocol):
if(event): if(event):
user[username]["event"] = event user[username]["event"] = event
self.sendSet({"user": user}) self.sendSet({"user": user})
def _addUserOnList(self, userlist, roomPositions, watcher): def _addUserOnList(self, userlist, roomPositions, watcher):
if (not userlist.has_key(watcher.room)): if (not userlist.has_key(watcher.room)):
userlist[watcher.room] = {} userlist[watcher.room] = {}
@ -336,7 +338,7 @@ class SyncServerProtocol(JSONCommandProtocol):
userlist[watcher.room][watcher.name] = { userlist[watcher.room][watcher.name] = {
"file": watcher.file if watcher.file else {}, "file": watcher.file if watcher.file else {},
"position": roomPositions[watcher.room] if roomPositions[watcher.room] else 0 "position": roomPositions[watcher.room] if roomPositions[watcher.room] else 0
} }
def sendList(self): def sendList(self):
userlist = {} userlist = {}
roomPositions = {} roomPositions = {}
@ -344,12 +346,16 @@ class SyncServerProtocol(JSONCommandProtocol):
for watcher in watchers.itervalues(): for watcher in watchers.itervalues():
self._addUserOnList(userlist, roomPositions, watcher) self._addUserOnList(userlist, roomPositions, watcher)
self.sendMessage({"List": userlist}) self.sendMessage({"List": userlist})
@requireLogged @requireLogged
def handleList(self, _): def handleList(self, _):
self.sendList() self.sendList()
def sendState(self, position, paused, doSeek, setBy, senderLatency, watcherLatency, forced = False): def sendState(self, position, paused, doSeek, setBy, forced=False):
if(self._clientLatencyCalculationArrivalTime):
processingTime = time.time() - self._clientLatencyCalculationArrivalTime
else:
processingTime = 0
playstate = { playstate = {
"position": position, "position": position,
"paused": paused, "paused": paused,
@ -357,10 +363,12 @@ class SyncServerProtocol(JSONCommandProtocol):
"setBy": setBy "setBy": setBy
} }
ping = { ping = {
"yourLatency": watcherLatency, "latencyCalculation": self._pingService.newTimestamp(),
"senderLatency": senderLatency, "serverRtt": self._pingService.getRtt()
"latencyCalculation": time.time()
} }
if(self._clientLatencyCalculation):
ping["clientLatencyCalculation"] = self._clientLatencyCalculation + processingTime
self._clientLatencyCalculation = 0
state = { state = {
"ping": ping, "ping": ping,
"playstate": playstate, "playstate": playstate,
@ -376,8 +384,8 @@ class SyncServerProtocol(JSONCommandProtocol):
self.clientIgnoringOnTheFly = 0 self.clientIgnoringOnTheFly = 0
if(self.serverIgnoringOnTheFly == 0 or forced): if(self.serverIgnoringOnTheFly == 0 or forced):
self.sendMessage({"State": state}) self.sendMessage({"State": state})
def _extractStatePlaystateArguments(self, state): def _extractStatePlaystateArguments(self, state):
position = state["playstate"]["position"] if state["playstate"].has_key("position") else 0 position = state["playstate"]["position"] if state["playstate"].has_key("position") else 0
paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None paused = state["playstate"]["paused"] if state["playstate"].has_key("paused") else None
@ -393,20 +401,54 @@ class SyncServerProtocol(JSONCommandProtocol):
if(self.serverIgnoringOnTheFly == ignore["server"]): if(self.serverIgnoringOnTheFly == ignore["server"]):
self.serverIgnoringOnTheFly = 0 self.serverIgnoringOnTheFly = 0
if(ignore.has_key("client")): if(ignore.has_key("client")):
self.clientIgnoringOnTheFly = ignore["client"] self.clientIgnoringOnTheFly = ignore["client"]
if(state.has_key("playstate")): if(state.has_key("playstate")):
position, paused, doSeek = self._extractStatePlaystateArguments(state) position, paused, doSeek = self._extractStatePlaystateArguments(state)
if(state.has_key("ping")): if(state.has_key("ping")):
latencyCalculation = state["ping"]["latencyCalculation"] if state["ping"].has_key("latencyCalculation") else None latencyCalculation = state["ping"]["latencyCalculation"] if state["ping"].has_key("latencyCalculation") else 0
clientRtt = state["ping"]["clientRtt"] if state["ping"].has_key("clientRtt") else 0
self._clientLatencyCalculation = state["ping"]["clientLatencyCalculation"] if state["ping"].has_key("clientLatencyCalculation") else 0
self._clientLatencyCalculationArrivalTime = time.time()
self._pingService.receiveMessage(latencyCalculation, clientRtt)
if(self.serverIgnoringOnTheFly == 0): if(self.serverIgnoringOnTheFly == 0):
self._factory.updateWatcherState(self, position, paused, doSeek, latencyCalculation) self._factory.updateWatcherState(self, position, paused, doSeek, self._pingService.getLastForwardDelay())
def handleHttpRequest(self, request): def handleHttpRequest(self, request):
self.sendLine(self._factory.gethttpRequestReply()) self.sendLine(self._factory.gethttpRequestReply())
def handleError(self, error): def handleError(self, error):
self.dropWithError(error["message"]) #TODO: more processing and fallbacking self.dropWithError(error["message"]) # TODO: more processing and fallbacking
def sendError(self, message): def sendError(self, message):
self.sendMessage({"Error": {"message": message}}) self.sendMessage({"Error": {"message": message}})
class PingService(object):
def __init__(self):
self._rtt = 0
self._fd = 0
self._avrRtt = 0
def newTimestamp(self):
return time.time()
def receiveMessage(self, timestamp, senderRtt):
if(not timestamp):
return
self._rtt = time.time() - timestamp
if(self._rtt < 0 or senderRtt < 0):
return
if(not self._avrRtt):
self._avrRtt = self._rtt
self._avrRtt = self._avrRtt * PING_MOVING_AVERAGE_WEIGHT + self._rtt * (1 - PING_MOVING_AVERAGE_WEIGHT)
if(senderRtt < self._rtt):
self._fd = self._avrRtt/2 + (self._rtt - senderRtt)
else:
self._fd = self._avrRtt/2
def getLastForwardDelay(self):
return self._fd
def getRtt(self):
return self._rtt

View File

@ -155,7 +155,7 @@ class SyncFactory(Factory):
else: else:
return getMessage("en", "server-default-http-reply") return getMessage("en", "server-default-http-reply")
def sendState(self, watcherProtocol, doSeek = False, senderLatency = 0, forcedUpdate = False): def sendState(self, watcherProtocol, doSeek = False, forcedUpdate = False):
watcher = self.getWatcher(watcherProtocol) watcher = self.getWatcher(watcherProtocol)
if(not watcher): if(not watcher):
return return
@ -164,18 +164,10 @@ class SyncFactory(Factory):
setBy = self._roomStates[room]["setBy"] setBy = self._roomStates[room]["setBy"]
watcher.paused = paused watcher.paused = paused
watcher.position = position watcher.position = position
watcherProtocol.sendState(position, paused, doSeek, setBy, senderLatency, watcher.latency, forcedUpdate) watcherProtocol.sendState(position, paused, doSeek, setBy, forcedUpdate)
if(time.time() - watcher.lastUpdate > constants.PROTOCOL_TIMEOUT): if(time.time() - watcher.lastUpdate > constants.PROTOCOL_TIMEOUT):
watcherProtocol.drop() watcherProtocol.drop()
self.removeWatcher(watcherProtocol) self.removeWatcher(watcherProtocol)
def __updateWatcherPing(self, latencyCalculation, watcher):
if (latencyCalculation):
ping = (time.time() - latencyCalculation) / 2
if (watcher.latency):
watcher.latency = watcher.latency * (constants.PING_MOVING_AVERAGE_WEIGHT) + ping * (1-constants.PING_MOVING_AVERAGE_WEIGHT) #Exponential moving average
else:
watcher.latency = ping
def __shouldServerForceUpdateOnRoom(self, pauseChanged, doSeek): def __shouldServerForceUpdateOnRoom(self, pauseChanged, doSeek):
return doSeek or pauseChanged return doSeek or pauseChanged
@ -210,9 +202,10 @@ class SyncFactory(Factory):
if (doSeek and position): if (doSeek and position):
self.ircBot.sp_seek(watcher.name, oldPosition, position, watcher.room) self.ircBot.sp_seek(watcher.name, oldPosition, position, watcher.room)
def updateWatcherState(self, watcherProtocol, position, paused, doSeek, latencyCalculation): def updateWatcherState(self, watcherProtocol, position, paused, doSeek, messageAge):
watcher = self.getWatcher(watcherProtocol) watcher = self.getWatcher(watcherProtocol)
self.__updateWatcherPing(latencyCalculation, watcher) if(not watcher):
return
watcher.lastUpdate = time.time() watcher.lastUpdate = time.time()
if(watcher.file): if(watcher.file):
oldPosition = self._roomStates[watcher.room]["position"] oldPosition = self._roomStates[watcher.room]["position"]
@ -220,11 +213,13 @@ class SyncFactory(Factory):
if(paused is not None): if(paused is not None):
pauseChanged = self.__updatePausedState(paused, watcher) pauseChanged = self.__updatePausedState(paused, watcher)
if(position is not None): if(position is not None):
if(not paused):
position += messageAge
self.__updatePositionState(position, doSeek or pauseChanged, watcher) self.__updatePositionState(position, doSeek or pauseChanged, watcher)
forceUpdate = self.__shouldServerForceUpdateOnRoom(pauseChanged, doSeek) forceUpdate = self.__shouldServerForceUpdateOnRoom(pauseChanged, doSeek)
if(forceUpdate): if(forceUpdate):
self.__notifyIrcBot(position, paused, doSeek, watcher, oldPosition, pauseChanged) self.__notifyIrcBot(position, paused, doSeek, watcher, oldPosition, pauseChanged)
l = lambda w: self.sendState(w, doSeek, watcher.latency, forceUpdate) l = lambda w: self.sendState(w, doSeek, forceUpdate)
self.broadcastRoom(watcher.watcherProtocol, l) self.broadcastRoom(watcher.watcherProtocol, l)
def removeWatcher(self, watcherProtocol): def removeWatcher(self, watcherProtocol):
@ -262,6 +257,8 @@ class SyncFactory(Factory):
def watcherSetFile(self, watcherProtocol, file_): def watcherSetFile(self, watcherProtocol, file_):
watcher = self.getWatcher(watcherProtocol) watcher = self.getWatcher(watcherProtocol)
if(not watcher):
return
watcher.file = file_ watcher.file = file_
l = lambda w: w.sendUserSetting(watcher.name, watcher.room, watcher.file, None) l = lambda w: w.sendUserSetting(watcher.name, watcher.room, watcher.file, None)
self.broadcast(watcherProtocol, l) self.broadcast(watcherProtocol, l)
@ -299,7 +296,7 @@ class SyncFactory(Factory):
self.ircBot.sp_paused("IRC: " + user.name, user.room) self.ircBot.sp_paused("IRC: " + user.name, user.room)
elif(not paused): elif(not paused):
self.ircBot.sp_unpaused("IRC: " + user.name, user.room) self.ircBot.sp_unpaused("IRC: " + user.name, user.room)
l = lambda w: self.sendState(w, False, user.latency, True) l = lambda w: self.sendState(w, False, 0, True)
self.broadcastRoom(user.watcherProtocol, l) self.broadcastRoom(user.watcherProtocol, l)
@ -320,7 +317,7 @@ class SyncFactory(Factory):
self._roomStates[user.room]['paused'] = time self._roomStates[user.room]['paused'] = time
self._roomStates[user.room]['setBy'] = "IRC: " + setBy self._roomStates[user.room]['setBy'] = "IRC: " + setBy
self.ircBot.sp_seek(user.name, oldPosition, time, user.room) self.ircBot.sp_seek(user.name, oldPosition, time, user.room)
l = lambda w: self.sendState(w, True, user.latency, True) l = lambda w: self.sendState(w, True, 0, True)
self.broadcastRoom(user.watcherProtocol, l) self.broadcastRoom(user.watcherProtocol, l)
@ -368,7 +365,6 @@ class Watcher(object):
self.file = None self.file = None
self._sendStateTimer = None self._sendStateTimer = None
self.position = None self.position = None
self.latency = 0
self.lastUpdate = time.time() self.lastUpdate = time.time()
def __lt__(self, b): def __lt__(self, b):