class Game: def __init__(self, port): self.running = False self.pid = 0 self.pids = [] self.nbPlayer = 0 self.players = {} self.entities = {} self.moves = [] self.shoot = False self.shoots = [] self.deads = [] self.killed = [] self.input = Input() self.graphics = Graphics() self.velocity = Constant.VELOCITY self.id = 0 self.port = port self.players_socket = {} self.players_socket_awaiting = {} self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.bind(('', self.port)) self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.listen(5) def init(self, name, nbEntity, nbPlayer = 1, pids = None): self.id = nbPlayer self.players[self.id] = Player(name) self.nbPlayer = nbPlayer self.pids = pids if pids else list(range(1, nbEntity + 1)) self.pid = self.pids[random.randrange(len(self.pids))] self.pids.remove(self.pid) def connect(self, ip, port, name): print("Connecting to ({}, {})...".format(ip, port)) player_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) player_socket.connect((ip, port)) message = self.receive(player_socket) if message.type & Message.OK and message.type & Message.INIT: (nbPlayer, pids, entities) = message.data self.init(name, 0, nbPlayer, pids) for (pid, position) in entities: self.entities[pid] = self.entity(position.x, position.y) n = nbPlayer - 2 self.players_socket[message.id] = player_socket self.notify(player_socket, Message.OK | Message.PORT, self.port) while n: player_socket, addr = self.socket.accept() print("Accepting new connection from", addr) self.notify(player_socket, Message.OK | Message.ID, None) message = self.receive(player_socket) if message.type & Message.OK and message.type & Message.ID: self.players_socket[message.id] = player_socket else: print("Can't join") self.disconnect() return self.notify(player_socket, Message.OK | Message.DONE, None) n -= 1 self.run() def connectTo(self, ip, port): print("Connecting to ({}, {})...".format(ip, port)) player_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) player_socket.connect((ip, port)) self.players_socket_awaiting[self.nbPlayer] = player_socket def disconnect(self): print("Disconnecting...") self.socket.close() if self.nbPlayer > 1: player_socket = next(iter(self.players_socket.values())) data = [[(pid, entity.position) for (pid, entity) in self.entities.items()]] self.notify(player_socket, Message.OK | Message.MOVE, data) time.sleep(0.05) # NEEDED : Wait at least one loop from the remote player self.notify(player_socket, Message.OK | Message.INIT, self.pids + [self.pid]) time.sleep(0.05) self.notifyAll(Message.OK | Message.DISCONNECT | Message.STRING, "Disconnecting...") for s in self.players_socket.values(): s.shutdown(socket.SHUT_WR) while len(self.players_socket): self.handleMessages(self.handleDisconnected) self.running = False print("Disconnected") def new(self, name, nbEntity): print("Game created") print("Listening on port", self.port) self.init(name, nbEntity) for i in range(1, nbEntity + 1): self.entities[i] = self.entity(random.randrange(Constant.WIDTH), random.randrange(Constant.HEIGHT)) self.run() def run(self): self.running = True self.graphics.createWindow(Constant.WIDTH, Constant.HEIGHT) t = time.time() while self.running: dt, t = self.getDt(t) self.clear() self.handleConnections() self.handleMessages(self.handleMessage) self.handleInputs(dt) self.update(dt) self.sendMessages() self.render() # NETWORK HANDLER def handleConnection(self, connection): player_socket, addr = connection.accept() self.nbPlayer += 1 player_pids, self.pids = self.pids[:len(self.pids)//2+1], self.pids[len(self.pids)//2+1:] data = (self.nbPlayer, player_pids, [(pid, self.entities[pid].position) for pid in player_pids]) self.notify(player_socket, Message.OK | Message.INIT, data) self.players_socket_awaiting[self.nbPlayer] = player_socket print("Accepting new connection from", addr) def handleConnections(self): connections, wlist, xlist = select.select([self.socket], [], [], 0.05) for connection in connections: self.handleConnection(connection) def handleMessage(self, player_socket): message = self.receive(player_socket) if message.type & Message.OK: if message.type & Message.CONNECT: self.nbPlayer += 1 (host, port) = message.data self.connectTo(host, port) elif message.type & Message.INIT: self.pids.extend(message.data) elif message.type & Message.DISCONNECT: self.nbPlayer -= 1 self.notify(player_socket, Message.OK | Message.DISCONNECTED, None) player_socket.shutdown(socket.SHUT_RDWR) player_socket.close() del self.players_socket[message.id] elif message.type & Message.ID: self.notify(player_socket, Message.OK | Message.ID, None) elif message.type & Message.PORT: self.notifyAll(Message.OK | Message.CONNECT, (player_socket.getpeername()[0], message.data)) self.players_socket[message.id] = self.players_socket_awaiting[message.id] del self.players_socket_awaiting[message.id] elif message.type & Message.DONE: self.players_socket[message.id] = self.players_socket_awaiting[message.id] del self.players_socket_awaiting[message.id] if message.type & Message.MOVE: for (pid, position) in message.data[0]: if pid in self.entities: self.entities[pid].position = position else: self.entities[pid] = self.entity(position.x, position.y) if message.type & Message.SHOOT: self.shoots.append(message.id) if message.type & Message.DEAD: self.killed.extend(message.data[1]) #elif message.type & Message.STRING: # print(message) def handleDisconnected(self, player_socket): message = self.receive(player_socket) if message.type & Message.OK and message.type & Message.DISCONNECTED: player_socket.close() del self.players_socket[message.id] def handleMessages(self, f): players_socket = [] if len(self.players_socket) + len(self.players_socket_awaiting): try: players_socket, wlist, xlist = select.select(list(self.players_socket.values()) + list(self.players_socket_awaiting.values()), [], [], 0.05) for player_socket in players_socket: f(player_socket) except select.error as e: #print(e) pass # GAME HANDLER def handleInputs(self, dt): # Get keyboard inputs actionKeys = self.input.update() # Create action for (actionType, obj) in actionKeys: if actionType == pygame.QUIT: self.disconnect() elif actionType == pygame.KEYDOWN: if obj == pygame.K_ESCAPE: self.disconnect() elif obj == pygame.K_SPACE: self.shoot = True # Get player action dx = 0 dy = 0 if self.input.getKeyDown(pygame.K_LEFT): dx = int(-self.velocity * dt) if self.input.getKeyDown(pygame.K_RIGHT): dx += int(self.velocity * dt) if self.input.getKeyDown(pygame.K_UP): dy = int(-self.velocity * dt) if self.input.getKeyDown(pygame.K_DOWN): dy += int(self.velocity * dt) if self.input.getKeyDown(pygame.K_w): self.velocity = max(self.velocity - 5, 5) if self.input.getKeyDown(pygame.K_x): self.velocity = min(self.velocity + 5, 80) if dx != 0 or dy != 0: self.entities[self.pid].move(dx, dy, Constant.WIDTH, Constant.HEIGHT) self.moves.append(self.pid) # NETWORK UTILITY def notify(self, player, type, data): try: player.sendall(pickle.dumps(Message(self.id, type, data))) #print("Sending -> ", Message(self.id, type, data)) except: pass def notifySome(self, players, type, data): for player in players: self.notify(player, type, data) def notifyAll(self, type, data): self.notifySome(self.players_socket.values(), type, data) def receive(self, s): BUFFER_SIZE = 4096 data = b'' while True: data += s.recv(BUFFER_SIZE) if not data: return Message(0, Message.ERROR, None) else: if data.endswith(b'.'): m = pickle.loads(data) print(m) return m def sendMessages(self): data = [[], []] data_move = [] move = Message.NOTHING data_shoot = [] shoot = Message.NOTHING data_deads = [] deads = Message.NOTHING if len(self.moves): move = Message.MOVE data[0] = [(pid, self.entities[pid].position) for pid in self.moves] if self.shoot: shoot = Message.MOVE | Message.SHOOT data_shoot = [(self.pid, self.entities[self.pid].position)] if len(self.deads): deads = Message.DEAD data_deads = [pid for pid in self.deads] data[0].extend(data_move) data[0].extend(data_shoot) data[1].extend(data_deads) if move or shoot or deads: self.notifyAll(Message.OK | move | shoot | deads, data) # GAME UTILITY def getDt(self, lastTime): t = time.time() dt = t - lastTime return dt, t def entity(self, x, y): return Entity(x, y, (random.randrange(256), random.randrange(256), random.randrange(256))) def clear(self): self.shoot = False self.moves.clear() self.shoots.clear() self.deads.clear() self.killed.clear() def update(self, dt): for pid in self.pids: if self.entities[pid].behave(dt, Constant.WIDTH, Constant.HEIGHT): self.moves.append(pid) for pid_shoot in self.shoots: for pid in self.pids: if self.entities[pid].collide(self.entities[pid_shoot], Constant.DEFAULT_RADIUS, Constant.FIRING_RADIUS): del self.entities[pid] self.deads.append(pid) for pid in self.killed: del self.entities[pid] def render(self): self.graphics.clear() self.graphics.renderEntities(self.entities, self.pid, self.shoot, self.shoots) self.graphics.displayTopScore(self.players) self.graphics.displayOverlay(Constant.VELOCITY, Constant.HEIGHT) self.graphics.flip()