Beispiel #1
0
	def _setup_client(self):
		"""Initialize connection object. Does not connect to the server."""
		server_address = [NETWORK.SERVER_ADDRESS, NETWORK.SERVER_PORT]
		client_address = None
		client_port = parse_port(horizons.globals.fife.get_uh_setting("NetworkPort"))

		if NETWORK.CLIENT_ADDRESS is not None and client_port > 0:
			client_address = [NETWORK.CLIENT_ADDRESS, client_port]
		try:
			self._connection = Connection(self.process_async_packet, server_address, client_address)
		except NetworkException as e:
			raise RuntimeError(e)
	def _setup_client(self):
		"""Initialize connection object. Does not connect to the server."""
		server_address = [NETWORK.SERVER_ADDRESS, NETWORK.SERVER_PORT]
		client_address = None
		client_port = parse_port(horizons.globals.fife.get_uh_setting("NetworkPort"))

		if NETWORK.CLIENT_ADDRESS is not None and client_port > 0:
			client_address = [NETWORK.CLIENT_ADDRESS, client_port]
		try:
			self._connection = Connection(self.process_async_packet, server_address, client_address)
		except NetworkException as e:
			raise RuntimeError(e)
class NetworkInterface(object):
	"""Interface for low level networking"""
	__metaclass__ = ManualConstructionSingleton

	log = logging.getLogger("network")

	PING_INTERVAL = 0.5 # ping interval in seconds

	def __init__(self):
		self._mode = None
		self.sid = None
		self.capabilities = None
		self._game = None

		message_types = ('lobbygame_chat', 'lobbygame_join', 'lobbygame_leave',
		                 'lobbygame_terminate', 'lobbygame_toggleready',
		                 'lobbygame_changename', 'lobbygame_kick',
		                 'lobbygame_changecolor', 'lobbygame_state',
		                 'lobbygame_starts', 'game_starts',
		                 'game_details_changed', 'game_prepare', 'error')

		self._messagebus = SimpleMessageBus(message_types)
		self.subscribe = self._messagebus.subscribe
		self.unsubscribe = self._messagebus.unsubscribe
		self.broadcast = self._messagebus.broadcast

		# create a game_details_changed callback
		for t in ('lobbygame_join', 'lobbygame_leave', 'lobbygame_changename',
		          'lobbygame_changecolor', 'lobbygame_toggleready'):
			self.subscribe(t, lambda *a, **b: self.broadcast("game_details_changed"))

		self.subscribe("lobbygame_starts", self._on_lobbygame_starts)
		self.subscribe('lobbygame_changename',  self._on_change_name)
		self.subscribe('lobbygame_changecolor', self._on_change_color)

		self.received_packets = []

		ExtScheduler().add_new_object(self.ping, self, self.PING_INTERVAL, -1)

		self._client_data = ClientData()
		self._setup_client()

	# Connection

	def _setup_client(self):
		"""Initialize connection object. Does not connect to the server."""
		server_address = [NETWORK.SERVER_ADDRESS, NETWORK.SERVER_PORT]
		client_address = None
		client_port = parse_port(horizons.globals.fife.get_uh_setting("NetworkPort"))

		if NETWORK.CLIENT_ADDRESS is not None and client_port > 0:
			client_address = [NETWORK.CLIENT_ADDRESS, client_port]
		try:
			self._connection = Connection(self.process_async_packet, server_address, client_address)
		except NetworkException as e:
			raise RuntimeError(e)

	@property
	def is_connected(self):
		return self._connection.is_connected

	def connect(self):
		"""
		@throws: NetworkError
		"""
		try:
			self._connection.connect()

			# wait for session id
			packet = self._connection.receive_packet(packets.server.cmd_session)

			self.sid = packet[1].sid
			self.capabilities = packet[1].capabilities
			self._mode = ClientMode.Server
			self.log.debug("[CONNECT] done (session=%s)" % (self.sid))
			self._set_client_language()
		except NetworkException as e:
			self.disconnect()
			raise e

	def disconnect(self):
		self._mode = None
		self._connection.disconnect()

	def ping(self):
		"""calls _connection.ping until all packets are received"""
		if self.is_connected:
			try:
				while self._connection.ping(): # ping receives packets
					pass
			except NetworkException as e:
				self._handle_exception(e)

	def network_data_changed(self):
		"""Call in case constants like client address or client port changed.

		@throws RuntimeError in case of invalid data or an NetworkException forwarded from connect
		"""
		if self.is_connected:
			self.disconnect()
		self._setup_client()

	def _set_client_language(self):
		lang = LANGUAGENAMES.get_by_value(horizons.globals.fife.get_uh_setting("Language"))
		if lang:
			return self.set_props({'lang': lang})

	def send_packet(self, packet, *args, **kwargs):
		"""
		"""
		if self._mode is ClientMode.Game:
			packet = packets.client.game_data(packet)
		packet.sid = self.sid

		self._connection.send_packet(packet)

	# Game related

	@property
	def is_joined(self):
		return self._game is not None

	def game2mpgame(self, game):
		return MPGame(game, self)

	def get_game(self):
		game = self._game
		if game is None:
			return None
		return self.game2mpgame(game)

	def set_props(self, props):
		try:
			self.log.debug("[SETPROPS]")
			self._assert_connection()
			self.send_packet(packets.client.cmd_sessionprops(props))
			self._connection.receive_packet(packets.cmd_ok)
		except NetworkException as e:
			self._handle_exception(e)
			return False
		return True

	def creategame(self, mapname, maxplayers, gamename, maphash="", password=""):
		self.log.debug("[CREATEGAME] %s(h=%s), %s, %s, %s", mapname, maphash, maxplayers, gamename)
		try:
			self._assert_connection()
			self.log.debug("[CREATE] mapname=%s maxplayers=%d" % (mapname, maxplayers))
			self.send_packet(packets.client.cmd_creategame(
				clientver=self._client_data.version,
				clientid=self._client_data.id,
				playername=self._client_data.name,
				playercolor=self._client_data.color,
				gamename=gamename,
				mapname=mapname,
				maxplayers=maxplayers,
				maphash=maphash,
				password=password
			))
			packet = self._connection.receive_packet(packets.server.data_gamestate)
			game = self._game = packet[1].game
		except NetworkException as e:
			self._handle_exception(e)
			return None
		return self.game2mpgame(game)

	def joingame(self, uuid, password="", fetch=False):
		"""Join a game with a certain uuid"""
		i = 2
		try:
			while i < 10: # FIXME: try 10 different names and colors
				try:
					self._joingame(uuid, password, fetch)
					return True
				except CommandError as e:
					self.log.debug("NetworkInterface: failed to join")
					if 'name' in e.message:
						self.change_name(self._client_data.name + unicode(i), save=False )
					elif 'color' in e.message:
						self.change_color(self._client_data.color + i, save=False)
					else:
						raise
				i += 1
			self._joingame(uuid, password, fetch)
		except NetworkException as e:
			self._handle_exception(e)
		return False

	def _joingame(self, uuid, password="", fetch=False):
		self._assert_connection()
		self.log.debug("[JOIN] %s" % (uuid))
		self.send_packet(packets.client.cmd_joingame(
			uuid=uuid,
			clientver=self._client_data.version,
			clientid=self._client_data.id,
			playername=self._client_data.name,
			playercolor=self._client_data.color,
			password=password,
			fetch=fetch
		))
		packet = self._connection.receive_packet(packets.server.data_gamestate)
		self._game = packet[1].game
		return self._game

	def leavegame(self):
		try:
			self._assert_connection()
			self._assert_lobby()
			self.log.debug("[LEAVE]")
			self.send_packet(packets.client.cmd_leavegame())
			self._connection.receive_packet(packets.cmd_ok)
			self._game = None
		except NetworkException as e:
			fatal = self._handle_exception(e)
			if fatal:
				return False
		return True

	def chat(self, message):
		try:
			self._assert_connection()
			self._assert_lobby()
			self.log.debug("[CHAT] %s" % (message))
			self.send_packet(packets.client.cmd_chatmsg(message))
		except NetworkException as e:
			self._handle_exception(e)
			return False
		return True

	def get_active_games(self, only_this_version_allowed=False):
		"""Returns a list of active games or None on fatal error"""
		ret_mp_games = []
		try:
			self._assert_connection()
			self.log.debug("[LIST]")
			version = self._client_data.version if only_this_version_allowed else -1
			self.send_packet(packets.client.cmd_listgames(version))
			packet = self._connection.receive_packet(packets.server.data_gameslist)
			games = packet[1].games
		except NetworkException as e:
			fatal = self._handle_exception(e)
			return [] if not fatal else None
		for game in games:
			ret_mp_games.append(self.game2mpgame(game))
			self.log.debug("NetworkInterface: found active game %s", game.mapname)
		return ret_mp_games

	def toggle_ready(self):
		self.log.debug("[TOGGLEREADY]")
		self.send_packet(packets.client.cmd_toggleready())

	def kick(self, player_sid):
		self.log.debug("[KICK]")
		self.send_packet(packets.client.cmd_kickplayer(player_sid))


	# Client

	def get_client_name(self):
		return self._client_data.name

	def get_client_color(self):
		return self._client_data.color

	def get_clientversion(self):
		return self._client_data.version

	def change_name(self, new_name, save=True):
		if save:
			horizons.globals.fife.set_uh_setting("Nickname", new_name)
			horizons.globals.fife.save_settings()

		try:
			if self._client_data.name == new_name:
				return True
			self.log.debug("[CHANGENAME] %s" % (new_name))
			if self._mode is None or self._game is None:
				self._client_data.name = new_name
				return
			self.send_packet(packets.client.cmd_changename(new_name))
		except NetworkException as e:
			self._handle_exception(e)

	def _on_change_name(self, game, plold, plnew, myself):
		self.log.debug("[ONCHANGENAME] %s -> %s" % (plold.name, plnew.name))
		if myself:
			self._client_data.name = plnew.name

	def change_color(self, new_color, save=True):
		new_color %= len(set(Color))

		if save:
			horizons.globals.fife.set_uh_setting("ColorID", new_color)
			horizons.globals.fife.save_settings()

		try:
			if self._client_data.color == new_color:
				return
			self.log.debug("[CHANGECOLOR] %s" % (new_color))
			if self._mode is None or self._game is None:
				self._client_data.color = new_color
				return
			self.send_packet(packets.client.cmd_changecolor(new_color))
		except NetworkException as e:
			self._handle_exception(e)

	def _on_change_color(self, game, plold, plnew, myself):
		self.log.debug("[ONCHANGECOLOR] %s: %s -> %s" % (plnew.name, plold.color, plnew.color))
		if myself:
			self._client_data.color = plnew.color

	# Helper functions, event callbacks, packet handling

	def receive_all(self):
		"""
		Returns list of all packets, that have arrived until now (since the last call)
		@return: list of packets
		"""
		try:
			while self._connection.ping(): # ping receives packets
				pass
		except NetworkException as e:
			self.log.debug("ping in receive_all failed: "+str(e))
			self._handle_exception(e)
			raise CommandError(e)
		ret_list = self.received_packets
		self.received_packets = []
		return ret_list

	def _handle_exception(self, e):
		try:
			raise e
		except FatalError as e:
			self.broadcast("error", e, fatal=True)
			self.disconnect()
			return True
		except NetworkException as e:
			self.broadcast("error", e, fatal=False)
			return False

	def process_async_packet(self, packet):
		"""
		return True if packet was processed successfully
		return False if packet should be queue
		"""
		if packet is None:
			return True
		if isinstance(packet[1], packets.server.cmd_chatmsg):
			# ignore packet if we are not a game lobby
			if self._game is None:
				return True
			self.broadcast("lobbygame_chat", self._game, packet[1].playername, packet[1].chatmsg)
		elif isinstance(packet[1], packets.server.data_gamestate):
			# ignore packet if we are not a game lobby
			if self._game is None:
				return True
			self.broadcast("lobbygame_state", self._game, packet[1].game)

			oldplayers = list(self._game.players)
			self._game = packet[1].game

			# calculate changeset
			for pnew in self._game.players:
				found = None
				for pold in oldplayers:
					if pnew.sid == pold.sid:
						found = pold
						myself = (pnew.sid == self.sid)
						if pnew.name != pold.name:
							self.broadcast("lobbygame_changename", self._game, pold, pnew, myself)
						if pnew.color != pold.color:
							self.broadcast("lobbygame_changecolor", self._game, pold, pnew, myself)
						if pnew.ready != pold.ready:
							self.broadcast("lobbygame_toggleready", self._game, pold, pnew, myself)
						break
				if found is None:
					self.broadcast("lobbygame_join", self._game, pnew)
				else:
					oldplayers.remove(found)
			for pold in oldplayers:
				self.broadcast("lobbygame_leave", self._game, pold)
			return True
		elif isinstance(packet[1], packets.server.cmd_preparegame):
			# ignore packet if we are not a game lobby
			if self._game is None:
				return True
			self._on_game_prepare()
		elif isinstance(packet[1], packets.server.cmd_startgame):
			# ignore packet if we are not a game lobby
			if self._game is None:
				return True
			self._on_game_start()
		elif isinstance(packet[1], packets.client.game_data):
			self.log.debug("[GAMEDATA] from %s" % (packet[0].address))
			self._on_game_data(packet[1].data)
		elif isinstance(packet[1], packets.server.cmd_kickplayer):
			player = packet[1].player
			game = self._game
			myself = (player.sid == self.sid)
			if myself:
				# this will destroy self._game
				self._assert_connection()
				self._assert_lobby()
				self.log.debug("[LEAVE]")
				self._game = None
			self.broadcast("lobbygame_kick", game, player, myself)

		return False

	def _on_game_prepare(self):
		self.log.debug("[GAMEPREPARE]")
		self._game.state = Game.State.Prepare
		self.broadcast("lobbygame_starts", self._game)
		self.send_packet(packets.client.cmd_preparedgame())

	def _on_game_start(self):
		self.log.debug("[GAMESTART]")
		self._game.state = Game.State.Running
		self._mode = ClientMode.Game
		self.broadcast("game_starts", self._game)

	def _on_lobbygame_starts(self, game):
		game = self.get_game()
		self.broadcast("game_prepare", game)

	def _on_game_data(self, data):
		self.received_packets.append(data)

	def _assert_connection(self):
		if self._mode is None:
			raise network.NotConnected()
		if self._mode is not ClientMode.Server:
			raise network.NotInServerMode("We are not in server mode")

	def _assert_lobby(self):
		if self._game is None:
			raise network.NotInGameLobby("We are not in a game lobby")
class NetworkInterface(object):
    """Interface for low level networking"""
    __metaclass__ = ManualConstructionSingleton

    log = logging.getLogger("network")

    PING_INTERVAL = 0.5  # ping interval in seconds

    def __init__(self):
        self._mode = None
        self.sid = None
        self.capabilities = None
        self._game = None

        message_types = ('lobbygame_chat', 'lobbygame_join', 'lobbygame_leave',
                         'lobbygame_terminate', 'lobbygame_toggleready',
                         'lobbygame_changename', 'lobbygame_kick',
                         'lobbygame_changecolor', 'lobbygame_state',
                         'lobbygame_starts', 'game_starts',
                         'game_details_changed', 'game_prepare', 'error')

        self._messagebus = SimpleMessageBus(message_types)
        self.subscribe = self._messagebus.subscribe
        self.unsubscribe = self._messagebus.unsubscribe
        self.broadcast = self._messagebus.broadcast
        self.discard = self._messagebus.discard

        # create a game_details_changed callback
        for t in ('lobbygame_join', 'lobbygame_leave', 'lobbygame_changename',
                  'lobbygame_changecolor', 'lobbygame_toggleready'):
            self.subscribe(
                t, lambda *a, **b: self.broadcast("game_details_changed"))

        self.subscribe("lobbygame_starts", self._on_lobbygame_starts)
        self.subscribe('lobbygame_changename', self._on_change_name)
        self.subscribe('lobbygame_changecolor', self._on_change_color)

        self.received_packets = []

        ExtScheduler().add_new_object(self.ping, self, self.PING_INTERVAL, -1)

        self._client_data = ClientData()
        self._setup_client()

    # Connection

    def _setup_client(self):
        """Initialize connection object. Does not connect to the server."""
        server_address = [NETWORK.SERVER_ADDRESS, NETWORK.SERVER_PORT]
        client_address = None
        client_port = parse_port(
            horizons.globals.fife.get_uh_setting("NetworkPort"))

        if NETWORK.CLIENT_ADDRESS is not None and client_port > 0:
            client_address = [NETWORK.CLIENT_ADDRESS, client_port]
        try:
            self._connection = Connection(self.process_async_packet,
                                          server_address, client_address)
        except NetworkException as e:
            raise RuntimeError(e)

    @property
    def is_connected(self):
        return self._connection.is_connected

    def connect(self):
        """
		@throws: NetworkError
		"""
        try:
            self._connection.connect()

            # wait for session id
            packet = self._connection.receive_packet(
                packets.server.cmd_session)

            self.sid = packet[1].sid
            self.capabilities = packet[1].capabilities
            self._mode = ClientMode.Server
            self.log.debug("[CONNECT] done (session=%s)" % (self.sid))
            self._set_client_language()
        except NetworkException as e:
            self.disconnect()
            raise e

    def disconnect(self):
        self._mode = None
        self._connection.disconnect()

    def ping(self):
        """calls _connection.ping until all packets are received"""
        if self.is_connected:
            try:
                while self._connection.ping():  # ping receives packets
                    pass
            except NetworkException as e:
                self._handle_exception(e)

    def network_data_changed(self):
        """Call in case constants like client address or client port changed.

		@throws RuntimeError in case of invalid data or an NetworkException forwarded from connect
		"""
        if self.is_connected:
            self.disconnect()
        self._setup_client()

    def _set_client_language(self):
        lang = LANGUAGENAMES.get_by_value(
            horizons.globals.fife.get_uh_setting("Language"))
        if lang:
            return self.set_props({'lang': lang})

    def send_packet(self, packet, *args, **kwargs):
        """
		"""
        if self._mode is ClientMode.Game:
            packet = packets.client.game_data(packet)
        packet.sid = self.sid

        self._connection.send_packet(packet)

    # Game related

    @property
    def is_joined(self):
        return self._game is not None

    def game2mpgame(self, game):
        return MPGame(game, self)

    def get_game(self):
        game = self._game
        if game is None:
            return None
        return self.game2mpgame(game)

    def set_props(self, props):
        try:
            self.log.debug("[SETPROPS]")
            self._assert_connection()
            self.send_packet(packets.client.cmd_sessionprops(props))
            self._connection.receive_packet(packets.cmd_ok)
        except NetworkException as e:
            self._handle_exception(e)
            return False
        return True

    def creategame(self,
                   mapname,
                   maxplayers,
                   gamename,
                   maphash="",
                   password=""):
        self.log.debug("[CREATEGAME] %s(h=%s), %s, %s, %s", mapname, maphash,
                       maxplayers, gamename)
        try:
            self._assert_connection()
            self.log.debug("[CREATE] mapname=%s maxplayers=%d" %
                           (mapname, maxplayers))
            self.send_packet(
                packets.client.cmd_creategame(
                    clientver=self._client_data.version,
                    clientid=self._client_data.id,
                    playername=self._client_data.name,
                    playercolor=self._client_data.color,
                    gamename=gamename,
                    mapname=mapname,
                    maxplayers=maxplayers,
                    maphash=maphash,
                    password=password))
            packet = self._connection.receive_packet(
                packets.server.data_gamestate)
            game = self._game = packet[1].game
        except NetworkException as e:
            self._handle_exception(e)
            return None
        return self.game2mpgame(game)

    def joingame(self, uuid, password="", fetch=False):
        """Join a game with a certain uuid"""
        i = 2
        try:
            while i < 10:  # FIXME: try 10 different names and colors
                try:
                    self._joingame(uuid, password, fetch)
                    return True
                except CommandError as e:
                    self.log.debug("NetworkInterface: failed to join")
                    if 'name' in e.message:
                        self.change_name(self._client_data.name + unicode(i),
                                         save=False)
                    elif 'color' in e.message:
                        self.change_color(self._client_data.color + i,
                                          save=False)
                    else:
                        raise
                i += 1
            self._joingame(uuid, password, fetch)
        except NetworkException as e:
            self._handle_exception(e)
        return False

    def _joingame(self, uuid, password="", fetch=False):
        self._assert_connection()
        self.log.debug("[JOIN] %s" % (uuid))
        self.send_packet(
            packets.client.cmd_joingame(uuid=uuid,
                                        clientver=self._client_data.version,
                                        clientid=self._client_data.id,
                                        playername=self._client_data.name,
                                        playercolor=self._client_data.color,
                                        password=password,
                                        fetch=fetch))
        packet = self._connection.receive_packet(packets.server.data_gamestate)
        self._game = packet[1].game
        return self._game

    def leavegame(self):
        try:
            self._assert_connection()
            self._assert_lobby()
            self.log.debug("[LEAVE]")
            self.send_packet(packets.client.cmd_leavegame())
            self._connection.receive_packet(packets.cmd_ok)
            self._game = None
        except NetworkException as e:
            fatal = self._handle_exception(e)
            if fatal:
                return False
        return True

    def chat(self, message):
        try:
            self._assert_connection()
            self._assert_lobby()
            self.log.debug("[CHAT] %s" % (message))
            self.send_packet(packets.client.cmd_chatmsg(message))
        except NetworkException as e:
            self._handle_exception(e)
            return False
        return True

    def get_active_games(self):
        """Returns a list of active games or None on fatal error"""
        ret_mp_games = []
        try:
            self._assert_connection()
            self.log.debug("[LIST]")
            version = self._client_data.version
            self.send_packet(packets.client.cmd_listgames(version))
            packet = self._connection.receive_packet(
                packets.server.data_gameslist)
            games = packet[1].games
        except NetworkException as e:
            fatal = self._handle_exception(e)
            return [] if not fatal else None
        for game in games:
            ret_mp_games.append(self.game2mpgame(game))
            self.log.debug("NetworkInterface: found active game %s",
                           game.mapname)
        return ret_mp_games

    def toggle_ready(self):
        self.log.debug("[TOGGLEREADY]")
        self.send_packet(packets.client.cmd_toggleready())

    def kick(self, player_sid):
        self.log.debug("[KICK]")
        self.send_packet(packets.client.cmd_kickplayer(player_sid))

    # Client

    def get_client_name(self):
        return self._client_data.name

    def get_client_color(self):
        return self._client_data.color

    def get_clientversion(self):
        return self._client_data.version

    def change_name(self, new_name, save=True):
        if save:
            horizons.globals.fife.set_uh_setting("Nickname", new_name)
            horizons.globals.fife.save_settings()

        try:
            if self._client_data.name == new_name:
                return True
            self.log.debug("[CHANGENAME] %s" % (new_name))
            if self._mode is None or self._game is None:
                self._client_data.name = new_name
                return
            self.send_packet(packets.client.cmd_changename(new_name))
        except NetworkException as e:
            self._handle_exception(e)

    def _on_change_name(self, game, plold, plnew, myself):
        self.log.debug("[ONCHANGENAME] %s -> %s" % (plold.name, plnew.name))
        if myself:
            self._client_data.name = plnew.name

    def change_color(self, new_color, save=True):
        new_color %= len(set(Color))

        if save:
            horizons.globals.fife.set_uh_setting("ColorID", new_color)
            horizons.globals.fife.save_settings()

        try:
            if self._client_data.color == new_color:
                return
            self.log.debug("[CHANGECOLOR] %s" % (new_color))
            if self._mode is None or self._game is None:
                self._client_data.color = new_color
                return
            self.send_packet(packets.client.cmd_changecolor(new_color))
        except NetworkException as e:
            self._handle_exception(e)

    def _on_change_color(self, game, plold, plnew, myself):
        self.log.debug("[ONCHANGECOLOR] %s: %s -> %s" %
                       (plnew.name, plold.color, plnew.color))
        if myself:
            self._client_data.color = plnew.color

    # Helper functions, event callbacks, packet handling

    def receive_all(self):
        """
		Returns list of all packets, that have arrived until now (since the last call)
		@return: list of packets
		"""
        try:
            while self._connection.ping():  # ping receives packets
                pass
        except NetworkException as e:
            self.log.debug("ping in receive_all failed: " + str(e))
            self._handle_exception(e)
            raise CommandError(e)
        ret_list = self.received_packets
        self.received_packets = []
        return ret_list

    def _handle_exception(self, e):
        try:
            raise e
        except FatalError as e:
            self.broadcast("error", e, fatal=True)
            self.disconnect()
            return True
        except NetworkException as e:
            self.broadcast("error", e, fatal=False)
            return False

    def process_async_packet(self, packet):
        """
		return True if packet was processed successfully
		return False if packet should be queue
		"""
        if packet is None:
            return True
        if isinstance(packet[1], packets.server.cmd_chatmsg):
            # ignore packet if we are not a game lobby
            if self._game is None:
                return True
            self.broadcast("lobbygame_chat", self._game, packet[1].playername,
                           packet[1].chatmsg)
        elif isinstance(packet[1], packets.server.data_gamestate):
            # ignore packet if we are not a game lobby
            if self._game is None:
                return True
            self.broadcast("lobbygame_state", self._game, packet[1].game)

            oldplayers = list(self._game.players)
            self._game = packet[1].game

            # calculate changeset
            for pnew in self._game.players:
                found = None
                for pold in oldplayers:
                    if pnew.sid == pold.sid:
                        found = pold
                        myself = (pnew.sid == self.sid)
                        if pnew.name != pold.name:
                            self.broadcast("lobbygame_changename", self._game,
                                           pold, pnew, myself)
                        if pnew.color != pold.color:
                            self.broadcast("lobbygame_changecolor", self._game,
                                           pold, pnew, myself)
                        if pnew.ready != pold.ready:
                            self.broadcast("lobbygame_toggleready", self._game,
                                           pold, pnew, myself)
                        break
                if found is None:
                    self.broadcast("lobbygame_join", self._game, pnew)
                else:
                    oldplayers.remove(found)
            for pold in oldplayers:
                self.broadcast("lobbygame_leave", self._game, pold)
            return True
        elif isinstance(packet[1], packets.server.cmd_preparegame):
            # ignore packet if we are not a game lobby
            if self._game is None:
                return True
            self._on_game_prepare()
        elif isinstance(packet[1], packets.server.cmd_startgame):
            # ignore packet if we are not a game lobby
            if self._game is None:
                return True
            self._on_game_start()
        elif isinstance(packet[1], packets.client.game_data):
            self.log.debug("[GAMEDATA] from %s" % (packet[0].address))
            self._on_game_data(packet[1].data)
        elif isinstance(packet[1], packets.server.cmd_kickplayer):
            player = packet[1].player
            game = self._game
            myself = (player.sid == self.sid)
            if myself:
                # this will destroy self._game
                self._assert_connection()
                self._assert_lobby()
                self.log.debug("[LEAVE]")
                self._game = None
            self.broadcast("lobbygame_kick", game, player, myself)

        return False

    def _on_game_prepare(self):
        self.log.debug("[GAMEPREPARE]")
        self._game.state = Game.State.Prepare
        self.broadcast("lobbygame_starts", self._game)
        self.send_packet(packets.client.cmd_preparedgame())

    def _on_game_start(self):
        self.log.debug("[GAMESTART]")
        self._game.state = Game.State.Running
        self._mode = ClientMode.Game
        self.broadcast("game_starts", self._game)

    def _on_lobbygame_starts(self, game):
        game = self.get_game()
        self.broadcast("game_prepare", game)

    def _on_game_data(self, data):
        self.received_packets.append(data)

    def _assert_connection(self):
        if self._mode is None:
            raise network.NotConnected()
        if self._mode is not ClientMode.Server:
            raise network.NotInServerMode("We are not in server mode")

    def _assert_lobby(self):
        if self._game is None:
            raise network.NotInGameLobby("We are not in a game lobby")