0

I'm building a game client in Pygame, that connects to a server which returns a list of entities(players, monsters, npc, items etc). The Pygame window then draws that info on the screen and updates according to the changes received from the server. For some reason the Pygame Window stops responding when I click on it with the mouse, and I'm not sure what to do to fix it.[text](

import pygame


import requests


import threading


import time


from emptyjsonerror import EmptyJSONError





server_url = "localhost:5000"





global username


username = None











class GameClient:


    """A class that represents a game client that communicates with a server and displays the game state using pygame."""








    def __init__(self, server_url, screen_size, tile_size, color):


        """Create a GameClient object with the given server URL, screen size, tile size and color."""


        self.server_url = server_url


        self.screen_size = screen_size


        self.tile_size = tile_size


        self.color = color


        pygame.init()


        self.screen = pygame.display.set_mode(screen_size)


        pygame.display.set_caption("Simple Client")


        self.entities = []


        self.images = {}


        self.lock = threading.Lock()





    def get_tile_map(self):


        """Get the tile map from the server as a list of dictionaries."""


        response = requests.get(self.server_url + "/map")


        if not response.content or response.headers.get("Content-Type") != "application/json":


            raise EmptyJSONError("Empty or invalid JSON response from server", response.url)


        return response.json()





    def draw_tile_map(self, tile_map):


        """Draw the tile map on the screen using green squares."""


        for tile in tile_map:


            x = tile["x"]


            y = tile["y"]


            pixel_x = x * self.tile_size[0]


            pixel_y = y * self.tile_size[1]


            rect = pygame.Rect(pixel_x, pixel_y, self.tile_size[0], self.tile_size[1])


            pygame.draw.rect(self.screen, self.color, rect)





    def get_entities(self, debug=False):


        """Get and update and draw the entities from the server as a list of dictionaries."""


        if debug:


            dummy_entity = {


                "type": "Player",


                "id": 0,


                "x": 5,


                "y": 5,


                "level": 1,


                "health": 100


                }


            existing_entity = next((e for e in self.entities if e.get("type") == dummy_entity.get("type") and e.get("id") == dummy_entity.get("id")), None)


            if not existing_entity:


                #self.lock.acquire()


                self.entities.append(dummy_entity)


                #self.lock.release()





        else:


            response = requests.get(self.server_url + "/entities", params={"username": username})


            print(f"Entities: {response.json()}")  # Print statement added for debugging





            if not response.content or response.headers.get("Content-Type") != "application/json":





                raise EmptyJSONError("Empty or invalid JSON response from server", response.url)





            new_entities = response.json()





            if not self.entities:


                #self.lock.acquire()


                self.entities = new_entities


                #self.lock.release()





            else:


                for new_entity in new_entities:





                    type = new_entity.get("type")


                    id = new_entity.get("id")





                    old_entity = next((e for e in self.entities if e.get("type") == type and e.get("id") == id), None)





                    if not old_entity:


                        #self.lock.acquire()


                        self.entities.append(new_entity)


                        #self.lock.release()





                    else:


                        old_entity.update(new_entity)





                for old_entity in self.entities:





                    type = old_entity.get("type")


                    id = old_entity.get("id")





                    new_entity = next((e for e in new_entities if e.get("type") == type and e.get("id") == id), None)





                    if not new_entity:


                        #self.lock.acquire()


                        self.entities.remove(old_entity)


                        #self.lock.release()





        time.sleep(1/60)











    def draw_entities(self):


        """Draw the entities on the screen using different colors and shapes."""





        for entity in self.entities:





            type = entity.get("type")


            id = entity.get("id")


            x = entity.get("x")


            y = entity.get("y")


            level = entity.get("level")


            health = entity.get("health")





            pixel_x = x * self.tile_size[0] + self.tile_size[0] // 2


            pixel_y = y * self.tile_size[1] + self.tile_size[1] // 2





            if type not in self.images:


                image_file = f"{type}.png"  # Assume that there is an image file for each entity type with its name as filename


                image = pygame.image.load(image_file)  # Load the image using pygame.image.load function


                self.images[type] = image  # Store the image in the images dictionary with its type as key





            image = self.images[type]


            print("drawing at (" + str(pixel_x) + ', ' + str(pixel_y) + ')')


            self.screen.blit(image, (pixel_x - image.get_width() // 2, pixel_y - image.get_height() // 2))





    def draw_loop(self):


        while True:


            self.screen.fill((0, 0, 0))


            tile_map = self.get_tile_map()


            self.draw_tile_map(tile_map)


            #print("debug draw_loop lock")


            #self.lock.acquire()


            self.draw_entities()


            #print("debug draw_loop release")


            #self.lock.release()


            pygame.display.update()


            fps_limit = 1/60


            time.sleep(fps_limit)











    def client_loop(self):


        """Run a client loop that gets and draws the tile map until the user quits."""


        try:





            entity_thread = threading.Thread(target=self.entity_loop)


            draw_thread = threading.Thread(target=self.draw_loop)





            entity_thread.start()


            draw_thread.start()


            run = True


            while run:


                events = pygame.event.get()


                for event in events:


                    if event.type == pygame.quit:


                        run = False


                        pygame.quit()


                        sys.exit()


                    elif event.type == pygame.MOUSEBUTTONDOWN:


                        print("mouse button pressed")


                    elif event.type == pygame.KEYDOWN:


                        print("keyboard button pressed")


                #draw_loop()


                #time.sleep(1/60)








        except EmptyJSONError as e:





            print(f"Error: {e} from {e.request_url}")











    def get_and_print_events(self):





        events = pygame.event.get()





        for event in events:





            print(event)








    def entity_loop(self):


        """Run an entity loop that gets and draws the entities from the server every 0.1 seconds."""


        try:


            while True:





                self.get_entities()


                #self.draw_entities()


                # Wait for 0.1 seconds before repeating


                time.sleep(0.1)


        except Exception as e:





            print(f"Error: {e}")

















screen_size = (320, 320)








tile_size = (32, 32)








green = (0, 255, 0)


)

I've tried implementing a dummy entity in place of the info from the server and it makes no difference. I've also tried turning off the threads for the draw entities and get entities loops, and those have made no difference either. It seems like it's an issue with my main loop, but I'm not sure what the cause is.

toyota Supra
  • 3,181
  • 4
  • 15
  • 19
  • Pygame's concurrency support is poor. See [Concurrency support](https://github.com/pygame/pygame/issues/2806) – Rabbid76 Jul 31 '23 at 19:51
  • You don't need a `draw_loop`, yous just need a `draw` function. Remove `while True` from `draw_loop`. the system sops responding when you have a loop without event handling. – Rabbid76 Jul 31 '23 at 19:55
  • @Rabbid76, so I tried removing while True, from the draw_loop function, and placing the draw function in the main client loop, but my issue still persists. Anything else you can recommend? – Ryan Lupton Jul 31 '23 at 20:57

0 Answers0