class TuxemonClient(): """Client class for multiplayer games. Creates a netaria client and synchronizes the local game with the host state. :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.available_games = [] self.server_list = [] self.selected_game = None self.enable_join_multiplayer = False self.wait_broadcast = 0 # Used to delay autodiscover broadcast. self.wait_delay = 0.25 # Delay used in autodiscover broadcast. self.join_self = False # Default False. Set True for testing on one device. self.populated = False self.listening = False self.event_list = {} self.ping_time = 2 # Handle users without networking support. if not networking: self.client = DummyNetworking() return self.client = NeteriaClient(server_port=40081) self.client.registry = {} def update(self, time_delta): """Updates the client and local game state with information sent from the server :param time_delta: Time since last frame. :type time_delta: float :rtype: None :returns: None """ if self.enable_join_multiplayer: self.join_multiplayer(time_delta) if self.client.registered and not self.populated: self.game.isclient = True self.game.current_state.multiplayer_join_success_menu.text = ["Success!"] self.populate_player() if self.ping_time >= 2: self.ping_time = 0 self.client_alive() else: self.ping_time += time_delta self.check_notify() def check_notify(self): """Checks for notify events sent from the server and updates the local client registry to reflect the updated information. :param: None :rtype: None :returns: None """ for euuid, event_data in self.client.event_notifies.items(): if event_data["type"] == "NOTIFY_CLIENT_DISCONNECTED": del self.client.registry[event_data["cuuid"]] del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_PUSH_SELF": if not event_data["cuuid"] in self.client.registry: self.client.registry[str(event_data["cuuid"])]={} sprite = populate_client(event_data["cuuid"], event_data, self.game, self.client.registry) update_client(sprite, event_data["char_dict"], self.game) del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_MOVE_START": direction = str(event_data["direction"]) sprite = self.client.registry[event_data["cuuid"]]["sprite"] sprite.facing = direction for d in sprite.direction: if sprite.direction[d]: sprite.direction[d] = False sprite.direction[direction] = True del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_MOVE_COMPLETE": sprite = self.client.registry[event_data["cuuid"]]["sprite"] sprite.final_move_dest = event_data["char_dict"]["tile_pos"] for d in sprite.direction: if sprite.direction[d]: sprite.direction[d] = False del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_MAP_UPDATE": self.update_client_map(event_data["cuuid"], event_data) del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_KEYDOWN": sprite = self.client.registry[event_data["cuuid"]]["sprite"] if event_data["kb_key"] == "SHIFT": sprite.running = True del self.client.event_notifies[euuid] elif event_data["kb_key"] == "CTRL": del self.client.event_notifies[euuid] elif event_data["kb_key"] == "ALT": del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_KEYUP": sprite = self.client.registry[event_data["cuuid"]]["sprite"] if event_data["kb_key"] == "SHIFT": sprite.running = False del self.client.event_notifies[euuid] elif event_data["kb_key"] == "CTRL": del self.client.event_notifies[euuid] elif event_data["kb_key"] == "ALT": del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_FACING": sprite = self.client.registry[event_data["cuuid"]]["sprite"] if not sprite.moving: sprite.facing = event_data["char_dict"]["facing"] del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_INTERACTION": world = self.game.get_state_name("WorldState") if not world: return world.handle_interaction(event_data, self.client.registry) del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_START_BATTLE": sprite = self.client.registry[event_data["cuuid"]]["sprite"] sprite.running = False sprite.final_move_dest = event_data["char_dict"]["tile_pos"] for d in sprite.direction: if sprite.direction[d]: sprite.direction[d] = False del self.client.event_notifies[euuid] def join_multiplayer(self, time_delta): """Joins the client to the selected server. :param time_delta: Time since last frame. :type time_delta: float :rtype: None :returns: None """ # Don't allow player to join another game if they are hosting. if self.game.ishost: self.enable_join_multiplayer = False return False # If we have already joined a game end the loop. if self.client.registered: self.enable_join_multiplayer = False return False # Join a game if we have already selected one. if self.selected_game: self.client.register(self.selected_game) # Once per second send a server discovery packet. if self.wait_broadcast >= self.wait_delay: self.update_multiplayer_list() self.wait_broadcast = 0 else: self.wait_broadcast += time_delta def update_multiplayer_list(self): """Sends a broadcast to 'ping' all servers on the local network. Once a server responds it will verify that the server is not hosted by the client who sent the ping. Once a server has been identified it adds it to self.available_games. :param None: :rtype: None :returns: None """ self.client.autodiscover(autoregister=False) # Logic to prevent joining your own game as a client. if self.client.discovered_servers: for ip, port in self.client.discovered_servers: host = (ip, port) host_name = self.client.discovered_servers[host][1] try: for ipa, porta in self.available_games: hosta = (ipa, porta) if hosta == host: logger.info('Game already in list, skipping.') return False except KeyError: pass # Populate list of detected servers self.available_games.append(host) self.server_list.append(host_name) def populate_player(self, event_type="PUSH_SELF"): """Sends client character to the server. :param None :rtype: None :returns: None """ if not event_type in self.event_list: self.event_list[event_type] = 0 pd = prepare.player1.__dict__ map_name = self.game.get_map_name() event_data = {"type": event_type, "event_number": self.event_list[event_type], "sprite_name": pd["sprite_name"], "map_name": map_name, "char_dict": { "tile_pos": pd["tile_pos"], "name": pd["name"], "facing": pd["facing"], #"monsters": pd["monsters"], #"inventory": pd["inventory"] } } self.event_list[event_type] +=1 self.client.event(event_data) self.populated = True def update_player(self, direction, event_type="CLIENT_MAP_UPDATE"): """Sends client's current map and location to the server. :param direction: Facing/Movement direction of clients character :param event_type: Event type sent to server used for event_legal() and event_execute() functions in middleware. :type direction: String :type type: String :rtype: None :returns: None """ if not event_type in self.event_list: self.event_list[event_type] = 0 pd = prepare.player1.__dict__ map_name = self.game.get_map_name() event_data = {"type": event_type, "event_number": self.event_list[event_type], "map_name": map_name, "direction": direction, "char_dict": {"tile_pos": pd["tile_pos"] } } self.event_list[event_type] +=1 self.client.event(event_data) def set_key_condition(self, event): """Sends server information about a key condition being set or that an interaction has occurred. :param event: Pygame key event :type event: Dictionary :rtype: None :returns: None """ if self.game.current_state != self.game.get_state_name("WorldState"): return False event_type = None kb_key = None if event.type == pg.KEYDOWN: event_type = "CLIENT_KEYDOWN" if event.key == pg.K_LSHIFT or event.key == pg.K_RSHIFT: kb_key = "SHIFT" elif event.key == pg.K_LCTRL or event.key == pg.K_RCTRL: kb_key = "CTRL" elif event.key == pg.K_LALT or event.key == pg.K_RALT: kb_key = "ALT" elif event.key == pg.K_UP: kb_key = "up" elif event.key == pg.K_DOWN: kb_key = "down" elif event.key == pg.K_LEFT: kb_key = "left" elif event.key == pg.K_RIGHT: kb_key = "right" if event.type == pg.KEYUP: event_type = "CLIENT_KEYUP" if event.key == pg.K_LSHIFT or event.key == pg.K_RSHIFT: kb_key = "SHIFT" elif event.key == pg.K_LCTRL or event.key == pg.K_RCTRL: kb_key = "CTRL" elif event.key == pg.K_LALT or event.key == pg.K_RALT: kb_key = "ALT" elif event.key == pg.K_UP: kb_key = "up" elif event.key == pg.K_DOWN: kb_key = "down" elif event.key == pg.K_LEFT: kb_key = "left" elif event.key == pg.K_RIGHT: kb_key = "right" if kb_key == "up" or kb_key == "down" or kb_key == "left" or kb_key == "right": event_type = "CLIENT_FACING" if not event_type in self.event_list: self.event_list[event_type] = 0 if event_type and kb_key: if event_type == "CLIENT_FACING": if self.game.isclient or self.game.ishost: event_data = {"type": event_type, "event_number": self.event_list[event_type], "char_dict": {"facing": kb_key} } self.event_list[event_type] +=1 self.client.event(event_data) elif event_type == "CLIENT_KEYUP" or event_type == "CLIENT_KEYDOWN": event_data = {"type": event_type, "event_number": self.event_list[event_type], "kb_key": kb_key } self.event_list[event_type] +=1 self.client.event(event_data) def update_client_map(self, cuuid, event_data): """Updates client's current map and location to reflect the server registry. :param cuuid: Clients unique user identification number. :param event_data: Client characters current variable values. :type cuuid: String :type event_data: Dictionary :rtype: None :returns: None """ sprite = self.client.registry[cuuid]["sprite"] self.client.registry[cuuid]["map_name"] = event_data["map_name"] update_client(sprite, event_data["char_dict"], self.game) def player_interact(self, sprite, interaction, event_type="CLIENT_INTERACTION", response=None): """Sends client to client interaction request to the server. :param sprite: Character sprite being interacted with. :param interaction: Which interaction you wish to do. :type sprite: core.components.player.Npc() object :type interaction: String :rtype: None :returns: None """ if not event_type in self.event_list: self.event_list[event_type] = 1 cuuid = None for client_id in self.client.registry: if self.client.registry[client_id]["sprite"] == sprite: cuuid = client_id pd = prepare.player1.__dict__ event_data = {"type": event_type, "event_number": self.event_list[event_type], "interaction": interaction, "target": cuuid, "response": response, "char_dict": {"monsters": pd["monsters"], "inventory": pd["inventory"] } } self.event_list[event_type] +=1 self.client.event(event_data) def route_combat(self, event): print(event) def client_alive(self): """Sends server a ping to let it know that it is still alive. :param: None :rtype: None :returns: None """ event_type = "PING" if not event_type in self.event_list: self.event_list[event_type] = 1 else: self.event_list[event_type] +=1 event_data = {"type": event_type, "event_number": self.event_list[event_type]} self.client.event(event_data)
class Game(object): def __init__(self): # Set up our Neteria client. self.client = NeteriaClient() self.client.listen() # Set up and configure PyGame. pygame.init() self.screen = pygame.display.set_mode((800, 600)) self.clock = pygame.time.Clock() font = pygame.font.Font(None, 36) self.text = font.render("Press ENTER to auto-register.", 1, (240, 240, 240)) pygame.display.set_caption("Neteria Client (Not connected)") # Create a game pad that we will use to control the player on the # server. ######## UP Arrow ######## self.up_arrow = pygame.image.load( "assets/client-up.png").convert_alpha() self.up_arrow_pos = ( (self.screen.get_width() / 2) - (self.up_arrow.get_width() / 2), (self.screen.get_height() / 2) - self.up_arrow.get_height()) self.up_arrow_rect = pygame.Rect(self.up_arrow_pos, self.up_arrow.get_size()) ######## DOWN Arrow ######## self.down_arrow = pygame.image.load( "assets/client-down.png").convert_alpha() self.down_arrow_pos = ( (self.screen.get_width() / 2) - (self.down_arrow.get_width() / 2), (self.screen.get_height() / 2)) self.down_arrow_rect = pygame.Rect(self.down_arrow_pos, self.down_arrow.get_size()) ######## LEFT Arrow ######## self.left_arrow = pygame.image.load( "assets/client-left.png").convert_alpha() self.left_arrow_pos = ( (self.screen.get_width() / 2) - self.left_arrow.get_width(), (self.screen.get_height() / 2) - self.left_arrow.get_height() / 2) self.left_arrow_rect = pygame.Rect(self.left_arrow_pos, self.left_arrow.get_size()) ######## RIGHT Arrow ######## self.right_arrow = pygame.image.load( "assets/client-right.png").convert_alpha() self.right_arrow_pos = ( (self.screen.get_width() / 2), (self.screen.get_height() / 2) - self.right_arrow.get_height() / 2) self.right_arrow_rect = pygame.Rect(self.right_arrow_pos, self.right_arrow.get_size()) def start(self): # Set up our main game loop. while True: self.clock.tick(60) self.screen.fill((0,0,0)) self.screen.blit(self.text, (0, 0)) # Draw the arrows on the screen. self.screen.blit(self.up_arrow, self.up_arrow_pos) self.screen.blit(self.down_arrow, self.down_arrow_pos) self.screen.blit(self.left_arrow, self.left_arrow_pos) self.screen.blit(self.right_arrow, self.right_arrow_pos) # Loop through all of our PyGame events. for event in pygame.event.get(): if event.type == pygame.QUIT: return elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: return elif event.type == pygame.KEYDOWN and event.key == pygame.K_RETURN: self.client.autodiscover(autoregister=True) # If we clicked on our directional pad, send an event to the server. elif event.type == pygame.MOUSEBUTTONDOWN: mouse_position = pygame.mouse.get_pos() if self.up_arrow_rect.collidepoint(mouse_position): self.client.event("KEYDOWN:up") if self.down_arrow_rect.collidepoint(mouse_position): self.client.event("KEYDOWN:down") if self.left_arrow_rect.collidepoint(mouse_position): self.client.event("KEYDOWN:left") if self.right_arrow_rect.collidepoint(mouse_position): self.client.event("KEYDOWN:right") # When we release the mouse, send a key up event. elif event.type == pygame.MOUSEBUTTONUP: self.client.event("KEYUP:up") self.client.event("KEYUP:down") self.client.event("KEYUP:left") self.client.event("KEYUP:right") # If the client successfully registers, change the window title. if self.client.registered: pygame.display.set_caption("Neteria Client (Registered!)") pygame.display.flip()
class TuxemonClient(): """Client class for multiplayer games. Creates a netaria client and synchronizes the local game with the host state. :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.interfaces = {} self.available_games = {} self.server_list = [] self.selected_game = None self.enable_join_multiplayer = False self.wait_broadcast = 0 # Used to delay autodiscover broadcast. self.wait_delay = 0.25 # Delay used in autodiscover broadcast. self.join_self = False # Default False. Set True for testing on one device. self.populated = False self.listening = False self.event_list = {} # Handle users without networking support. if not networking: self.client = DummyNetworking() return self.client = NeteriaClient(server_port=40081) self.client.registry = {} for device in netifaces.interfaces(): interface = netifaces.ifaddresses(device) try: self.interfaces[device] = interface[netifaces.AF_INET][0]['addr'] except KeyError: pass def update(self, time_delta): """Updates the client and local game state with information sent from the server :param time_delta: Time since last frame. :type time_delta: float :rtype: None :returns: None """ if self.enable_join_multiplayer: self.join_multiplayer(time_delta) if self.client.registered and not self.populated: self.game.isclient = True self.game.current_state.multiplayer_join_success_menu.text = ["Success!"] self.populate_player() self.check_notify() def check_notify(self): """Checks for notify events sent from the server and updates the local client registry to reflect the updated information. :param: None :rtype: None :returns: None """ for euuid, event_data in self.client.event_notifies.items(): if event_data["type"] == "NOTIFY_PUSH_SELF": if not event_data["cuuid"] in self.client.registry: self.client.registry[str(event_data["cuuid"])]={} sprite = populate_client(event_data["cuuid"], event_data, self.game, self.client.registry) update_client(sprite, event_data["char_dict"], self.game) del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_MOVE_START": direction = str(event_data["direction"]) sprite = self.client.registry[event_data["cuuid"]]["sprite"] sprite.facing = direction for d in sprite.direction: if sprite.direction[d]: sprite.direction[d] = False sprite.direction[direction] = True del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_MOVE_COMPLETE": sprite = self.client.registry[event_data["cuuid"]]["sprite"] sprite.final_move_dest = event_data["char_dict"]["tile_pos"] for d in sprite.direction: if sprite.direction[d]: sprite.direction[d] = False del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_MAP_UPDATE": self.update_client_map(event_data["cuuid"], event_data) del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_KEYDOWN": sprite = self.client.registry[event_data["cuuid"]]["sprite"] if event_data["kb_key"] == "SHIFT": sprite.running = True del self.client.event_notifies[euuid] elif event_data["kb_key"] == "CRTL": del self.client.event_notifies[euuid] elif event_data["kb_key"] == "ALT": del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_KEYUP": sprite = self.client.registry[event_data["cuuid"]]["sprite"] if event_data["kb_key"] == "SHIFT": sprite.running = False del self.client.event_notifies[euuid] elif event_data["kb_key"] == "CRTL": del self.client.event_notifies[euuid] elif event_data["kb_key"] == "ALT": del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_FACING": sprite = self.client.registry[event_data["cuuid"]]["sprite"] if not sprite.moving: sprite.facing = event_data["char_dict"]["facing"] del self.client.event_notifies[euuid] if event_data["type"] == "NOTIFY_CLIENT_INTERACTION": world = self.game.get_world_state() if not world: return world.handle_interaction(event_data, self.client.registry) del self.client.event_notifies[euuid] def join_multiplayer(self, time_delta): """Joins the client to the selected server. :param time_delta: Time since last frame. :type time_delta: float :rtype: None :returns: None """ # Don't allow player to join another game if they are hosting. if self.game.ishost: self.enable_join_multiplayer = False return False # If we have already joined a game end the loop. if self.client.registered: self.enable_join_multiplayer = False return False # Join a game if we have already selected one. if self.selected_game: self.client.register(self.selected_game) # Once per second send a server discovery packet. if self.wait_broadcast >= self.wait_delay: self.update_multiplayer_list() self.wait_broadcast = 0 else: self.wait_broadcast += time_delta def update_multiplayer_list(self): """Sends a broadcast to 'ping' all servers on the local network. Once a server responds it will verify that the server is not hosted by the client who sent the ping. Once a server has been identified it adds it to self.available_games. :param None: :rtype: None :returns: None """ self.client.autodiscover(autoregister=False) # Logic to prevent joining your own game as a client. if len(self.client.discovered_servers) > 0: for ip, port in self.client.discovered_servers: try: if self.available_games[ip]: logger.info('Game already in list, skipping.') return False except KeyError: pass if not self.join_self: for interface in self.interfaces: if ip == self.interfaces[interface]: logger.info('Users server responded to users own broadcast, Deleting entry.') del self.client.discovered_servers[(ip, port)] return False # # Populate list of detected servers self.available_games[ip] = port for item in self.available_games.items(): self.server_list.append(item[0]) def populate_player(self, event_type="PUSH_SELF"): """Sends client character to the server. :param None :rtype: None :returns: None """ if not event_type in self.event_list: self.event_list[event_type] = 0 pd = prepare.player1.__dict__ map_name = self.game.get_map_name() event_data = {"type": event_type, "event_number": self.event_list[event_type], "sprite_name": pd["sprite_name"], "map_name": map_name, "char_dict": { "tile_pos": pd["tile_pos"], "name": pd["name"], "facing": pd["facing"], "monsters": pd["monsters"], "inventory": pd["inventory"] } } self.event_list[event_type] +=1 self.client.event(event_data) self.populated = True def update_player(self, direction, event_type="CLIENT_MAP_UPDATE"): """Sends client's current map and location to the server. :param direction: Facing/Movement direction of clients character :param event_type: Event type sent to server used for event_legal() and event_execute() functions in middleware. :type direction: String :type type: String :rtype: None :returns: None """ if not event_type in self.event_list: self.event_list[event_type] = 0 pd = prepare.player1.__dict__ map_name = self.game.get_map_name() event_data = {"type": event_type, "event_number": self.event_list[event_type], "map_name": map_name, "direction": direction, "char_dict": {"tile_pos": pd["tile_pos"] } } self.event_list[event_type] +=1 self.client.event(event_data) def set_key_condition(self, event): """Sends server information about a key condition being set or that an interaction has occurred. :param event: Pygame key event :type event: Dictionary :rtype: None :returns: None """ event_type = None kb_key = None if event.type == pygame.KEYDOWN: event_type = "CLIENT_KEYDOWN" if event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: kb_key = "SHIFT" elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: kb_key = "CTRL" elif event.key == pygame.K_LALT or event.key == pygame.K_RALT: kb_key = "ALT" elif event.key == pygame.K_UP: kb_key = "up" elif event.key == pygame.K_DOWN: kb_key = "down" elif event.key == pygame.K_LEFT: kb_key = "left" elif event.key == pygame.K_RIGHT: kb_key = "right" if event.type == pygame.KEYUP: event_type = "CLIENT_KEYUP" if event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: kb_key = "SHIFT" elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: kb_key = "CTRL" elif event.key == pygame.K_LALT or event.key == pygame.K_RALT: kb_key = "ALT" elif event.key == pygame.K_UP: kb_key = "up" elif event.key == pygame.K_DOWN: kb_key = "down" elif event.key == pygame.K_LEFT: kb_key = "left" elif event.key == pygame.K_RIGHT: kb_key = "right" if kb_key == "up" or kb_key == "down" or kb_key == "left" or kb_key == "right": event_type = "CLIENT_FACING" if not event_type in self.event_list: self.event_list[event_type] = 0 if event_type and kb_key: if event_type == "CLIENT_FACING": if self.game.isclient or self.game.ishost: event_data = {"type": event_type, "event_number": self.event_list[event_type], "char_dict": {"facing": kb_key} } self.event_list[event_type] +=1 self.client.event(event_data) elif event_type == "CLIENT_KEYUP" or event_type == "CLIENT_KEYDOWN": event_data = {"type": event_type, "event_number": self.event_list[event_type], "kb_key": kb_key } self.event_list[event_type] +=1 self.client.event(event_data) def update_client_map(self, cuuid, event_data): """Updates client's current map and location to reflect the server registry. :param cuuid: Clients unique user identification number. :param event_data: Client characters current variable values. :type cuuid: String :type event_data: Dictionary :rtype: None :returns: None """ sprite = self.client.registry[cuuid]["sprite"] self.client.registry[cuuid]["map_name"] = event_data["map_name"] update_client(sprite, event_data["char_dict"], self.game) def player_interact(self, sprite, interaction, event_type="CLIENT_INTERACTION", response=None): """Sends client to client interaction request to the server. :param sprite: Character sprite being interacted with. :param interaction: Which interaction you wish to do. :type sprite: core.components.player.Npc() object :type interaction: String :rtype: None :returns: None """ if not event_type in self.event_list: self.event_list[event_type] = 1 cuuid = None for client_id in self.client.registry: if self.client.registry[client_id]["sprite"] == sprite: cuuid = client_id pd = prepare.player1.__dict__ event_data = {"type": event_type, "event_number": self.event_list[event_type], "interaction": interaction, "target": cuuid, "response": response, "char_dict": {"monsters": pd["monsters"], "inventory": pd["inventory"] } } self.event_list[event_type] +=1 self.client.event(event_data)
#!/usr/bin/python import logging import sys import time from neteria.client import NeteriaClient # Create a client instance. client = NeteriaClient() client.listen() # Discover a Neteria Server. print "Discovering Neteria servers..." while not client.registered: client.autodiscover() time.sleep(1) print "Connected!" # Send data to the server. exit_cmds = ['quit', 'exit'] data = None while data not in exit_cmds: data = str(raw_input("> ")) if data: client.event(data) sys.exit()