class TuxemonServer(): """Server class for multiplayer games. Creates a netaria server and synchronizes the local game with all client states. :param game: instance of the local game. :type game: core.control.Control object. :rtype: None :returns: None """ def __init__(self, game, server_name=None): self.game = game if not server_name: self.server_name = "Default Tuxemon Server" else: self.server_name = server_name self.network_events = [] self.listening = False self.interfaces = {} self.ips = [] # Handle users without networking support. if not networking: self.server = DummyNetworking() return self.server = NeteriaServer(Multiplayer(self), server_port=40081, server_name=self.server_name) def update(self): """Updates the server state with information sent from the clients :param None :type None :rtype: None :returns: None """ self.server_timestamp = datetime.now() for cuuid in self.server.registry: try: difference = self.server_timestamp - self.server.registry[cuuid]["ping_timestamp"] if difference.seconds > 15: logger.info("Client Disconnected. CUUID: " + str(cuuid)) event_data = {"type": "CLIENT_DISCONNECTED"} self.notify_client(cuuid, event_data) del self.server.registry[cuuid] return False except KeyError: self.server.registry[cuuid]["ping_timestamp"] = datetime.now() def server_event_handler(self, cuuid, event_data): """Handles events sent from the middleware that are legal. :param cuuid: Clients unique user identification number. :param event_data: Event information sent by client. :type cuuid: String :type event_data: Dictionary :rtype: None :returns: None """ # Only respond to the latest message of a given type if not "event_list" in self.server.registry[cuuid]: self.server.registry[cuuid]["event_list"] = {} elif not event_data["type"] in self.server.registry[cuuid]["event_list"]: self.server.registry[cuuid]["event_list"][event_data["type"]] = -1 elif event_data["event_number"] <= self.server.registry[cuuid]["event_list"][event_data["type"]]: return False else: self.server.registry[cuuid]["event_list"][event_data["type"]] = event_data["event_number"] if event_data["type"] == "PUSH_SELF": self.server.registry[cuuid]["sprite_name"] = event_data["sprite_name"] self.server.registry[cuuid]["map_name"] = event_data["map_name"] self.server.registry[cuuid]["char_dict"] = event_data["char_dict"] self.server.registry[cuuid]["ping_timestamp"] = datetime.now() self.notify_populate_client(cuuid, event_data) elif event_data["type"] == "PING": self.server.registry[cuuid]["ping_timestamp"] = datetime.now() elif event_data["type"] == "CLIENT_INTERACTION" or event_data["type"] == "CLIENT_RESPONSE": self.notify_client_interaction(cuuid, event_data) elif event_data["type"] == "CLIENT_KEYDOWN": if event_data["kb_key"] == "SHIFT": self.server.registry[cuuid]["char_dict"]["running"] = True self.notify_client(cuuid, event_data) elif event_data["kb_key"] == "CTRL": self.notify_client(cuuid, event_data) elif event_data["kb_key"] == "ALT": self.notify_client(cuuid, event_data) elif event_data["type"] == "CLIENT_KEYUP": if event_data["kb_key"] == "SHIFT": self.server.registry[cuuid]["char_dict"]["running"] = False self.notify_client(cuuid, event_data) elif event_data["kb_key"] == "CTRL": self.notify_client(cuuid, event_data) elif event_data["kb_key"] == "ALT": self.notify_client(cuuid, event_data) elif event_data["type"] == "CLIENT_START_BATTLE": self.server.registry[cuuid]["char_dict"]["running"] = False self.update_char_dict(cuuid, event_data["char_dict"]) self.server.registry[cuuid]["map_name"] = event_data["map_name"] self.notify_client(cuuid, event_data) else: self.update_char_dict(cuuid, event_data["char_dict"]) if "map_name" in event_data: self.server.registry[cuuid]["map_name"] = event_data["map_name"] self.notify_client(cuuid, event_data) def update_char_dict(self, cuuid, char_dict): """Updates registry with player updates. :param cuuid: Clients unique user identification number. :param char_dict: character dictionary :type cuuid: String :type event_data: String :rtype: None :returns: None """ for param in char_dict: self.server.registry[cuuid]["char_dict"][param] = char_dict[param] def notify_client(self, cuuid, event_data): """Updates all clients with player updates. :param cuuid: Clients unique user identification number. :param event_data: Notification flag information. :type cuuid: String :type event_data: String :rtype: None :returns: None """ cuuid = str(cuuid) event_data["type"] = "NOTIFY_" + event_data["type"] event_data["cuuid"] = cuuid for client_id in self.server.registry: # Don't notify a player that they themselves moved. if client_id == cuuid: continue # Notify client of the players new position. elif client_id != cuuid: self.server.notify(client_id, event_data) def notify_populate_client(self, cuuid, event_data): """Updates all clients with the details of the new client. :param cuuid: Clients unique user identification number. :param event_data: Event information sent by client. :type cuuid: String :type event_data: Dictionary :rtype: None :returns: None """ cuuid = str(cuuid) event_data["type"] = "NOTIFY_" + event_data["type"] event_data_1 = event_data for client_id in self.server.registry: # Don't notify a player that they themselves populated. if client_id == cuuid:continue elif client_id != cuuid: # Send the new client data to this client event_data_1["cuuid"] = cuuid self.server.notify(client_id, event_data_1) # Send this clients data to the new client char = self.server.registry[client_id] event_data_2 = {"type":event_data["type"], "cuuid": client_id, "event_number": event_data["event_number"], "sprite_name": char["sprite_name"], "map_name": char["map_name"], "char_dict": char["char_dict"] } self.server.notify(cuuid, event_data_2) def notify_client_interaction(self, cuuid, event_data): """Notify a client that another client has interacted with them. :param cuuid: Clients unique user identification number. :param event_data: Notification information. :type cuuid: String :type event_data: Dictionary :rtype: None :returns: None """ cuuid = str(cuuid) event_data["type"] = "NOTIFY_" + event_data["type"] client_id = event_data["target"] event_data["target"] = cuuid self.server.notify(client_id, event_data)
class TuxemonServer(): """Server class for multiplayer games. Creates a netaria server and synchronizes the local game with all client states. :param game: instance of the local game. :type game: core.control.Control object. :rtype: None :returns: None """ def __init__(self, game): self.game = game self.network_events = [] self.listening = False self.interfaces = {} self.ips = [] # Handle users without networking support. if not networking: self.server = DummyNetworking() return self.server = NeteriaServer(Multiplayer(self), server_port=40081) for device in netifaces.interfaces(): interface = netifaces.ifaddresses(device) try: self.interfaces[device] = interface[netifaces.AF_INET][0]['addr'] except KeyError: pass for interface in self.interfaces: ip = self.interfaces[interface] if ip == "127.0.0.1": continue else: self.ips.append(ip) def update(self): """Updates the server state with information sent from the clients :param None :type None :rtype: None :returns: None """ pass def server_event_handler(self, cuuid, event_data): """Handles events sent from the middleware that are legal. :param cuuid: Clients unique user identification number. :param event_data: Event information sent by client. :type cuuid: String :type event_data: Dictionary :rtype: None :returns: None """ # Only respond to the latest message of a given type if not "event_list" in self.server.registry[cuuid]: self.server.registry[cuuid]["event_list"] = {} elif not event_data["type"] in self.server.registry[cuuid]["event_list"]: self.server.registry[cuuid]["event_list"][event_data["type"]] = -1 elif event_data["event_number"] <= self.server.registry[cuuid]["event_list"][event_data["type"]]: return False else: self.server.registry[cuuid]["event_list"][event_data["type"]] = event_data["event_number"] if event_data["type"] == "PUSH_SELF": self.server.registry[cuuid]["sprite_name"] = event_data["sprite_name"] self.server.registry[cuuid]["map_name"] = event_data["map_name"] self.server.registry[cuuid]["char_dict"] = event_data["char_dict"] self.notify_populate_client(cuuid, event_data) elif event_data["type"] == "CLIENT_INTERACTION" or event_data["type"] == "CLIENT_RESPONSE": self.notify_client_interaction(cuuid, event_data) elif event_data["type"] == "CLIENT_KEYDOWN": if event_data["kb_key"] == "SHIFT": self.server.registry[cuuid]["char_dict"]["running"] = True self.notify_client(cuuid, event_data) elif event_data["kb_key"] == "CRTL": pass elif event_data["kb_key"] == "ALT": pass elif event_data["type"] == "CLIENT_KEYUP": if event_data["kb_key"] == "SHIFT": self.server.registry[cuuid]["char_dict"]["running"] = False self.notify_client(cuuid, event_data) elif event_data["kb_key"] == "CRTL": pass elif event_data["kb_key"] == "ALT": pass else: self.update_char_dict(cuuid, event_data["char_dict"]) if "map_name" in event_data: self.server.registry[cuuid]["map_name"] = event_data["map_name"] self.notify_client(cuuid, event_data) def update_char_dict(self, cuuid, char_dict): """Updates registry with player updates. :param cuuid: Clients unique user identification number. :param char_dict: character dictionary :type cuuid: String :type event_data: String :rtype: None :returns: None """ for param in char_dict: self.server.registry[cuuid]["char_dict"][param] = char_dict[param] def notify_client(self, cuuid, event_data): """Updates all clients with player updates. :param cuuid: Clients unique user identification number. :param event_data: Notification flag information. :type cuuid: String :type event_data: String :rtype: None :returns: None """ cuuid = str(cuuid) event_data["type"] = "NOTIFY_" + event_data["type"] event_data["cuuid"] = cuuid for client_id in self.server.registry: # Don't notify a player that they themselves moved. if client_id == cuuid: continue # Notify client of the players new position. elif client_id != cuuid: self.server.notify(client_id, event_data) def notify_populate_client(self, cuuid, event_data): """Updates all clients with the details of the new client. :param cuuid: Clients unique user identification number. :param event_data: Event information sent by client. :type cuuid: String :type event_data: Dictionary :rtype: None :returns: None """ cuuid = str(cuuid) event_data["type"] = "NOTIFY_" + event_data["type"] event_data_1 = event_data for client_id in self.server.registry: # Don't notify a player that they themselves populated. if client_id == cuuid:continue elif client_id != cuuid: # Send the new client data to this client event_data_1["cuuid"] = cuuid self.server.notify(client_id, event_data_1) # Send this clients data to the new client char = self.server.registry[client_id] event_data_2 = {"type":event_data["type"], "cuuid": client_id, "event_number": event_data["event_number"], "sprite_name": char["sprite_name"], "map_name": char["map_name"], "char_dict": char["char_dict"] } self.server.notify(cuuid, event_data_2) def notify_client_interaction(self, cuuid, event_data): """Notify a client that another client has interacted with them. :param cuuid: Clients unique user identification number. :param event_data: Notification information. :type cuuid: String :type event_data: Dictionary :rtype: None :returns: None """ cuuid = str(cuuid) event_data["type"] = "NOTIFY_" + event_data["type"] client_id = event_data["target"] event_data["target"] = cuuid self.server.notify(client_id, event_data)