Example #1
0
class Host(Frame):  #inherit from Game?
    """
    Handles all server-side operations; also maintains connections with Clients and communicates at each game cycle
    """

    DELAY_START = 150
    MAX_ASTEROIDS = 6
    INTRODUCE_CHANCE = 0.01

    #server_address = ("localhost", 10000)
    def __init__(
        self,
        name,
        w,
        h,
        ww,
        wh,
        topology='wrapped',
        console_lines=0,
        port=10000,
        num_cns=1,
        FPS=60
    ):  #need to add options so running Main lets you choose your port.

        #stuff I need (from Frame) but don't want.  Remove?

        # Register the world coordinate and graphics parameters.
        self.WINDOW_WIDTH = ww
        self.WINDOW_HEIGHT = wh
        self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2)
        self.topology = topology

        #        self.num_frames = 0

        # actual host:
        self.connections = [
        ]  #for now, this will be a pointer to the Client object.  I will need to change this when I go multiplayer.
        self.agents = [
        ]  #storing agents in a dictionary, rather than in a list, because it makes it easier to think about how to construct the command string.
        self.ships = []  #how does this work with closing connections?
        self.available_IDs = all_IDs()

        self.number_of_asteroids = 0
        self.number_of_shrapnel = 0

        self.before_start_ticks = self.DELAY_START
        self.started = False
        self.FPS = FPS

        self.command_string = ""

        self.level = 3  #deal with this later
        self.score = 0

        self.GAME_OVER = False  #should I include a way for the host to terminate the game, kicking out all the clients?

        #network stuff:
        IP = socket.gethostbyname(socket.gethostname())
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_address = (
            IP, port
        )  #does this need to go later?  Can I just make this work with localhost, and still have others connect to it?
        print('starting up on {} port {}'.format(*server_address))
        self.sock.bind(server_address)
        self.sock.listen(
            num_cns)  #should I be printing the ip address near here?
        connections_formed = 0
        while connections_formed < num_cns:  #Do I need to close each connection after each pass, and re-open it later?
            print("broadcasting on", self.sock.getsockname()[0])
            print("Waiting for a connection.  Currently connected:",
                  connections_formed, "out of", num_cns)
            connection, client_address = self.sock.accept()
            print("Connection from", client_address, "\n")
            self.add_connection((connection, client_address))
            connections_formed += 1
        self.address = server_address

        if len(self.ships) > 1:
            center = Point2D(w / 2, h / 2)

    def add_connection(self, cn):
        self.connections.append(cn)
        ship = PlayAsteroids.Ship(self)
        self.ships.append(ship)
#        cn.receive_ID(ship.ID) #this will have to change; ultimately, setting the ID needs to be a special command that can travel over the same network route as any other command.
#        self.pass_output() #to make sure that the clients have a copy of the ship

    def drop_connection(self, cn):
        print("Client at", cn[1], "dropped")
        ship_index = self.connections.index(cn)
        ship = self.ships[ship_index]
        ship.remove_dependants()  #to deal with shields, exhaust, etc.
        self.ships.remove(
            ship
        )  #to fix the problem with the ships staying around after the connection dropped.
        self.agents.remove(ship)
        cn[0].close()
        self.connections.remove(cn)
        if len(self.connections) == 0:
            self.sock.close()  #is this going to cause errors?
            self.GAME_OVER = True

    def trim(
        self, agent
    ):  #holding measure - this is an important part of the game engine, and needs to be addressed.
        if self.topology == 'wrapped':
            agent.position = self.bounds.wrap(agent.position)
        elif self.topology == 'bound':
            agent.position = self.bounds.clip(agent.position)
        elif self.topology == 'open':
            pass

    def max_asteroids(self):
        return min(2 + self.level, self.MAX_ASTEROIDS)

    def receive_input(self):
        data_list = []
        for c in self.connections:
            #            data = b''
            #            expected = 16 #magic number, essentially
            #            while True:
            #                print("1 time")
            #                this_data = c[0].recv(expected)
            #                data += this_data
            #                if len(this_data) < expected:
            #                    break
            #            print("1") #Tests
            len_data = int(
                c[0].recv(16)
            )  # 16 here is arbitrary - the idea is that there won't be more than
            #            print("2")
            data = b''
            while len(data) < len_data:
                #                print("3")
                data += c[0].recv(4096)  # also arbitrary
            data_list.append(data.decode('ascii'))
        self.handle_input(data_list)

    def handle_input(self, data_list):  #TODO #????
        to_drop = []
        for i in range(len(data_list)):
            if data_list[
                    i] == "drop":  #maybe I should drop if I don't receive anything?  Might that work better?
                to_drop.append(self.connections[i])
            else:
                parts = data_list[i].split("|")
                del parts[0]
                #                print("parts = ",parts)
                ship = self.ships[
                    i]  #was self.agents[i].  Not sure why that ever worked.
                for p in parts:
                    ship.set_property(p.split(":"))
        for cn in to_drop:
            self.drop_connection(cn)

    def update(self):

        self.receive_input()

        self.command_string = ""  #first 'command' will be null?  Shouldn't matter.
        #                           Turns out it does, but I can resolve that in Client.
        if self.before_start_ticks > 0:
            self.before_start_ticks -= 60 / self.FPS
        else:
            self.started = True
            for s in self.ships:
                s.toggle_weapons()

        if self.started:
            tense = (self.number_of_asteroids >= self.max_asteroids())
            tense = tense or (self.number_of_shrapnel >= 2 * self.level)
            if not tense and random.random() < self.INTRODUCE_CHANCE:
                PlayAsteroids.LargeAsteroid(self)

        for agent in self.agents:
            agent.update()
#            self.command_string += "|update:" + self.agents[ID].report()

        for agent in self.agents:
            self.command_string += "|" + agent.color()
            point_list = agent.shape()
            for p in point_list:
                self.command_string += ":" + str(round(p.x, 3)) + "," + str(
                    round(p.y, 3))  #the 3 here is a magic number.
                #Adding round() here - it shouldn't reduce display accuracy by much, but it ought to cut down the size of the command string by a lot.
        self.pass_output()
#        print(self.command_string)
#        self.num_frames += 1
#        print ("Agents:", self.agents)

    def pass_output(self):
        markers = [s.player_marker() for s in self.ships
                   ] if len(self.ships) > 1 else ["" for s in self.ships]
        for i in range(len(self.connections)):
            cmds = (markers[i] + self.command_string).encode(
                'ascii')  #is this wasteful?
            len_data = str(len(cmds))
            to_send = ("0" *
                       (16 - len(len_data)) + len_data).encode('ascii') + cmds
            self.connections[i][0].sendall(to_send)
#            print(len(to_send))

    def add(self, agent):
        self.agents.append(agent)
#        self.command_string += "|create:" + agent.report() + "," + agent.get_type()

    def remove(self, agent):
        self.agents.remove(agent)
Example #2
0
class Game(Frame):

    # Game(name,w,h,ww,wh)
    #
    # Creates a world with a coordinate system of width w and height
    # h, with x coordinates ranging between -w/2 and w/2, and with y
    # coordinates ranging between -h/2 and h/2.
    #
    # Creates a corresponding graphics window, for rendering
    # the world, with pixel width ww and pixel height wh.
    #
    # The window will be named by the string given in name.
    #
    # The topology string is used by the 'trim' method to (maybe) keep
    # bodies within the frame of the world. (For example, 'wrapped'
    # yields "SPACEWAR" topology, i.e. a torus.)
    #
    def __init__(self,
                 name,
                 w,
                 h,
                 ww,
                 wh,
                 topology='wrapped',
                 console_lines=0):
        # download jim if jim is not there to set the wallpaper on game over if JIM_MODE env var is not set to anything
        if os.path.isfile('fix-james-d.jpg') == False:
            request.urlretrieve(
                'https://www.reed.edu/dean_of_faculty/faculty_profiles/profiles/photos/fix-james-d.jpg',
                'fix-james-d.jpg')
        self.wallpaperSet = False

        self.paused = False
        self.gameOver = False

        # Register the world coordinate and graphics parameters.
        self.WIDTH = w
        self.HEIGHT = h
        self.WINDOW_WIDTH = ww
        self.WINDOW_HEIGHT = wh
        self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2)
        self.topology = topology

        # Populate the world with creatures
        self.agents = []
        self.display = 'test'
        self.GAME_OVER = False

        # Populate the background with the walls for pacman
        self.prevWalls = None
        self.walls = []

        # Initialize the graphics window.
        self.root = Tk()
        self.root.title(name)
        # grab window focus after game starts
        self.root.after(500, lambda: self.root.grab_set_global())
        Frame.__init__(self, self.root)
        self.canvas = Canvas(self.root,
                             width=self.WINDOW_WIDTH,
                             height=self.WINDOW_HEIGHT)

        # Handle mouse pointer motion and keypress events.
        self.mouse_position = Point2D(0.0, 0.0)
        self.mouse_down = False
        self.bind_all('<Motion>', self.handle_mouse_motion)
        self.canvas.bind('<Button-1>', self.handle_mouse_press)
        self.canvas.bind('<ButtonRelease-1>', self.handle_mouse_release)
        self.bind_all('<Key>', self.handle_keypress)

        self.canvas.pack()
        if console_lines > 0:
            self.text = Text(self.root,
                             height=console_lines,
                             bg="#000000",
                             fg="#A0F090",
                             width=115)
            self.text.pack()
        else:
            self.text = None
        self.pack()

        # keep track of multiplayer
        self.otherPlayers = []
        self.socketID = []

    def trim(self, agent):
        if self.topology == 'wrapped':
            agent.position = self.bounds.wrap(agent.position)
        elif self.topology == 'bound':
            agent.position = self.bounds.clip(agent.position)
        elif self.topology == 'open':
            pass

    def add(self, agent):
        self.agents.append(agent)

    def remove(self, agent):
        self.agents.remove(agent)

    def update(self):
        # broadcast thread will put socket id in s queue once it connects
        if s.empty():
            pass
        else:
            self.socketID = s.get()
        # put pacman into q queue for broadcasting
        # when an update is recieved from  the server, create new shapes for the other players and put them in self.otherPlayers
        if self.PacMan and self.gameOver != True:
            q.put(self.PacMan)
            otherPlayers = p.get()
            self.otherPlayers = []
            for player in otherPlayers:
                if player != self.socketID:
                    # h = translate(x, 0, 30, -15, 15)
                    # v = translate(y, 0, 45, -22, 22) - .45
                    h = otherPlayers[player]['x']
                    v = otherPlayers[player]['y']
                    # print(h, v)
                    p1 = Point2D(.5 + h, .5 + v)
                    p2 = Point2D(-.5 + h, .5 + v)
                    p3 = Point2D(-.5 + h, -.5 + v)
                    p4 = Point2D(.5 + h, -.5 + v)

                    self.otherPlayers.append([p1, p2, p3, p4])

        # if the maze hasn't been drawn yet, draw the MazeBoundAgent
        # will re-draw the maze if the maze updates
        if self.prevWalls != self.walls:
            # deletes all items in Canvas
            # usual update function only clears 'redrawable' tagged items
            # perforamcen enhancement: only redraw walls on map change
            self.canvas.delete()
            self.drawBackground()
            self.prevWalls = self.walls
        if self.gameOver == True:
            self.paused = True
            self.canvas.create_text(200,
                                    200,
                                    font='inconsolata 50',
                                    fill='#FFF',
                                    text='game over\n' + self.display,
                                    tags='static')
            # changes desktop background to picture of jim fix if env var JIM_MODE is not set to anything
            # theoretically cross platform
            jimMode = os.environ.get('JIM_MODE')
            if self.wallpaperSet == False and jimMode == None:
                # load game over prize
                SCRIPT = """/usr/bin/osascript<<END
                tell application "Finder"
                set desktop picture to POSIX file "%s"
                end tell"""

                filename = os.getcwd() + '/fix-james-d.jpg'
                print(filename)
                try:
                    subprocess.Popen(SCRIPT % filename, shell=True)
                    self.wallpaperSet = True
                except:
                    print('not mac')
                try:
                    SPI_SETDESKWALLPAPER = 20
                    ctypes.windll.user32.SystemParametersInfoA(
                        SPI_SETDESKWALLPAPER, 0, "fix-james-d.jpg.jpg", 0)
                    self.wallpaperSet = True
                except:
                    print('not windows')
        if self.paused == False:
            for agent in self.agents:
                agent.update()
            self.clear()
            for agent in self.agents:
                self.draw_shape(agent.shape(), agent.color())
            # displays score and lives
            self.canvas.create_text(60,
                                    25,
                                    font='inconsolata 20',
                                    fill='#FFF',
                                    text=self.display,
                                    tags='redrawable')
            # draw other players
            for shape in self.otherPlayers:
                self.draw_shape(shape, 'purple')
        else:
            self.canvas.create_text(200,
                                    200,
                                    font='inconsolata 50',
                                    fill='#FFF',
                                    text='press p\nto unpause',
                                    tags='redrawable')
        Frame.update(self)

    # if tag 'static' is used, it will not be redrawn
    def draw_shape(self, shape, color, tag='redrawable'):
        wh, ww = self.WINDOW_HEIGHT, self.WINDOW_WIDTH
        h = self.bounds.height()
        x = self.bounds.xmin
        y = self.bounds.ymin
        points = [((p.x - x) * wh / h, wh - (p.y - y) * wh / h) for p in shape]
        first_point = points[0]
        points.append(first_point)
        self.canvas.create_polygon(points, fill=color, tags=tag)

    # draws maze outline
    def drawBackground(self):
        # black background
        self.canvas.create_rectangle(0,
                                     0,
                                     self.WINDOW_WIDTH,
                                     self.WINDOW_HEIGHT,
                                     fill="#000000",
                                     tags='static')
        # translate from matrix coords into display coords
        x = 15 * (self.WINDOW_WIDTH / self.WIDTH)
        y = 22 * (self.WINDOW_HEIGHT / self.HEIGHT)
        p1 = Point2D(.5, .5)
        p2 = Point2D(-.5, .5)
        p3 = Point2D(-.5, -.5)
        p4 = Point2D(.5, -.5)

        walls = self.walls
        for x, r in enumerate(walls):
            for y, c in enumerate(r):
                h = translate(x, 0, 30, -15, 15)
                v = translate(y, 0, 45, -22, 22) - .45
                if c > 0:
                    p1 = Point2D(.5 + h, .5 + v)
                    p2 = Point2D(-.5 + h, .5 + v)
                    p3 = Point2D(-.5 + h, -.5 + v)
                    p4 = Point2D(.5 + h, -.5 + v)

                    self.draw_shape([p1, p2, p3, p4], 'blue', 'static')

    def clear(self):
        self.canvas.delete('redrawable')

    def window_to_world(self, x, y):
        return self.bounds.point_at(x / self.WINDOW_WIDTH,
                                    1.0 - y / self.WINDOW_HEIGHT)

    def handle_mouse_motion(self, event):
        self.mouse_position = self.window_to_world(event.x, event.y)
        #print("MOUSE MOVED",self.mouse_position,self.mouse_down)

    def handle_mouse_press(self, event):
        self.mouse_down = True
        self.handle_mouse_motion(event)
        #print("MOUSE CLICKED",self.mouse_down)

    def handle_mouse_release(self, event):
        self.mouse_down = False
        self.handle_mouse_motion(event)
        #print("MOUSE RELEASED",self.mouse_down)

    def handle_keypress(self, event):
        if event.char == 'q':
            self.GAME_OVER = True
Example #3
0
class Game(Frame):

    # Game(name,w,h,ww,wh)
    #
    # Creates a world with a coordinate system of width w and height
    # h, with x coordinates ranging between -w/2 and w/2, and with y
    # coordinates ranging between -h/2 and h/2.
    #
    # Creates a corresponding graphics window, for rendering
    # the world, with pixel width ww and pixel height wh.
    #
    # The window will be named by the string given in name.
    #
    # The topology string is used by the 'trim' method to (maybe) keep
    # bodies within the frame of the world. (For example, 'wrapped'
    # yields "SPACEWAR" topology, i.e. a torus.)
    #
    def __init__(self,
                 name,
                 w,
                 h,
                 ww,
                 wh,
                 topology='wrapped',
                 console_lines=0):

        # Register the world coordinate and graphics parameters.
        self.WINDOW_WIDTH = ww
        self.WINDOW_HEIGHT = wh
        self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2)
        self.topology = topology

        # Populate the world with creatures
        self.agents = []
        self.GAME_OVER = False
        self.PAUSE_GAME = False

        # Initialize the graphics window.
        self.root = Tk()
        self.root.title(name)
        Frame.__init__(self, self.root)
        self.canvas = Canvas(self.root,
                             width=self.WINDOW_WIDTH,
                             height=self.WINDOW_HEIGHT)

        # Handle mouse pointer motion and keypress events.
        self.mouse_position = Point2D(0.0, 0.0)
        self.mouse_down = False
        self.bind_all('<Motion>', self.handle_mouse_motion)
        self.canvas.bind('<Button-1>', self.handle_mouse_press)
        self.canvas.bind('<ButtonRelease-1>', self.handle_mouse_release)
        self.bind_all('<Key>', self.handle_keypress)

        self.canvas.pack()
        if console_lines > 0:
            self.text = Text(self.root,
                             height=console_lines,
                             bg="#000000",
                             fg="#A0F090",
                             width=115)
            self.text.pack()
        else:
            self.text = None
        self.pack()

    def report(self, line=""):
        line += "\n"
        if self.text == None:
            print(line)
        else:
            self.text.insert(END, line)
            self.text.see(END)

    def trim(self, agent):
        if self.topology == 'wrapped':
            agent.position = self.bounds.wrap(agent.position)
        elif self.topology == 'bound':
            agent.position = self.bounds.clip(agent.position)
        elif self.topology == 'open':
            pass

    def add(self, agent):
        self.agents.append(agent)

    def remove(self, agent):
        self.agents.remove(agent)

    def update(self):
        if not self.PAUSE_GAME:
            for agent in self.agents:
                agent.update()
            self.clear()
            for agent in self.agents:
                self.draw_shape(agent.shape(), agent.color())

        Frame.update(self)

    def draw_shape(self, shape, color):
        wh, ww = self.WINDOW_HEIGHT, self.WINDOW_WIDTH
        h = self.bounds.height()
        x = self.bounds.xmin
        y = self.bounds.ymin
        points = [((p.x - x) * wh / h, wh - (p.y - y) * wh / h) for p in shape]
        first_point = points[0]
        points.append(first_point)
        self.canvas.create_polygon(points, fill=color)

    def clear(self):
        self.canvas.delete('all')
        self.canvas.create_rectangle(0,
                                     0,
                                     self.WINDOW_WIDTH,
                                     self.WINDOW_HEIGHT,
                                     fill="#000000")

    def window_to_world(self, x, y):
        return self.bounds.point_at(x / self.WINDOW_WIDTH,
                                    1.0 - y / self.WINDOW_HEIGHT)

    def handle_mouse_motion(self, event):
        self.mouse_position = self.window_to_world(event.x, event.y)
        #print("MOUSE MOVED",self.mouse_position,self.mouse_down)

    def handle_mouse_press(self, event):
        self.mouse_down = True
        self.handle_mouse_motion(event)
        #print("MOUSE CLICKED",self.mouse_down)

    def handle_mouse_release(self, event):
        self.mouse_down = False
        self.handle_mouse_motion(event)
        #print("MOUSE RELEASED",self.mouse_down)

    def handle_keypress(self, event):
        if event.char == 'q':
            self.GAME_OVER = True
        elif event.char == 'p':
            # pause game
            if self.PAUSE_GAME == False:
                self.PAUSE_GAME = True
            else:
                self.PAUSE_GAME = False
Example #4
0
class Game(Frame):
    def __init__(self, name, w, h, ww, wh, topology='wrapped'):

        #initializes world and window geometry
        self.WINDOW_WIDTH = ww
        self.WINDOW_HEIGHT = wh
        self.bounds = Bounds(-w / 2, -h / 2, w / 2, h / 2)
        self.topology = topology

        self.agents = []

        self.root = Tk()
        self.root.title(name)

        Frame.__init__(self, self.root)

        self.bind_all('<KeyPress>', self.keypress)
        self.bind_all('<KeyRelease>', self.keyrelease)

        #makes background canvas
        self.canvas = Canvas(self,
                             width=self.WINDOW_WIDTH,
                             height=self.WINDOW_HEIGHT,
                             bg='purple')

        self.grid()
        self.canvas.grid()

        #sets the top left corner of the display window to the actual (0,0). Was having
        #some weird issues with the edges of the world before, like the window was
        #at (3,3) instead or something weird like that.
        self.canvas.xview_moveto(0.0)
        self.canvas.yview_moveto(0.0)

    def trim(self, agent):
        if self.topology == 'wrapped':
            agent.position = self.bounds.wrap(agent.position)
        elif self.topology == 'bound':
            agent.position = self.bounds.clip(agent.position)
        elif self.topology == 'open':
            pass

    def walltrim(self, agent):
        agent.position = self.wallbounds.hitboxtrim(agent.position,
                                                    agent.size / 2)

    def add(self, agent):
        self.agents.append(agent)

    def remove(self, agent):
        self.agents.remove(agent)
        self.bullets.remove(agent)

    def update(self):
        pass

    def worldToPixel(self, shape):
        #broke up the drawing function in order to get the translation of world geometry
        #to window geometry. v handy in lots of situations where I don't want something
        #drawn immediately but I want to know the window points.
        wh, ww = self.WINDOW_HEIGHT, self.WINDOW_WIDTH
        h = self.bounds.height()
        x = self.bounds.xmin
        y = self.bounds.ymin
        points = [((p.x - x) * wh / h, wh - (p.y - y) * wh / h) for p in shape]
        return points

    def drawagent(self, shape, color):
        points = self.worldToPixel(shape)
        return self.canvas.create_rectangle(points,
                                            fill=color,
                                            width=0,
                                            tags='agent')

    def draw_poly(self, shape, color, tags):
        points = self.worldToPixel(shape)
        first_point = points[0]
        points.append(first_point)
        return self.canvas.create_polygon(points,
                                          width=0,
                                          fill=color,
                                          tags=tags)

    def draw_oval(self, shape, color, tags):
        points = self.worldToPixel(shape)
        first_point = points[0]
        points.append(first_point)
        return self.canvas.create_polygon(points,
                                          width=0,
                                          fill=color,
                                          smooth=1,
                                          tags=tags)

    def keypress(self, event):
        pass

    def keyrelease(self, event):
        pass