Пример #1
0
    async def send_loop(self):
        while True:
            await asyncio.sleep(0.25)
            if len(self.send_queue) == 0:
                continue

            # Copy send queue and clear the global one
            queue = self.send_queue.copy()
            self.send_queue.clear()

            # Process and push out the queue.
            try:
                await self.instance.gbx.multicall(*queue)
            except Fault as e:
                if 'Login unknown' in str(e):
                    return
                logger.exception(e)
                handle_exception(exception=e,
                                 module_name=__name__,
                                 func_name='send_loop')
            except Exception as e:
                logger.exception(e)
                handle_exception(exception=e,
                                 module_name=__name__,
                                 func_name='send_loop')
Пример #2
0
	async def add_map(self, filename, insert=True, save_matchsettings=True):
		"""
		Add or insert map to current online playlist.

		:param filename: Load from filename relative to the 'Maps' directory on the dedicated host server.
		:param insert: Insert after the current map, this will make it play directly after the current map. True by default.
		:param save_matchsettings: Save match settings as well.
		:type filename: str
		:type insert: bool
		:type save_matchsettings: bool
		:raise: pyplanet.contrib.map.exceptions.MapIncompatible
		:raise: pyplanet.contrib.map.exceptions.MapException
		"""
		gbx_method = 'InsertMap' if insert else 'AddMap'

		try:
			result = await self._instance.gbx(gbx_method, filename)
		except Fault as e:
			if 'unknown' in e.faultString:
				raise MapNotFound('Map is not found on the server.')
			elif 'already' in e.faultString:
				raise MapException('Map already added to server.')
			raise MapException(e.faultString)

		# Try to save match settings.
		try:
			if save_matchsettings:
				await self.save_matchsettings()
		except Exception as e:
			handle_exception(e, __name__, 'add_map', extra_data={'EXTRAHOOK': 'Map Insert bug, see #306'})

		return result
Пример #3
0
    async def execute_receiver(receiver,
                               args,
                               kwargs,
                               ignore_exceptions=False):
        try:
            if asyncio.iscoroutinefunction(receiver):
                if len(args) > 0:
                    return receiver, await receiver(*args, **kwargs)
                return receiver, await receiver(**kwargs)

            if len(args) > 0:
                return receiver, receiver(*args, **kwargs)
            return receiver, receiver(**kwargs)
        except Exception as exc:
            if not ignore_exceptions:
                raise

            logger.exception(SignalException(
                'Signal receiver \'{}\' => {} thrown an exception!'.format(
                    receiver.__module__, receiver.__name__)),
                             exc_info=False)

            # Handle, will send to sentry if it's related to the core/contrib apps.
            handle_exception(exc, receiver.__module__, receiver.__name__)

            # Log the actual exception.
            logger.exception(exc)
            return receiver, exc
Пример #4
0
	async def listen(self):
		"""
		Listen to socket.
		"""
		try:
			while True:
				head = await self.reader.readexactly(8)
				size, handle = struct.unpack_from('<LL', head)
				body = await self.reader.readexactly(size)
				data = method = fault = None

				try:
					data, method = loads(body, use_builtin_types=True)
				except Fault as e:
					fault = e
				except ExpatError as e:
					# See #121 for this solution.
					handle_exception(exception=e, module_name=__name__, func_name='listen', extra_data={'body': body})
					continue

				if data and len(data) == 1:
					data = data[0]

				self.event_loop.create_task(self.handle_payload(handle, method, data, fault))
		except ConnectionResetError as e:
			logger.critical(
				'Connection with the dedicated server has been closed, we will now close down the subprocess! {}'.format(str(e))
			)
			# When the connection has been reset, we will close the controller process so it can be restarted by the god
			# process. Exit code 10 gives the information to the god process.
			exit(10)
		except Exception as e:
			handle_exception(exception=e, module_name=__name__, func_name='listen')
			raise
Пример #5
0
	async def execute(self, method, *args):
		payload = dumps(args, methodname=method, allow_none=True)
		body = gzip.compress(payload.encode('utf8'))
		try:
			res = await self.loop.run_in_executor(None, self.__request, body)
			data, _ = loads(res.text, use_datetime=True)
			if isinstance(data, (tuple, list)) and len(data) > 0 and len(data[0]) > 0:
				if isinstance(data[0][0], dict) and 'faultCode' in data[0][0]:
					raise DedimaniaFault(faultCode=data[0][0]['faultCode'], faultString=data[0][0]['faultString'])
				self.retries = 0
				return data[0]
			raise DedimaniaTransportException('Invalid response from dedimania!')
		except (ConnectionError, ReadTimeout, ConnectionRefusedError, requests.exceptions.ConnectionError) as e:
			raise DedimaniaTransportException(e) from e
		except ConnectTimeout as e:
			raise DedimaniaTransportException(e) from e
		except DedimaniaTransportException:
			# Try to setup new session.
			self.retries += 1
			if self.retries > 5:
				raise DedimaniaTransportException('Dedimania didn\'t gave the right answer after few retries!')
			self.client = requests.session()
			try:
				await self.authenticate()
				return await self.execute(method, *args)
			except Exception as e:
				logger.error('XML-RPC Fault retrieved from Dedimania: {}'.format(str(e)))
				handle_exception(e, __name__, 'execute')
				raise DedimaniaTransportException('Could not retrieve data from dedimania!')
		except DedimaniaFault as e:
			if 'Bad SessionId' in e.faultString or ('SessionId' in e.faultString and 'not found' in e.faultString):
				try:
					self.retries += 1
					if self.retries > 5:
						raise DedimaniaTransportException('Max retries reached for reauthenticating with dedimania!')

					# Save original session ID.
					original_session_id = '{}'.format(self.session_id)

					# Reauthenticate
					await self.authenticate()

					# Replace session_id in args.
					if len(args) > 0 and len(args[0]) > 0 and isinstance(args[0][0], dict) and 'params' in args[0][0]:
						new_params = list(args[0][0]['params'])
						if (new_params and isinstance(new_params[0], str)
						    and new_params[0] == original_session_id):
							new_params[0] = self.session_id
							args[0][0]['params'] = tuple(new_params)

					# Try again.
					return await self.execute(method, *args)
				except:
					return
			logger.error('XML-RPC Fault retrieved from Dedimania: {}'.format(str(e)))
			handle_exception(e, __name__, 'execute', extra_data={
				'dedimania_retries': self.retries,
			})
			raise DedimaniaTransportException('Could not retrieve data from dedimania!')
Пример #6
0
 async def execute(self, method, *args):
     payload = dumps(args, methodname=method, allow_none=True)
     body = gzip.compress(payload.encode('utf8'))
     try:
         res = await self.loop.run_in_executor(None, self.__request, body)
         data, _ = loads(res.text, use_datetime=True)
         if isinstance(
                 data,
             (tuple, list)) and len(data) > 0 and len(data[0]) > 0:
             if isinstance(data[0][0], dict) and 'faultCode' in data[0][0]:
                 raise DedimaniaFault(faultCode=data[0][0]['faultCode'],
                                      faultString=data[0][0]['faultString'])
             self.retries = 0
             return data[0]
         raise DedimaniaTransportException(
             'Invalid response from dedimania!')
     except (ConnectionError, ReadTimeout) as e:
         raise DedimaniaTransportException(e) from e
     except ConnectTimeout as e:
         raise DedimaniaTransportException(e) from e
     except DedimaniaTransportException:
         # Try to setup new session.
         self.retries += 1
         if self.retries > 5:
             raise DedimaniaTransportException(
                 'Dedimania didn\'t gave the right answer after few retries!'
             )
         self.client = requests.session()
         try:
             await self.authenticate()
             return await self.execute(method, *args)
         except Exception as e:
             logger.error(
                 'XML-RPC Fault retrieved from Dedimania: {}'.format(
                     str(e)))
             handle_exception(e, __name__, 'execute')
             raise DedimaniaTransportException(
                 'Could not retrieve data from dedimania!')
     except DedimaniaFault as e:
         if 'Bad SessionId' in e.faultString:
             try:
                 self.retries += 1
                 if self.retries > 5:
                     raise DedimaniaTransportException(
                         'Max retries reached for reauthenticating with dedimania!'
                     )
                 await self.authenticate()
                 return await self.execute(method, *args)
             except:
                 return
         logger.error('XML-RPC Fault retrieved from Dedimania: {}'.format(
             str(e)))
         handle_exception(e, __name__, 'execute')
         raise DedimaniaTransportException(
             'Could not retrieve data from dedimania!')
Пример #7
0
    async def refresh_records(self):
        try:
            player_list, self.current_script = await asyncio.gather(
                self.instance.gbx('GetPlayerList', -1, 0),
                self.instance.mode_manager.get_current_script(),
            )
            self.server_max_rank, modes, player_infos, self.current_records = await self.api.get_map_details(
                self.instance.map_manager.current_map,
                self.current_script,
                server_name=self.instance.game.server_name,
                server_comment='',
                is_private=self.instance.game.server_is_private,
                max_players=self.instance.game.
                server_max_players['CurrentValue'],
                max_specs=self.instance.game.server_max_specs['CurrentValue'],
                players=player_list,
                server_login=self.instance.game.server_player_login)
            self.ready = True
        except DedimaniaTransportException as e:
            self.ready = False

            if 'Max retries exceeded' in str(e):
                message = '$f00Error: Dedimania seems down?'
            else:
                message = '$f00Error: Dedimania error occured!'
                logger.exception(e)
            await self.instance.chat(message)
            return
        except DedimaniaFault as e:
            self.ready = False

            if 'session' in str(e).lower():
                handle_exception(e,
                                 module_name=__name__,
                                 func_name='refresh_records')
            logger.error('Dedimania gave an Fault: {}'.format(str(e)))

            self.current_records = list()
            return
        except Exception as e:
            self.ready = False
            handle_exception(e,
                             module_name=__name__,
                             func_name='refresh_records')
            logger.exception(e)
            self.current_records = list()
            return

        for info in player_infos:
            self.player_info[info['Login']] = dict(
                banned=False,
                login=info['Login'],
                max_rank=info['MaxRank'],
            )
Пример #8
0
 def check_memory_leak(self):
     asyncio.get_event_loop().call_later(30, self.check_memory_leak)
     logger.debug('Checking for memory leaks...')
     if 0 < len(gc.garbage) < 20:
         logger.warning('Found possible memory leaks: {}'.format(
             gc.garbage))
     elif len(gc.garbage) >= 20:
         logger.error('Found memory leaks: {}'.format(gc.garbage))
         if not self.reported:
             try:
                 raise MemoryError('Found memory leaks: {}'.format(
                     gc.garbage))
             except MemoryError as e:
                 log.handle_exception(exception=e,
                                      extra_data=dict(leaks=gc.garbage))
             self.reported = True
Пример #9
0
	async def refresh_records(self):
		try:
			player_list, self.current_script = await asyncio.gather(
				self.instance.gbx('GetPlayerList', -1, 0),
				self.instance.mode_manager.get_current_script(),
			)
			self.server_max_rank, modes, player_infos, self.current_records = await self.api.get_map_details(
				self.instance.map_manager.current_map,
				self.current_script,
				server_name=self.instance.game.server_name, server_comment='', is_private=self.instance.game.server_is_private,
				max_players=self.instance.game.server_max_players, max_specs=self.instance.game.server_max_specs,
				players=player_list,
				server_login=self.instance.game.server_player_login
			)
			self.ready = True
		except DedimaniaNotSupportedException as e:
			self.ready = False
			await self.instance.chat('$0b3Dedimania doesn\'t support or know the current script mode {}'.format(
				self.current_script
			))
			logger.warning('Dedimania doesn\'t support or known the mode {}'.format(self.current_script))

			# Still silently report.
			handle_exception(e, module_name=__name__, func_name='refresh_records', extra_data={
				'script': self.current_script
			})
			return
		except DedimaniaTransportException as e:
			self.ready = False

			if 'Max retries exceeded' in str(e) or 'Max retries reached' in str(e):
				message = '$f00Error: Dedimania seems down? We retried to connect but after several retries it kept failing.'
			else:
				message = '$f00Error: Dedimania error occured! ({})'.format(str(e))
				logger.exception(e)
			await self.instance.chat(message)
			return
		except DedimaniaFault as e:
			self.ready = False

			if 'session' in str(e).lower():
				handle_exception(e, module_name=__name__, func_name='refresh_records')
			logger.error('Dedimania gave an Fault: {}'.format(str(e)))

			self.current_records = list()
			return
		except Exception as e:
			self.ready = False
			handle_exception(e, module_name=__name__, func_name='refresh_records')
			logger.exception(e)
			self.current_records = list()
			return

		for info in player_infos:
			self.player_info[info['Login']] = dict(
				banned=False, login=info['Login'], max_rank=info['MaxRank'],
			)
Пример #10
0
	async def player_finish(self, player, race_time, lap_time, cps, flow, raw, **kwargs):
		record_limit = await self.setting_record_limit.get_value()
		chat_announce = await self.setting_chat_announce.get_value()
		async with self.lock:
			current_records = [x for x in self.current_records if x.player.login == player.login]
			score = lap_time

			previous_index = None
			previous_time = None

			if len(current_records) > 0:
				current_record = current_records[0]
				if score > current_record.score:
					# No improvement, ignore
					return

				# Temporary make index + time local for the messages.
				previous_index = self.current_records.index(current_record) + 1
				previous_time = current_record.score

				# If equal, only show message.
				if score == current_record.score and (record_limit == 0 or previous_index <= record_limit):
					message = '$fff{}$z$s$0f3 equalled the $fff{}.$0f3 Local Record: $fff\uf017 {}$0f3.'.format(
						player.nickname, previous_index, times.format_time(score)
					)

					if chat_announce >= previous_index:
						return await self.instance.chat(message)
					elif chat_announce != 0:
						return await self.instance.chat(message, player.login)

			else:
				current_record = LocalRecord(
					map=self.instance.map_manager.current_map,
					player=player,
				)

			# Set details (score + cps times).
			current_record.score = score
			current_record.checkpoints = ','.join([str(cp) for cp in cps])

			# Add to list when it's a new record!
			if current_record.get_id() is None:
				self.current_records.append(current_record)

			# (Re)sort the record list.
			self.current_records.sort(key=lambda x: x.score)
			new_index = self.current_records.index(current_record) + 1

			if new_index == 1:
				map = next((m for m in self.instance.map_manager.maps if m.uid == self.instance.map_manager.current_map.uid), None)
				if map is not None:
					map.local = {'record_count': len(self.current_records), 'first_record': current_record}

		# Prepare messages.
		if previous_index is not None and (record_limit == 0 or previous_index <= record_limit):
			if new_index < previous_index:
				message = '$fff{}$z$s$0f3 gained the $fff{}.$0f3 Local Record: $fff\uf017 {}$0f3 ($fff{}.$0f3 $fff-{}$0f3).'.format(
					player.nickname, new_index, times.format_time(score), previous_index,
					times.format_time((previous_time - score))
				)
			else:
				message = '$fff{}$z$s$0f3 improved the $fff{}.$0f3 Local Record: $fff\uf017 {}$0f3 ($fff-{}$0f3).'.format(
					player.nickname, new_index, times.format_time(score),
					times.format_time((previous_time - score))
				)
		else:
			message = '$fff{}$z$s$0f3 drove the $fff{}.$0f3 Local Record: $fff\uf017 {}$0f3.'.format(
				player.nickname, new_index, times.format_time(score)
			)

		# Save to database (but don't wait for it).
		try:
			asyncio.ensure_future(current_record.save())
		except Exception as e:
			# To investigate #283.
			handle_exception(e, __name__, 'player_finish', extra_data={
				'own_records': current_records,
				'own_record': current_record
			})

		if self.widget is None:
			self.widget = LocalRecordsWidget(self)

		coros = [self.widget.display()]
		if record_limit == 0 or new_index <= record_limit:
			if chat_announce >= new_index:
				coros.append(self.instance.chat(message))
			elif chat_announce != 0:
				coros.append(self.instance.chat(message, player))
		await asyncio.gather(*coros)

		# Reload map referenced information
		asyncio.ensure_future(self.load_map_locals(map=self.instance.map_manager.current_map))