Example #1
0
	def tickWrap(self):
		try:
			self.tick()
			return True
		except Exception, e:
			print "EventThread error:"
			if self.gameCon is not None:
				self.gameCon.err = str(e)
				if hasattr(self.gameCon, 'game'):
					WSSpadesHandler.on_game_change(self.gameCon.game)
			traceback.print_exc()
			return False
Example #2
0
	def tick(self):
		if self.gameCon is None:
			# Get list of logs (['YYYY-mm-dd_HHMMSS.log'])
			# Unfortunately the server doesn't return an etag for this page; instead we cache based on the number of logs displayed, on the assumption that logs will never be removed
			req = requests.get(logURL)
			if req.status_code != 200:
				raise RuntimeError("Server returned %d looking up log list" % req.status_code)
			logs = map(str, lxml.html.fromstring(req.text).xpath('//a[substring-after(@href, ".")="log"]/@href'))
			logs = filter(None, map(Shim.onLogLoad, logs))

			numGames = len(getGames())
			if len(logs) < numGames:
				raise RuntimeError("Only got %d %s from server (have %d in database)" % (len(logs), 'log' if len(logs) == 1 else 'logs', numGames))
			elif len(logs) == numGames: # No new logs
				return

			# Find the first one we don't have a game for
			for log in logs:
				if log not in db['games']:
					console('event thread', "Starting new game for %s" % log)
					self.gameCon = Shim.onGameCon(GameConstructor(log, self.onGameEnd))
					break
			else:
				console('event thread', "No games in progress")
				return

		# Look for new events in the current log
		data = self.cachedGet(logURL + self.gameCon.logFilename)
		if data is None: # Nothing new
			console('event thread', "No new data in %s" % self.gameCon.logFilename)
			return
		elif data is False: # Server is down. Hopefully temporarily; try again next tick
			console('event thread', "Failed to fetch data from %s" % self.gameCon.logFilename)
			return
		elif len(data) < self.gameCon.logOffset:
			raise RuntimeError("Fetched %d-byte log file %s, but next event expected at %d" % (len(data), self.gameCon.logFilename, self.gameCon.logOffset))
		while self.gameCon and self.gameCon.logOffset < len(data) and self.test != 0:
			line = data[self.gameCon.logOffset:data.index('\n', self.gameCon.logOffset)+1]
			print "%8d %s" % (self.gameCon.logOffset, line)
			originalLen = len(line)
			line = Shim.onLine(self.gameCon, self.gameCon.logOffset, unpretty(line))
			if line is None:
				self.gameCon.logOffset += originalLen
				continue
			# print "Searching for pattern at %s offset %d: %s" % (self.gameCon.logFilename, self.gameCon.logOffset, line)
			for pattern, fns in eventPatterns:
				match = pattern.match(line)
				if match:
					for fn in fns:
						g = match.groupdict() # Copy; the original match groupdict is not changed below
						# Bit of a hack. We want to rewrite any group that contains a PLAY, but there's no way to tell now. Currently all those groups are named 'play', so we only rewrite those
						if 'play' in g:
							g['play'] = unpretty(g['play'])
						# Same hack with USER
						for k in ('user', 'user1', 'user2'):
							if k in g:
								shimmed = Shim.onUsername(g[k])
								if shimmed != g[k]:
									self.gameCon.usernameShims[shimmed] = g[k]
									g[k] = shimmed
						tz = int(round((datetime.now() - datetime.utcnow()).total_seconds() / 3600))
						event = {'ts': datetime.strptime(g['ts'], '%Y-%m-%d %H:%M:%S') + timedelta(hours = tz), 'off': self.gameCon.logOffset}
						del g['ts']
						event.update(fn(**g))
						if self.test > 0:
							self.test -= 1
						event = Shim.onEvent(self.gameCon, self.gameCon.logOffset, event)
						if event is not None:
							self.gameCon.pump(event)
					# pump() may have triggered onGameEnd and killed the current gameCon
					if self.gameCon is not None:
						self.gameCon.logOffset += originalLen
					break
			else:
				raise RuntimeError("Unrecognized log line at %s:%d: %s" % (self.gameCon.logFilename, self.gameCon.logOffset, line))
		if hasattr(self.gameCon, 'game'):
			self.gameCon.game.out()
			WSSpadesHandler.on_game_change(self.gameCon.game)