Last active
August 29, 2015 14:08
-
-
Save mrjoes/749e61191627c2d89081 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import socket | |
| import select | |
| import sys | |
| # Start this server locally as follows | |
| # python server.py 127.0.0.1 4444 1 | |
| BACKLOG = 1 | |
| def cmd_help(user, msg): | |
| user.send('Commands:\n') | |
| for cmd in all_commands: | |
| user.send(' %s\n' % cmd) | |
| def cmd_say(user, msg): | |
| room = user.room | |
| if room: | |
| user.room.broadcast('%s said %s\n' % (user.name, msg), ignore=user) | |
| user.send('You said %s\n' % msg) | |
| def cmd_look(user, msg): | |
| user.send_room_description(user.room) | |
| all_commands = { | |
| 'help': cmd_help, | |
| 'say': cmd_say, | |
| 'look': cmd_look | |
| } | |
| class User(object): | |
| def __init__(self, sock): | |
| self.sock = sock | |
| self.room = None | |
| # TODO: Read line | |
| self.name = sock.recv(4096).strip() | |
| self.send('Welcome %s!\n' % self.name) | |
| self.move_to(entry) | |
| self.send_prompt() | |
| self.broadcast('%s connected.\n' % self.name) | |
| def got_stuff(self): | |
| try: | |
| data = self.sock.recv(4096) | |
| except: | |
| disconnect(self.sock) | |
| return | |
| if not data: | |
| disconnect(self.sock) | |
| self.handle_command(data.strip()) | |
| # Logic | |
| def handle_command(self, msg): | |
| command = msg.split(' ', 1) | |
| rest = None | |
| if len(command) > 1: | |
| command, rest = command | |
| else: | |
| command = command[0] | |
| if not command: | |
| return | |
| handler = all_commands.get(command) | |
| if handler: | |
| handler(self, rest) | |
| else: | |
| direction = self.room.get_exit(command) | |
| if direction: | |
| self.move_to(direction) | |
| else: | |
| self.send('Huh?\n') | |
| self.send_prompt() | |
| def move_to(self, room): | |
| if self.room: | |
| self.room.remove_user(self) | |
| self.room = room | |
| self.room.add_user(self) | |
| self.send_room_description(room) | |
| # Helpers | |
| def send_prompt(self): | |
| self.send('> '); | |
| def send_room_description(self, room): | |
| self.send('%s\n\n' % room.description) | |
| if room.participants: | |
| users = [u for u in room.participants if u is not self] | |
| if users: | |
| if len(users) > 1: | |
| self.send('%s are here.\n' % ', '.join(u.name for u in room.participants)) | |
| else: | |
| self.send('%s is here.\n' % users[0].name) | |
| else: | |
| self.send('There\'s no one else here.\n') | |
| self.send('\nExits:\n') | |
| for side, room in room.exits.iteritems(): | |
| self.send(' %s leads to %s\n' % (side, room.name)) | |
| # Networking | |
| def send(self, msg): | |
| try: | |
| self.sock.send(msg) | |
| except: | |
| disconnect(self.sock) | |
| def notify(self, msg): | |
| self.send('\n%s' % msg) | |
| self.send_prompt() | |
| def broadcast(self, msg, ignore=None): | |
| for user in all_users.itervalues(): | |
| if user is not ignore: | |
| user.notify(msg) | |
| def close(self): | |
| if self.room: | |
| self.room.remove_user(self) | |
| class Room(object): | |
| def __init__(self, name, description): | |
| self.name = name | |
| self.description = description | |
| self.participants = set() | |
| self.exits = {} | |
| # API | |
| def add_user(self, user): | |
| self.broadcast('%s entered room.\n' % user.name) | |
| self.participants.add(user) | |
| def remove_user(self, user): | |
| if user in self.participants: | |
| self.participants.remove(user) | |
| self.broadcast('%s left room.\n' % user.name) | |
| def add_exit(self, side, room): | |
| self.exits[side] = room | |
| def get_exit(self, side): | |
| return self.exits.get(side) | |
| # Networking | |
| def broadcast(self, msg, ignore=None): | |
| for u in self.participants: | |
| if u is not ignore: | |
| u.notify(msg) | |
| # Global State | |
| all_users = {} | |
| entry = Room('Portal room', 'You\'re in a small portal room. Welcome to the dungeon. Type `help` to get list of commands.') | |
| connections = [] | |
| # Game | |
| def create_world(): | |
| # Garden | |
| garden = Room('Garden', 'Garden. Very pretty. Butterflies, grass, cows.') | |
| entry.add_exit('left', garden) | |
| garden.add_exit('right', entry) | |
| # Factory | |
| factory = Room('Factory', 'Factory.\nLots of weird mechanisms and it is quite noisy here.') | |
| entry.add_exit('right', factory) | |
| factory.add_exit('back', entry) | |
| def disconnect(sock): | |
| sock.close() | |
| if sock in connections: | |
| connections.remove(sock) | |
| if sock in all_users: | |
| all_users[sock].close() | |
| def serve(address, port, timeout=None, backlog=None): | |
| # Create a TCP/IP socket | |
| server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
| # Bind the socket to the port | |
| server_address = (address, port) | |
| print >>sys.stderr, 'Starting up on %s port %s' % server_address | |
| server_sock.bind(server_address) | |
| # Listen for incoming connections | |
| server_sock.listen(backlog) | |
| connections.append(server_sock) | |
| while True: | |
| readable, _, exceptional = select.select(connections, [], connections) | |
| # Handle events | |
| for s in readable: | |
| if s is server_sock: | |
| sock, addr = s.accept() | |
| connections.append(sock) | |
| sock.send('Enter your name: ') | |
| else: | |
| user = all_users.get(s) | |
| if user is not None: | |
| all_users[s].got_stuff() | |
| else: | |
| all_users[s] = User(s) | |
| # Handle disconnects | |
| for s in exceptional: | |
| disconnect(s) | |
| if __name__ == '__main__': | |
| address = sys.argv[1] | |
| port = int(sys.argv[2]) | |
| timeout = float(sys.argv[3]) | |
| backlog = int(sys.argv[4]) if len(sys.argv) > 4 else BACKLOG | |
| create_world() | |
| serve(address, port, timeout=timeout, backlog=backlog) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment