connectionService.py (13791B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\client\script\remote\connectionService.py 2 import sys 3 import blue 4 import localization 5 import log 6 import telemetry 7 import uthread 8 import uthread2 9 from carbon.common.lib import const, serverInfo 10 from carbon.common.script.net.GPSExceptions import GPSException 11 from carbon.common.script.sys import basesession, serviceConst 12 from carbon.common.script.sys.service import Service 13 from carbon.common.script.util.exceptionEater import ExceptionEater 14 from carbon.common.script.util.format import FmtTime 15 from carbonui import uiconst 16 from carbonui.uicore import uicore 17 from eve.common.lib.jwt import is_jwt 18 from eveexceptions import UserError 19 from eveprefs import prefs 20 from globalConfig.getFunctions import GetMaxConnectionIdleTime 21 from player_messaging.client.ui_message import prompt_player_fsd_dialog 22 from utillib import GetServerName, GetServerPort, GetLoginCredentials 23 DEFAULTLONGWAITWARNINGSECS = 300 24 LONGWAITWARNINGMETHODS = ['MachoBindObject', 'SelectCharacterID'] 25 LoginProgressLabels = {'loginprogress::authenticating': '/Carbon/UI/Login/Progress/Authenticating', 26 'loginprogress::connecting': '/Carbon/UI/Login/Progress/Connecting', 27 'loginprogress::done': '/Carbon/UI/Login/Progress/Done', 28 'loginprogress::lowlevelversioncheck': '/Carbon/UI/Login/Progress/LowLevelVersionCheck'} 29 MIN_SYNC_TIME_IN_MINUTES = 1 30 MAX_SYNC_TIME_IN_MINUTES = 3 31 32 class ConnectionService(Service): 33 __guid__ = 'svc.connection' 34 __servicename__ = 'connection' 35 __displayname__ = 'Network Connection Service' 36 __exportedcalls__ = {'Connect': [], 37 'Disconnect': [], 38 'IsConnected': [], 39 'ConnectToService': [], 40 'Login': []} 41 __dependencies__ = ['machoNet'] 42 __notifyevents__ = ['OnProcessLoginProgress'] 43 44 def __init__(self): 45 Service.__init__(self) 46 self.shell = None 47 self.reentrancyGaurd = 0 48 self.clocksynchronizing = 0 49 self.clocklastsynchronized = None 50 self.lastLongCallTimestamp = 0 51 self.loginJWT = None 52 self.accessToken = None 53 self.computerHash = None 54 blue.pyos.synchro.timesyncs.append(self.OnTimerResync) 55 56 def Run(self, *args): 57 Service.Run(self, *args) 58 uthread.worker('ConnectionService::LongCallTimer', self.__LongCallTimer) 59 60 def OnTimerResync(self, old, new): 61 if session and session.nextSessionChange: 62 diff = new - old 63 log.general.Log('Readjusting next session change by %.3f seconds' % (diff / float(const.SEC)), log.LGINFO) 64 session.nextSessionChange += diff 65 66 @telemetry.ZONE_METHOD 67 def Connect(self, host, port, userid, password, ct): 68 try: 69 self.LogInfo('calling connect') 70 address = '%s:%d' % (str(host), port) 71 response = self.machoNet.ConnectToServer(address, userid, password, ct) 72 self.LogInfo('connect completed...') 73 except GPSException as what: 74 self.LogError('connect failed %s' % what) 75 raise UserError('LoginConnectFailed', {'what': what}) 76 77 self.SynchronizeClock() 78 uthread.new(self.ClockSyncDaemon) 79 uthread2.start_tasklet(self.PingLoop) 80 return response 81 82 def ConnectSso(self, host, port, token, ct): 83 try: 84 self.LogInfo('calling connect') 85 address = '%s:%d' % (str(host), port) 86 response = self.machoNet.ConnectToServer(address, 'EVE-SSO-CONNECTION', 'fakePW', ct, token) 87 self.LogInfo('connect completed...') 88 except GPSException as what: 89 self.LogError('connect failed %s' % what) 90 raise UserError('LoginConnectFailed', {'what': what}) 91 92 self.SynchronizeClock() 93 uthread.new(self.ClockSyncDaemon) 94 uthread2.start_tasklet(self.PingLoop) 95 return response 96 97 @telemetry.ZONE_METHOD 98 def OnProcessLoginProgress(self, what, prefix = None, current = 1, total = 1, response = None): 99 if what not in LoginProgressLabels.keys(): 100 text = "Unexpected step in OnProcessLoginProgress, '%s'" % what 101 log.LogTraceback(extraText=text, severity=log.LGERR) 102 return 103 useMorph = 1 104 msg = localization.GetByLabel(LoginProgressLabels[what]) 105 uthread.new(sm.GetService('loading').ProgressWnd, localization.GetByLabel('/Carbon/UI/Login/LoggingIn'), msg, current, total, useMorph=useMorph, autoTick=useMorph) 106 blue.pyos.synchro.Yield() 107 108 @telemetry.ZONE_METHOD 109 def Login(self, loginparam): 110 if self.reentrancyGaurd: 111 return 112 self.reentrancyGaurd = 1 113 try: 114 if loginparam is None: 115 raise RuntimeError('loginparam can not be None anymore dude') 116 if loginparam[4]: 117 ct = 'udp' 118 else: 119 ct = 'tcp' 120 response = self.Connect(loginparam[2], loginparam[3], loginparam[0], loginparam[1], ct) 121 return response 122 finally: 123 self.reentrancyGaurd = 0 124 125 def LoginSso(self, token): 126 if self.reentrancyGaurd: 127 return 128 self.reentrancyGaurd = 1 129 try: 130 if token is None: 131 raise RuntimeError('Trying to do an SSO log in without an SSO token') 132 if is_jwt(token): 133 self.loginJWT = token 134 response = self.ConnectSso(serverInfo.GetServerIP(GetServerName()), GetServerPort(), token, 'tcp') 135 self.accessToken = response['access_token'] 136 self.computerHash = response['computer_hash'] 137 return response 138 finally: 139 self.reentrancyGaurd = 0 140 141 def GetLoginJWT(self): 142 return self.loginJWT 143 144 def GetAccessToken(self): 145 return self.accessToken 146 147 def GetComputerHash(self): 148 return self.computerHash 149 150 def Disconnect(self, silently = 0): 151 if self.reentrancyGaurd: 152 return 153 self.reentrancyGaurd = 1 154 try: 155 self.machoNet.DisconnectFromServer() 156 finally: 157 self.reentrancyGaurd = 0 158 159 def ConnectToProxyService(self, serviceName): 160 return session.ConnectToRemoteService(serviceName, sm.services['machoNet'].myProxyNodeID) 161 162 def ConnectToService(self, serviceName): 163 return session.ConnectToRemoteService(serviceName) 164 165 def IsConnected(self): 166 return self.machoNet.IsConnected() 167 168 def PingLoop(self): 169 while self.IsConnected(): 170 maxIdleTime = GetMaxConnectionIdleTime(self.machoNet) 171 if maxIdleTime <= 0: 172 log.LogInfo('Ping inactive') 173 uthread2.sleep(300) 174 continue 175 sleepTime = maxIdleTime 176 with ExceptionEater('Exception during ping loop'): 177 transport = self.machoNet.GetTransport('ip:packet:server').transport 178 idleTime = transport.GetIdleTime() 179 if idleTime >= maxIdleTime: 180 log.LogInfo('Ping after %d seconds idle time' % idleTime) 181 sm.ProxySvc('pingService').Ping() 182 idleTime = 0 183 sleepTime = maxIdleTime - idleTime 184 log.LogInfo('Ping loop sleeping for %d seconds' % sleepTime) 185 uthread2.sleep(sleepTime) 186 187 def ClockSyncDaemon(self): 188 syncRateMS = MAX_SYNC_TIME_IN_MINUTES * 60 * 1000 189 while self.IsConnected(): 190 with ExceptionEater('Exception during soft clock sync'): 191 blue.synchro.SleepWallclock(syncRateMS) 192 log.LogInfo('*** Soft Clock Sync Starting ***') 193 results = [] 194 numPings = 5 195 blue.os.timeSyncAdjust = 0 196 for i in xrange(numPings): 197 myTime = blue.os.GetWallclockTimeNow() 198 serverTime = sm.ProxySvc('machoNet').GetTime() 199 now = blue.os.GetWallclockTimeNow() 200 elaps = now - myTime 201 serverTime += elaps / 2 202 diff = float(now - serverTime) 203 results.append((elaps, diff)) 204 log.LogInfo('SoftClock:', elaps, diff) 205 206 averageElaps = sum([ x[0] for x in results ]) / numPings 207 results = filter(lambda x: x[0] < averageElaps * 1.2, results) 208 log.LogInfo('SoftClock results:', results) 209 avgDiff = sum([ x[1] for x in results ]) / len(results) 210 log.LogInfo('*** Soft Clock Sync adjusting by', -avgDiff / const.SEC, 'seconds') 211 blue.os.timeSyncAdjust = long(-avgDiff) 212 if abs(avgDiff) > 3 * const.SEC: 213 log.LogError('Having to adjust more than 3 seconds in a single timesync interval! (%fs per %ds)' % (avgDiff / float(const.SEC), syncRateMS / 1000)) 214 syncRateMS = max(MIN_SYNC_TIME_IN_MINUTES * 60 * 1000, syncRateMS - 60000) 215 else: 216 syncRateMS = min(MAX_SYNC_TIME_IN_MINUTES * 60 * 1000, syncRateMS + 60000) 217 driftFactor = abs(avgDiff) / float(syncRateMS * const.MSEC) 218 driftFactor = min(0.95, max(0.02, driftFactor)) 219 blue.os.timeSyncAdjustFactor = driftFactor 220 221 def SynchronizeClock(self, firstTime = 1, maxIterations = 5): 222 log.general.Log('connection.synchronizeClock called', log.LGINFO) 223 if not firstTime: 224 if self.clocklastsynchronized is not None and blue.os.GetWallclockTime() - self.clocklastsynchronized < const.HOUR: 225 return 226 if self.clocksynchronizing: 227 return 228 self.clocklastsynchronized = blue.os.GetWallclockTime() 229 self.clocksynchronizing = 1 230 try: 231 diff = 0 232 goodCount = 0 233 lastElaps = None 234 log.general.Log('*** *** *** *** Clock Synchronizing loop initiating *** *** *** ***', log.LGINFO) 235 for i in range(maxIterations): 236 myTime = blue.os.GetWallclockTimeNow() 237 serverTime = sm.ProxySvc('machoNet').GetTime() 238 now = blue.os.GetWallclockTimeNow() 239 elaps = now - myTime 240 serverTime += elaps / 2 241 diff = float(now - serverTime) / float(const.SEC) 242 if diff > 2.0 and not firstTime: 243 logflag = log.LGERR 244 else: 245 logflag = log.LGINFO 246 log.general.Log('Synchronizing clock diff %.3f sec elaps %f sec.' % (diff, elaps / float(const.SEC)), logflag) 247 if lastElaps is None or elaps < lastElaps and elaps < const.SEC: 248 goodCount += 1 249 log.general.Log('Synchronizing clock: iteration completed, setting time', logflag) 250 blue.pyos.synchro.ResetClock(serverTime) 251 lastElaps = elaps 252 else: 253 log.general.Log('Synchronizing clock: iteration ignored as it was less accurate (%f) than our current time (%f)' % (elaps / float(const.SEC), lastElaps / float(const.SEC)), log.LGINFO) 254 if goodCount >= 3: 255 break 256 firstTime = 0 257 258 log.general.Log('*** *** *** *** Clock Synchronizing loop completed *** *** *** ***', log.LGINFO) 259 finally: 260 self.clocksynchronizing = 0 261 262 def IsClockSynchronizing(self): 263 if self.clocksynchronizing: 264 log.general.Log('Clock synchronization in progress', log.LGINFO) 265 return self.clocksynchronizing 266 267 def __LongCallTimer(self): 268 longWarningSecs = prefs.GetValue('longOutstandingCallWarningSeconds', DEFAULTLONGWAITWARNINGSECS) 269 sleepSecs = 60 270 if longWarningSecs < sleepSecs: 271 sleepSecs = longWarningSecs 272 self.LogInfo('__LongCallTimer reporting warnings after', longWarningSecs, 'seconds and checking every', sleepSecs, 'seconds') 273 274 def ShouldWarn(method): 275 for w in LONGWAITWARNINGMETHODS: 276 if w in repr(method): 277 return True 278 279 return False 280 281 while self.state == serviceConst.SERVICE_RUNNING: 282 if sm.GetService('machoNet').GetGlobalConfig().get('disableLongCallWarning'): 283 self.LogWarn('__LongCallTimer should not be running! Exiting.') 284 return 285 blue.pyos.synchro.SleepWallclock(sleepSecs * 1000) 286 try: 287 maxDiff = 0 288 for ct in basesession.outstandingCallTimers: 289 method = ct[0] 290 t = ct[1] 291 diff = blue.os.GetWallclockTimeNow() - t 292 if diff > maxDiff and ShouldWarn(method): 293 maxDiff = diff 294 if diff > 60 * const.SEC: 295 self.LogWarn('Have waited', FmtTime(diff), 'for', method) 296 297 if maxDiff > longWarningSecs * const.SEC and self.lastLongCallTimestamp < blue.os.GetWallclockTimeNow() - longWarningSecs * const.SEC: 298 modalWnd = uicore.registry.GetModalWindow() 299 if modalWnd: 300 modalWnd.SetModalResult(uiconst.ID_CLOSE) 301 uthread.new(prompt_player_fsd_dialog, 'LongWaitForRemoteCall', {'time': int(maxDiff / const.MIN)}) 302 self.lastLongCallTimestamp = blue.os.GetWallclockTimeNow() 303 except: 304 log.LogException() 305 sys.exc_clear() 306 307 def TryAutomaticLogin(self): 308 if GetLoginCredentials() is None or session.userid is not None: 309 return 310 username, password = GetLoginCredentials() 311 self.Login((username, 312 password, 313 GetServerName(), 314 GetServerPort(), 315 None))