20

I want to write a gomoku game with server and client. The terminal version works well, but pygame version just blocked and can't rend anything.

Here is the game execute function

  • First it start a socket connection

  • self._running = True in init, package get from server is like {"grid":GRID(strings with 0 and 1), "x":X, "y":Y, "player":LAST_PLAYER, "next_player":CURRENT_PLAYER, "gameover":IS_GAMEOVER}

  • In the the loop:

    • parse the package
    • check if gameover, if so show information about gameover and close the socket
    • if current player is me, call on_event and let user move( I suspect this step is wrong, so the parse package step blocked the main thread? But how should I fix this ) and then send the new move package to server
    • if current player is not me, then send a special package to server I am waiting(I know this is a bit stupid)

This is the loop like

        while self._running:
            data = self.client_thread.reply_q.get(True)
            
            if data:
                self.last_player = data["player"]
                self.grid = self.grid_str_2_matrix(data["grid"])
                self.lastPosition = [data["x"], data["y"]]
                self.gomoku_board_init()
                
                if data["gameover"] == -1:
                    if data["next_player"] != self.player:
                        self.client_thread.cmd_q.put(ClientCommand(ClientCommand.SEND, {"wait": True}))
                        print("waiting")
                    else:
                        for event in pygame.event.get():
                            self.on_event(event)

                        print("new move")
                else:
                    print("game over")
                    self._running = False
                    if data["gameover"] == 0:
                        self.winner = 0
                    else:
                        self.winner = data["player"]
                    self.client_thread.cmd_q.put(ClientCommand(ClientCommand.CLOSE))
                    break
            self.on_render()
        self.on_cleanup()

and the on_event function be called in the middle to accepet user's next move

if data["gameover"] == -1:
    if data["next_player"] != self.player:
        ...
    else:    
        for event in pygame.event.get():
            self.on_event(event)

code as this

def on_event(self, event):
    print(event.type == pygame.MOUSEBUTTONUP)
    

    if event.type == pygame.MOUSEBUTTONUP:
        pos = pygame.mouse.get_pos()
        r = (pos[0] - PADDING + WIDTH // 2) // (WIDTH + MARGIN)
        c = (pos[1] - PADDING + WIDTH // 2) // (WIDTH + MARGIN)
        print(r, c)
        if 0 <= r < self.board_row and 0 <= c < self.board_column and self.grid[r][c] == 0:
            self.grid[r][c] = self.player
            data = {"grid":self.grid, "x":r, "y":c, "player":self.player}
            self.client_thread.cmd_q.put(ClientCommand(ClientCommand.SEND, data))

What I've tried:

  • I added a print in on_event print(event.type == pygame.MOUSEBUTTONUP) and of course MOUSEBUTTONUP never happened(But I wonder why?)

  • So I decide just skip this using random input

code as follows

#for event in pygame.event.get():
#    self.on_event(event)
x, y = self.random_position()
self.grid[x][y] = self.player
data = {"grid":self.grid, "x":x, "y":y, "player":self.player}
self.client_thread.cmd_q.put(ClientCommand(ClientCommand.SEND, data))

The result is package goes all right BUT GUI still blocking and even I add sleep in the while loop, it rended only when gameover

I am new in python multithread and also pygame with socket, I wrote a just pygame part it works good, so does terminal+socket.

Laura White
  • 188
  • 2
  • 9
Aries_is_there
  • 386
  • 2
  • 13
  • 6
    Are you able to reduce this problem to a [mcve]? That will make it possible for people to help you. I suspect your issue is with handing events. – import random Oct 05 '18 at 03:25
  • @Eric Thank you for your suggestion, I will edit it – Aries_is_there Oct 05 '18 at 05:03
  • 1
    If you don't process [events](http://www.pygame.org/docs/ref/event.html) your OS will report that your application is not responding. Is this what you mean by the GUI is blocked? Unfortunately, I'm unable to run your code as it's incomplete. Perhaps you can instead get your other thread to generate events that are then handled consistently by your mainloop. This [answer](https://stackoverflow.com/a/15557671/7675174) indicates that you can use [pygame.event.post](https://www.pygame.org/docs/ref/event.html#pygame.event.post) safely from another thread. – import random Oct 16 '18 at 00:55
  • The answer to this problem very much depends on more context. We need to be seeing exactly which python modules you use. If you have built your own native modules which multithread, you'll need to lock the GIL in certain situations. You could start by running your program through `gdb python` and backtracing when it's hung. This will show you what kind of code was executing at the time. – kevr Oct 09 '21 at 01:19
  • I experienced it to be good practice to wrap the code within all callbacks ( in this case: on_event ) into try-except blocks. The reason is that exceptions within callbacks can cause strange behaviour of the caller – Pablo Henkowski Nov 03 '21 at 19:11

2 Answers2

1

You are reading from a reply queue with a param block=True. Which means that it is blocking whole while loop. And that on_render call is blocked as well by reply_q.get(True). So if you are not constantly feeding reply_q with messages then your screen is not going to be re-rendered. By the way, your events processing code is also gonna be blocked.

docs.python#Queue.get

petraszd
  • 4,249
  • 1
  • 20
  • 12
0

Usually those kind of issues in Pygame and other GUI frameworks come from the special role the main thread has. Quoting from pygame's docs:

Pygame handles all its event messaging through an event queue. The routines in this module help you manage that event queue. The input queue is heavily dependent on the pygame.displaypygame module to control the display window and screen module. If the display has not been initialized and a video mode not set, the event queue may not work properly. The event subsystem should be called from the main thread. If you want to post events into the queue from other threads, please use the pygame.fasteventpygame module for interacting with events and queues module.

Bastian Venthur
  • 12,515
  • 5
  • 44
  • 78