def main(): game = Game.Game(("player 1", "X"), ("player 2", "O")) server = GameServer(sys.argv[1], int(sys.argv[2]), game) server.start() print(MESSAGE_EXITING)
class GameClient(GameModule): """Contains game client and pyGame functionality.""" def __init__(self): self.in_queue = Queue() self.dispatch_queue = Queue() GameModule.__init__(self, self.in_queue, self.dispatch_queue) self.name = 'Client' self.module_id = GAME.INVALID_ID self.server_id = GAME.LOCAL_SERVER_ID self.server_addr = '127.0.0.1' self.local_server_instance = None self.dispatch = None self.network_mgr = None self.connected = False # Initialize pyGame pygame.mixer.pre_init() pygame.init() self.screen = pygame.display.set_mode((GAME.WIDTH, GAME.HEIGHT)) pygame.display.set_caption('AstroBlast!') self.clock = pygame.time.Clock() pygame.mouse.set_visible(False) # Initialize pgu.gui # Title screen GUI self.title_app = gui.App() self.title_container = gui.Container(width=GAME.WIDTH, height=GAME.HEIGHT, x=0, y=0) self.title_sp_button = gui.Button("Single Player", width=150, height=100) self.title_sp_button.connect(gui.CLICK, self.gui_button_clicked, GAME.GUI_BUTTON_SP) self.title_container.add(self.title_sp_button, GAME.WIDTH / 2 - 75, GAME.HEIGHT / 2) self.title_mp_button = gui.Button("Multiplayer", width=150, height=100) self.title_mp_button.connect(gui.CLICK, self.gui_button_clicked, GAME.GUI_BUTTON_MP) self.title_container.add(self.title_mp_button, GAME.WIDTH / 2 - 75, 2 * GAME.HEIGHT / 3) self.title_sp_button.connect(gui.CLICK, self.gui_button_clicked, GAME.GUI_BUTTON_MP) self.title_app.init(self.title_container, self.screen, pygame.rect.Rect(0, 0, GAME.WIDTH, GAME.HEIGHT)) # Multiplayer Menu GUI self.multi_app = gui.App() self.multi_container = gui.Container(width=GAME.WIDTH, height=GAME.HEIGHT, x=0, y=0) self.multi_host_button = gui.Button("Host Game", width=100, height=100) self.multi_host_button.connect(gui.CLICK, self.gui_button_clicked, GAME.GUI_BUTTON_HOST) self.multi_container.add(self.multi_host_button, GAME.WIDTH / 3, GAME.HEIGHT / 3) self.multi_join_button = gui.Button("Join Game", width=100, height=100) self.multi_join_button.connect(gui.CLICK, self.gui_button_clicked, GAME.GUI_BUTTON_JOIN) self.multi_container.add(self.multi_join_button, GAME.WIDTH / 3 + 150, GAME.HEIGHT / 3) self.multi_back_button = gui.Button("Back", width=50, height=50) self.multi_back_button.connect(gui.CLICK, self.gui_button_clicked, GAME.GUI_BUTTON_BACK) self.multi_container.add(self.multi_back_button, 30, GAME.HEIGHT - 80) self.multi_ip_input = gui.Input("IP address") self.multi_container.add(self.multi_ip_input, 2 * GAME.WIDTH / 3, GAME.HEIGHT / 3) self.multi_port_input = gui.Input("Port") self.multi_container.add(self.multi_port_input, 2 * GAME.WIDTH / 3, GAME.HEIGHT / 3 + 50) self.multi_app.init(self.multi_container, self.screen, pygame.rect.Rect(0, 0, GAME.WIDTH, GAME.HEIGHT)) # Mouse cursor cursor, rect = load_image('cursor.png') self.cursor = cursor # Images for sprites self.frames = { 'ship': [ load_image_all_rotations('ship.png'), load_image_all_rotations('ship_thrust.png') ], 'asteroid_big': [load_image_all_rotations('asteroid_big.png')], 'asteroid_med': [load_image_all_rotations('asteroid_med.png')], 'asteroid_small': [load_image_all_rotations('asteroid_small.png')], 'bullet_g': [load_image_all_rotations('bullet_g.png')], 'explosion': [ load_image_all_rotations('explosion_1.png'), load_image_all_rotations('explosion_2.png') ] } self.background = load_image('bg5.jpg')[0] # Sounds self.thrust_sound = pygame.mixer.Sound( path.join(ASSETSDIR, 'thrust.ogg')) self.thrust_sound.set_volume(1) self.shot_sound = pygame.mixer.Sound(path.join(ASSETSDIR, 'shot.wav')) self.shot_sound.set_volume(0.75) self.explode_sound = pygame.mixer.Sound( path.join(ASSETSDIR, 'explode.wav')) self.explode_sound.set_volume(1) self.entities = {} self.unused_entities = [] # Generate some unused entities for x in range(100): self.unused_entities.append(EntitySprite(None)) self.sprites = pygame.sprite.Group() self.player_entity_id = -1 self.player_alive = False self.player_lives = 0 self.player_score = 0 self.game_state = GAME.STATE_TITLE def quit(self): """Sends quit message and halts client""" # Make sure Server and Dispatcher quit too if self.local_server_instance is not None: self.local_server_instance.running = False if self.dispatch is not None: self.dispatch.running = False if self.network_mgr is not None: self.network_mgr.running = False # self.send_msg(MESSAGES.TERMINATE, self.local_server_id) self.running = False def process_msg(self, msg_type, sender_id, msg_content): """Process an incoming message""" # Server accepted connection request if msg_type == MESSAGES.CONNECT_ACCEPT: self.connected = True self.send_msg(MESSAGES.CONNECT_SUCCESS, sender_id) # Server rejected connection request elif msg_type == MESSAGES.CONNECT_REJECT: self.log("Connection to server unsuccessful; connection rejected") # Server is disconnecting elif msg_type == MESSAGES.SIGNAL_DISCONNECT: self.disconnect(False) # Server created an entity elif msg_type == MESSAGES.CREATE_ENTITY: expected_content = (MSGCONTENT.ENTITY_ID, MSGCONTENT.ENTITY_TYPE, MSGCONTENT.X_POS, MSGCONTENT.Y_POS, MSGCONTENT.ROTATION) if self.assert_msg_content(msg_type, msg_content, *expected_content): entity_id = msg_content[MSGCONTENT.ENTITY_ID] entity_type = msg_content[MSGCONTENT.ENTITY_TYPE] pos = (msg_content[MSGCONTENT.X_POS], msg_content[MSGCONTENT.Y_POS]) rot = msg_content[MSGCONTENT.ROTATION] # Try to get an unused entity, or create one if there are none try: e = self.unused_entities.pop() except IndexError: e = EntitySprite(None) if entity_type == GAME.ENTITY_TEST: info = SpriteInfo(entity_type, self.frames['ship'], 0, 0) e.initialize(info, pos, rot, entity_id, entity_type) self.entities[e.entity_id] = e self.sprites.add(e) elif entity_type == GAME.ENTITY_PLAYERSHIP: info = SpriteInfo(entity_type, self.frames['ship'], 0, 0) e.initialize(info, pos, rot, entity_id, entity_type) self.entities[e.entity_id] = e self.sprites.add(e) pid = msg_content.get(MSGCONTENT.PLAYER_ID, None) if pid is not None and pid == self.module_id: self.player_entity_id = e.entity_id self.player_alive = True elif entity_type == GAME.ENTITY_ASTEROID_BIG: info = SpriteInfo(entity_type, self.frames['asteroid_big'], 0, 0) e.initialize(info, pos, rot, entity_id, entity_type) self.entities[e.entity_id] = e self.sprites.add(e) elif entity_type == GAME.ENTITY_ASTEROID_MED: info = SpriteInfo(entity_type, self.frames['asteroid_med'], 0, 0) e.initialize(info, pos, rot, entity_id, entity_type) self.entities[e.entity_id] = e self.sprites.add(e) elif entity_type == GAME.ENTITY_ASTEROID_SMALL: info = SpriteInfo(entity_type, self.frames['asteroid_small'], 0, 0) e.initialize(info, pos, rot, entity_id, entity_type) self.entities[e.entity_id] = e self.sprites.add(e) elif entity_type == GAME.ENTITY_BULLET: info = SpriteInfo(entity_type, self.frames['bullet_g'], 0, 0) e.initialize(info, pos, rot, entity_id, entity_type) self.entities[e.entity_id] = e self.sprites.add(e) self.shot_sound.play() elif entity_type == GAME.ENTITY_EXPLOSION: info = SpriteInfo(entity_type, self.frames['explosion'], 1, 500) e.initialize(info, pos, rot, entity_id, entity_type) self.entities[e.entity_id] = e self.sprites.add(e) self.explode_sound.play() # Server destroyed an entity elif msg_type == MESSAGES.DESTROY_ENTITY: if self.assert_msg_content(msg_type, msg_content, MSGCONTENT.ENTITY_ID): entity_id = msg_content[MSGCONTENT.ENTITY_ID] e = self.entities.pop(entity_id) if e is not None: self.sprites.remove(e) self.unused_entities.append(e) if e.entity_id == self.player_entity_id: self.player_alive = False else: self.log( 'Received message to destroy sprite for entity %d, but sprite did not exist' % entity_id) # An entity rotated by some amount elif msg_type == MESSAGES.UPDATEROT: expected_content = (MSGCONTENT.ENTITY_ID, MSGCONTENT.ROTATION) if self.assert_msg_content(msg_type, msg_content, *expected_content): entity_id = msg_content[MSGCONTENT.ENTITY_ID] rot = msg_content[MSGCONTENT.ROTATION] e = self.entities.get(entity_id) if e is not None: e.rotation = rot else: self.log( 'Received message to update rotation for entity %d, but sprite does not exist' % entity_id) # An entity moved elif msg_type == MESSAGES.UPDATEPOS: expected_content = (MSGCONTENT.X_POS, MSGCONTENT.Y_POS) if self.assert_msg_content(msg_type, msg_content, *expected_content): entity_id = msg_content[MSGCONTENT.ENTITY_ID] pos = (msg_content[MSGCONTENT.X_POS], msg_content[MSGCONTENT.Y_POS]) e = self.entities.get(entity_id) if e is not None: e.position = pos else: self.log( 'Received message to update position for entity %d, but sprite does not exist' % entity_id) # Player life count changed elif msg_type == MESSAGES.UPDATELIVES: expected_content = MSGCONTENT.PLAYER_LIVES if self.assert_msg_content(msg_type, msg_content, expected_content): self.player_lives = msg_content[MSGCONTENT.PLAYER_LIVES] # Player score changed elif msg_type == MESSAGES.UPDATESCORE: expected_content = MSGCONTENT.PLAYER_SCORE if self.assert_msg_content(msg_type, msg_content, expected_content): self.player_score = msg_content[MSGCONTENT.PLAYER_SCORE] # Server wants to change the client state to some other setting elif msg_type == MESSAGES.CHANGE_STATE: expected_content = MSGCONTENT.GAME_STATE if self.assert_msg_content(msg_type, msg_content, expected_content): self.game_state = msg_content[MSGCONTENT.GAME_STATE] def disconnect(self, should_send_signal=True): """Disconnect this client from a server""" if should_send_signal: self.send_msg(MESSAGES.SIGNAL_DISCONNECT, self.server_id, self.server_addr) self.connected = False self.log("Disconnected from server") self.module_id = GAME.INVALID_ID # Delete sprites self.sprites.empty() self.entities.clear() def gui_button_clicked(self, button_type): """Called when one of the buttons in the UI is clicked""" # Singleplayer button if button_type == GAME.GUI_BUTTON_SP: server_queue = Queue() remote_queue = Queue() # Create server and dispatcher self.dispatch = Dispatcher(self.dispatch_queue, server_queue, self.in_queue, remote_queue) self.dispatch.start() self.out_queue = self.dispatch_queue self.module_id = GAME.LOCAL_CLIENT_ID self.local_server_instance = GameServer(self.dispatch, server_queue) self.local_server_instance.start() self.server_id = self.local_server_instance.module_id self.server_addr = self.local_server_instance.addr # Start singleplayer by connecting to server self.send_msg(MESSAGES.REQCONNECT, self.server_id) # Switch to multiplayer menu elif button_type == GAME.GUI_BUTTON_MP: self.game_state = GAME.STATE_MULTIPLAYER_MENU # Host Multiplayer Session elif button_type == GAME.GUI_BUTTON_HOST: port = int(self.multi_port_input.value) server_queue = Queue() remote_queue = Queue() # Create server and dispatcher self.dispatch = Dispatcher(self.dispatch_queue, server_queue, self.in_queue, remote_queue) self.network_mgr = NetworkManager(remote_queue, self.dispatch, self.dispatch_queue, NETWORK.MODE_SERVER, port) self.dispatch.start() self.network_mgr.start() self.addr = self.network_mgr.addr self.server_addr = self.network_mgr.addr self.out_queue = self.dispatch_queue self.module_id = GAME.LOCAL_CLIENT_ID self.local_server_instance = GameServer(self.dispatch, server_queue) self.local_server_instance.start() self.server_id = GAME.LOCAL_SERVER_ID self.send_msg(MESSAGES.REQCONNECT, self.server_id) # Join a Multiplayer session elif button_type == GAME.GUI_BUTTON_JOIN: server_queue = Queue() remote_queue = Queue() self.server_addr = self.multi_ip_input.value port = int(self.multi_port_input.value) self.dispatch = Dispatcher(self.dispatch_queue, server_queue, self.in_queue, remote_queue) self.dispatch.mode = NETWORK.MODE_CLIENT self.network_mgr = NetworkManager(remote_queue, self.dispatch, self.dispatch_queue, NETWORK.MODE_CLIENT, port, self.server_addr) self.dispatch.start() self.network_mgr.start() self.out_queue = self.dispatch_queue self.addr = get_lan_ip() self.module_id = GAME.REMOTE_CLIENT_ID self.send_msg(MESSAGES.REQCONNECT, self.server_id) elif button_type == GAME.GUI_BUTTON_BACK: self.game_state = GAME.STATE_TITLE def update(self): """Update game state/input state""" self.clock.tick(GAME.FPS) # Handle pyGame events events_found = False event_str = 'pyGame events: ' for event in pygame.event.get(): # Cache the event name for logging events_found = True event_str += pygame.event.event_name(event.type) + ', ' if self.game_state == GAME.STATE_TITLE: # Pass events to gui self.title_app.event(event) elif self.game_state == GAME.STATE_MULTIPLAYER_MENU: # Pass events to gui self.multi_app.event(event) else: # Handle keyboard events if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: self.quit() elif event.key == pygame.K_BACKSLASH: pass elif event.key == pygame.K_BACKSPACE: self.disconnect() elif event.key == pygame.K_LEFT: self.send_msg(MESSAGES.INPUT_LEFT_DOWN, self.server_id) elif event.key == pygame.K_RIGHT: self.send_msg(MESSAGES.INPUT_RIGHT_DOWN, self.server_id) elif event.key == pygame.K_UP: pship = self.entities.get(self.player_entity_id) if pship is not None: pship.current_frame = 1 self.thrust_sound.play() else: self.thrust_sound.stop() self.send_msg(MESSAGES.INPUT_THRUST_DOWN, self.server_id) elif event.key == pygame.K_SPACE: self.send_msg(MESSAGES.INPUT_SHOOT_DOWN, self.server_id) if event.type == pygame.KEYUP: if event.key == pygame.K_LEFT: self.send_msg(MESSAGES.INPUT_LEFT_UP, self.server_id) elif event.key == pygame.K_RIGHT: self.send_msg(MESSAGES.INPUT_RIGHT_UP, self.server_id) elif event.key == pygame.K_UP: pship = self.entities.get(self.player_entity_id) if pship is not None: pship.current_frame = 0 self.thrust_sound.stop() self.send_msg(MESSAGES.INPUT_THRUST_UP, self.server_id) elif event.key == pygame.K_SPACE: self.send_msg(MESSAGES.INPUT_SHOOT_UP, self.server_id) if event.type == pygame.QUIT: self.quit() # Print the event name to stdout if events_found and GAME.PRINTEVENTS: print(event_str) # Clear surface self.screen.fill(GAME.BLACK) # Draw background self.screen.blit(self.background, (0, 0)) # Draw sprites on the surface # self.ship_sprite.rotation += 10 self.sprites.update() self.sprites.draw(self.screen) # Text/GUI if (self.game_state == GAME.STATE_TITLE): # Draw GUI self.title_app.paint(self.screen) # Draw cursor when appropriate if pygame.mouse.get_focused(): self.screen.blit(self.cursor, pygame.mouse.get_pos()) elif (self.game_state == GAME.STATE_MULTIPLAYER_MENU): # Draw GUI self.multi_app.paint(self.screen) # Draw cursor when appropriate if pygame.mouse.get_focused(): self.screen.blit(self.cursor, pygame.mouse.get_pos()) # Draw labels normal_font = pygame.font.SysFont('Helvetica, Arial', 20, 1) larger_font = pygame.font.SysFont('Helvetica, Arial', 30, 1) huge_font = pygame.font.SysFont('Helvetica, Arial,', 150, 1) if (self.game_state == GAME.STATE_TITLE): width, height = huge_font.size('AstroBlast!') title_label = huge_font.render('AstroBlast!', 1, (0, 255, 0)) self.screen.blit(title_label, (GAME.WIDTH / 2 - width / 2, height)) if (self.game_state == GAME.STATE_IN_GAME or self.game_state == GAME.STATE_GAME_START or self.game_state == GAME.STATE_PLAYER_DIED): width, height = normal_font.size('Lives:') lives_label = normal_font.render('Lives: %d' % self.player_lives, 1, (255, 255, 0)) self.screen.blit(lives_label, (20, 20)) score_label = normal_font.render('Score: %d' % self.player_score, 1, (255, 255, 0)) self.screen.blit(score_label, (20, height + 20)) if self.game_state == GAME.STATE_GAME_START or self.game_state == GAME.STATE_GAME_OVER: width, height = normal_font.size('Press Fire (Spacebar)') start_label = normal_font.render('Press Fire (Spacebar)', 1, (255, 255, 0)) self.screen.blit(start_label, (GAME.WIDTH / 2 - width / 2, GAME.HEIGHT / 2)) if self.game_state == GAME.STATE_GAME_OVER: width, height = larger_font.size('Game Over!') game_over_label = larger_font.render('Game Over!', 1, (255, 255, 0)) self.screen.blit( game_over_label, (GAME.WIDTH / 2 - width / 2, GAME.HEIGHT / 2 - 40)) # Display surface to the screen pygame.display.flip() # Handles quitting/etc. super().update()