Merge branch 'spikeDrops'
This commit is contained in:
commit
a919cfe49a
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user