user14249883
user14249883

Reputation:

Multiplayer program in pygame running very slow after added a bit of code

I have a pygame/socket program that is basically a maze game where you have to 'tag' the other player, but when i added a send code the program has slowed down drastically, the program is fullscreen, I added another socket send line to make shure the seekers are the same then it stopped running fast. Hears my code:

My first end:

import pygame
import time
import random
import socket
import struct
pygame.init()
s = socket.socket()
port = 12345
s.bind(('', port))
s.listen(5)
c, addr = s.accept()
print ("Socket Up and running with a connection from",addr)
screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
blocks = [(50, 50, 200, 25), (200, 50, 25, 250), (300, 200, 200, 25), (50, 200, 25, 200), (250, 100, 25, 200),
          (300, 250, 25, 200), (350, 225, 25, 200), (75, 325, 200, 25), (25, 150, 150, 25), (100, 200, 75, 100),
          (100,375,175,25), (250, 425,25,75),(250,500,175,25),(300,450,100,25),
          (100,425,25,150),(150,425,75,100),(100,550,300,25),(50,425,25,175),(50,600,150,25),
          (0,100,175,25),(0,200,25,200),(0,425,25,200),(0,0,25,100),(0,0,250,25),(50,400,25,25),
          (0,650,100,25),(125,625,25,100),(0,750,10000,28),(1350,0,16,10000),(225,600,25,125),
          (175,650,25,100),(25,700,100,25),(275,0,10000,25),(275,50,200,25),(300,75,175,25),
          (275,125,225,50),(500,200,25,300),(400,250,25,225),(425,450,25,25),(450,250,25,275),
          (425,550,100,25),  (500,500,25,25),(500,0,25,500),(275,25,25,25),(550,50,25,475),
          (275,575,25,175),  (325,600,25,125),(350,600,25,25),(375,600,25,150),(425,575,25,150),
          (475,600,25,150), (525,550,50,175), (600,525,25,225),(650,575,175,25),(650,525,175,25),
          (850,475,25,250), (650,625,200,25), (650,600,25,25),(650,675,175,25),(650,700,25,50),
          (700,725,125,25), (900,500,25,300),(850,450,200,25),(950,500,100,25),(950,225,25,200),
          (800,300,125,25),(850,300,25,100),(900,325,25,100),(800,250,125,25),(800,200,200,25),
          (600,475,225,25), (600,300,25,200),(650,275,25,175),(700,300,25,225),(550,250,200,25),
          (750,250,25,200), (600,200,175,25),(550,150,300,25),(875,0,25,200),(800,350,25,100),
          (600,100,500,25),(575,50,275,25),(925,150,100,25),(1025,150,25,200),(1050,150,100,25),(1125,50,25,375),
          (1075,200,25,200),(1025,350,25,75),(1050,450,275,25),(1075,400,25,50),(1175,200,250,25),(1150,150,175,25),
          (1175,100,175,25), (1150,50,175,25),(925,50,200,25),(1150,250,175,25),(1300,275,25,150),(1250,300,25,150),(1175,300,50,125),
          (1075,500,250,25),(950,500,25,225),(1000,550,25,225),(1050,450,25,275),(1100,550,25,275),
          (1150,525,25,200),(1150,700,175,25),(1200,650,175,25),(1150,600,175,25),(1200,550,175,25)
          ]
walls = [pygame.Rect(r) for r in blocks]
Svel = 5
Hvel = 5
red = pygame.Rect(225, 225, 25, 25)
blue = pygame.Rect(1100, 300, 25, 25)
SeekChoice = random.randint(1,2)
if SeekChoice == 1:
    seeker = "red"
    Snum = 2
if SeekChoice == 2:
    seeker = "blue"
    Snum = 1
Num = 0
c.send(struct.pack("2i", SeekChoice, Num))
RedScore = 0
BlueScore = 0
RTime = 0
BTime = 0
running = True
font = pygame.font.Font(None, 60)
RedWin = font.render("RED IS WINNER!", 100,(255,0,0))
BlueWin = font.render("BLUE IS WINNER!", 100,(0,0,255))
while running:
    buf = c.recv(8, socket.MSG_WAITALL)
    struct.unpack("2i", buf)
    x, y = struct.unpack("2i", buf)
    red = pygame.Rect(x,y,25,25)
    
    if RedScore >= 20:
        screen.fill([255,255,255])
        screen.blit(RedWin, [500,250])
        pygame.display.flip()
        time.sleep(10)
        pygame.quit()
        running = False
    if BlueScore >= 20:
        screen.fill([255,255,255])
        screen.blit(BlueWin, [500,250])
        pygame.display.flip()
        time.sleep(10)
        pygame.quit()
        running = False
    RTime += 1
    BTime += 1
    x1 = red.x
    y1 = red.y
    x2 = blue.x
    y2 = blue.y
    
    if seeker == "red":
        RTime = 0
    elif RTime >= 2000:
        RTime = 0
        RedScore += 1
    if seeker == "blue":
        BTime = 0
    elif BTime >= 2000:
        BTime = 0
        BlueScore += 1
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                pygame.quit()
                running = False
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        blue.x = max(blue.x - Svel, 0)
        for wall in walls: 
            if blue.colliderect(wall):
                blue.left = max(blue.left, wall.right)
    if keys[pygame.K_RIGHT]:
        blue.x = min(blue.x + Svel, 1341)
        for wall in walls: 
            if blue.colliderect(wall):
                blue.right = min(blue.right, wall.left)
    if keys[pygame.K_UP]:
        blue.y = max(blue.y - Svel, 0)
        for wall in walls: 
            if blue.colliderect(wall):
                blue.top = max(blue.top, wall.bottom)
    if keys[pygame.K_DOWN]:
        blue.y = min(blue.y + Svel, 743)
        for wall in walls: 
            if blue.colliderect(wall):
                blue.bottom = min(blue.bottom, wall.top)
    screen.fill([0,0,0])
    #pygame.draw.circle(screen,[255,255,255],(x1+12,y1+12),125)
    pygame.draw.circle(screen,[255,255,255],(x2+12,y2+12),125)
    for wall in walls: 
        pygame.draw.rect(screen, [0,0,0], wall)
    #pygame.draw.rect(screen, [255,0,0], red)
    pygame.draw.rect(screen, [0,0,255], blue)
    if seeker == "red":
        pygame.draw.rect(screen,[0,0,0],(red.x+7,red.y+7,10,10))
    if seeker == "blue":
        pygame.draw.rect(screen,[0,0,0],(blue.x+7,blue.y+7,10,10))
    pygame.display.flip()
    if red.colliderect(blue):
        if seeker == "red":
            print ("red/blue")
            seeker = "blue"
            Snum = 1
            BTime = 0
            RedScore += 2
            BlueScore -= 3
            red = pygame.Rect(225, 225, 25, 25)
            blue = pygame.Rect(1166, 334, 25, 25)
        elif seeker == "blue":
            print ("blue/red")
            Snum = 2
            RTime = 0
            seeker = "red"
            BlueScore += 2
            RedScore -= 3
            red = pygame.Rect(225, 225, 25, 25)
            blue = pygame.Rect(1166, 334, 25, 25)
    seeker2 = seeker.encode()
    c.send(seeker2)
    c.send(struct.pack("2i", blue.x, blue.y))

my second end:

import pygame
import time
import random
import socket
import struct
pygame.init()

s = socket.socket()
s.connect(('192.168.43.188',12345))

screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
blocks = [(50, 50, 200, 25), (200, 50, 25, 250), (300, 200, 200, 25), (50, 200, 25, 200), (250, 100, 25, 200),
          (300, 250, 25, 200), (350, 225, 25, 200), (75, 325, 200, 25), (25, 150, 150, 25), (100, 200, 75, 100),
          (100,375,175,25), (250, 425,25,75),(250,500,175,25),(300,450,100,25),
          (100,425,25,150),(150,425,75,100),(100,550,300,25),(50,425,25,175),(50,600,150,25),
          (0,100,175,25),(0,200,25,200),(0,425,25,200),(0,0,25,100),(0,0,250,25),(50,400,25,25),
          (0,650,100,25),(125,625,25,100),(0,750,10000,28),(1350,0,16,10000),(225,600,25,125),
          (175,650,25,100),(25,700,100,25),(275,0,10000,25),(275,50,200,25),(300,75,175,25),
          (275,125,225,50),(500,200,25,300),(400,250,25,225),(425,450,25,25),(450,250,25,275),
          (425,550,100,25),  (500,500,25,25),(500,0,25,500),(275,25,25,25),(550,50,25,475),
          (275,575,25,175),  (325,600,25,125),(350,600,25,25),(375,600,25,150),(425,575,25,150),
          (475,600,25,150), (525,550,50,175), (600,525,25,225),(650,575,175,25),(650,525,175,25),
          (850,475,25,250), (650,625,200,25), (650,600,25,25),(650,675,175,25),(650,700,25,50),
          (700,725,125,25), (900,500,25,300),(850,450,200,25),(950,500,100,25),(950,225,25,200),
          (800,300,125,25),(850,300,25,100),(900,325,25,100),(800,250,125,25),(800,200,200,25),
          (600,475,225,25), (600,300,25,200),(650,275,25,175),(700,300,25,225),(550,250,200,25),
          (750,250,25,200), (600,200,175,25),(550,150,300,25),(875,0,25,200),(800,350,25,100),
          (600,100,500,25),(575,50,275,25),(925,150,100,25),(1025,150,25,200),(1050,150,100,25),(1125,50,25,375),
          (1075,200,25,200),(1025,350,25,75),(1050,450,275,25),(1075,400,25,50),(1175,200,250,25),(1150,150,175,25),
          (1175,100,175,25), (1150,50,175,25),(925,50,200,25),(1150,250,175,25),(1300,275,25,150),(1250,300,25,150),(1175,300,50,125),
          (1075,500,250,25),(950,500,25,225),(1000,550,25,225),(1050,450,25,275),(1100,550,25,275),
          (1150,525,25,200),(1150,700,175,25),(1200,650,175,25),(1150,600,175,25),(1200,550,175,25)
          ]

walls = [pygame.Rect(r) for r in blocks]
Svel = 5
Hvel = 5
red = pygame.Rect(225, 225, 25, 25)
blue = pygame.Rect(1100, 300, 25, 25)
buf = s.recv(8, socket.MSG_WAITALL)
SeekChoice, Num = struct.unpack("2i", buf)
if SeekChoice == 1:
    seeker = "red"
    Snum = 1
if SeekChoice == 2:
    seeker = "blue"
    Snum = 2
RedScore = 0
BlueScore = 0
RTime = 0
BTime = 0
running = True
font = pygame.font.Font(None, 60)
RedWin = font.render("RED IS WINNER!", 100,(255,0,0))
BlueWin = font.render("BLUE IS WINNER!", 100,(0,0,255))

screen.fill([255,255,255])
pygame.draw.circle(screen,[255,255,255],(red.x+12,red.y+12),75)
pygame.draw.circle(screen,[255,255,255],(blue.x+12,blue.y+12),75)
for wall in walls: 
    pygame.draw.rect(screen, [0,0,0], wall)
pygame.draw.rect(screen, [255,0,0], red)
pygame.draw.rect(screen, [0,0,255], blue)
if seeker == "red":
    pygame.draw.rect(screen,[0,0,0],(red.x+7,red.y+7,10,10))
if seeker == "blue":
    pygame.draw.rect(screen,[0,0,0],(blue.x+7,blue.y+7,10,10))
pygame.display.flip()
while running:
    if Snum == 1:
        seeker = "red"
    if Snum == 2:
        seeker = "blue"
    s.send(struct.pack("2i", red.x, red.y))
    if RedScore >= 20:
        screen.fill([255,255,255])
        screen.blit(RedWin, [500,250])
        pygame.display.flip()
        time.sleep(10)
        pygame.quit()
        running = False
    if BlueScore >= 20:
        screen.fill([255,255,255])
        screen.blit(BlueWin, [500,250])
        pygame.display.flip()
        time.sleep(10)
        pygame.quit()
        running = False
    RTime += 1
    BTime += 1

    if seeker == "red":
        RTime = 0
    elif RTime >= 2000:
        RTime = 0
        RedScore += 1
    if seeker == "blue":
        BTime = 0
    elif BTime >= 2000:
        BTime = 0
        BlueScore += 1
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                pygame.quit()
                running = False
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        red.x = max(red.x - Svel, 0)
        for wall in walls: 
            if red.colliderect(wall):
                red.left = max(red.left, wall.right)
    if keys[pygame.K_RIGHT]:
        red.x = min(red.x + Svel, 1341)
        for wall in walls: 
            if red.colliderect(wall):
                red.right = min(red.right, wall.left)
    if keys[pygame.K_UP]:
        
        red.y = max(red.y - Svel, 0)
        for wall in walls: 
            if red.colliderect(wall):
                red.top = max(red.top, wall.bottom)
    if keys[pygame.K_DOWN]:
        red.y = min(red.y + Svel, 743)
        for wall in walls: 
            if red.colliderect(wall):
                red.bottom = min(red.bottom, wall.top)
    screen.fill([0,0,0])
    pygame.draw.circle(screen,[255,255,255],(red.x+12,red.y+12),125)
    #pygame.draw.circle(screen,[255,255,255],(blue.x+12,blue.y+12),125)
    for wall in walls: 
        pygame.draw.rect(screen, [0,0,0], wall)
    pygame.draw.rect(screen, [255,0,0], red)
    #pygame.draw.rect(screen, [0,0,255], blue)
    if seeker == "red":
        pygame.draw.rect(screen,[0,0,0],(red.x+7,red.y+7,10,10))
    if seeker == "blue":
        pygame.draw.rect(screen,[0,0,0],(blue.x+7,blue.y+7,10,10))
    pygame.display.flip()
    seeker = s.recv(1024)
    seeker = seeker.decode()
    buf = s.recv(8, socket.MSG_WAITALL)
    x, y = struct.unpack("2i", buf)
    blue = pygame.Rect(x,y,25,25)
    red = pygame.Rect(red.x,red.y,25,25)

Any help is appreciated.

Upvotes: 2

Views: 150

Answers (1)

Kingsley
Kingsley

Reputation: 14924

Your socket handling code is (probably) responsible for the slowdown. The socket.recv() is waiting for data to arrive. Sure you can just set the socket to non-blocking read, but you still have the issue of whether a full data-packet has arrived.

I would convert your code to use fixed packet sizes. Instead of sending a variable-length colour name, why not just send a colour tuple. It's much quicker to send [ 250 250 210 ], then "light goldenrod yellow" ;) I guess you could just use [red ] and [blue].

Once you have fixed packet sizes, then you can know exactly when a new packet has arrived, versus say half a packet. Sure for tiny packets on the local network (where there isn't so much fragmentation) it's not really an issue. But data sizes grow, network topologies change, fragmentation occurs.

I like to use the select module (which closely models the C system-function of the same name) to know if any data has arrived on the socket, and if there's nothing then simply return the the main program loop. But if data is available, read whatever there is into a buffer. This gives you a program model where you read whatever-has-arrived (most of the time nothing), and if there's enough bytes in the "arrived" buffer, then take enough bytes out to form a full-packet. It's simple, robust and works well.

So given the function:

import socket
import select

def socketReadIfAvailable( read_socket:socket.socket, packet_buffer:bytearray, amount:int=1, timeout:float=0 ):
    """ Given an open socket, and a packet-buffer, return a packet if one is available. 
        Returns a tuple of: 
            None        - if no packet is available
            data-packet - (of <amount> bytes) if data is available (of <amount> bytes length)
            -1          - if the socket has closed  
            AND the current packet-buffer """
    result = None
    # If we already have enough data buffered, don't re-fetch
    if ( len( packet_buffer ) >= amount ):
        bytes_read  = packet_buffer[0:amount]
        packet_buffer = packet_buffer[amount:]
        result = bytes_read
    else:
        # There's not enough data, so try to read some, but only for <timeout> seconds
        # if timeout is zero, just read whatever is sitting in the socket buffer already
        read_events_on   = [ read_socket ]
        (read_list, write_list, except_list) = select.select( read_events_on, [], [], timeout )

        if ( len( read_list ) > 0 ):
            # New data arrived, read it
            incoming = read_socket.recv( 8192 )
            if ( len( incoming ) == 0 ):
                # No data arrived, meaning the socket has closed
                result = -1
            else:
                #print("%d bytes Rx'd" % ( len( incoming ) ) )
                #print("%d bytes buffered" % ( len( packet_buffer ) ) )
                packet_buffer += incoming  # new bytes arrived, is there enough data now?
                if ( len( packet_buffer ) >= amount ):
                    bytes_read  = packet_buffer[0:amount]
                    packet_buffer = packet_buffer[amount:]
                    result = bytes_read
    return result, packet_buffer

And a fixed-sized packet of 11 bytes ( 3 bytes for the RGB colour, and 4 bytes for each co-ordinate ) your reading code could be something like:

packet_buffer = bytearray()

...

pygame.display.flip()
packet, packet_buffer = socketReadIfAvailable( s, packet_buffer, 11 )
if ( packet != None ):
    if ( packet == -1 ):
        # TODO: handle Socket has closed
        packet_buffer = bytearray()  # clear buffer            
    else:
        colour = struct.unpack( "!BBB", packet[0:3] )   # use network byte-order
        x,y    = struct.unpack( "!2i",  packet[3:11] )

Of course if you don't want to use fixed sizes, you can just see if any data has arrived, and try: to unpack (or unpickle) it and hope for the best. That's relatively inefficient though.

EDIT

Normally I try not to be an unpaid code-writing service, but I was convinced by an interesting looking proto-game ;)

Ok, I'm not really sure how your game is supposed to work. I found the whole red and blue variable set confusing. I think you should re-engineer your code to have a single server that has no GUI, and simply shunts updated positions between clients. Then you can have a single game-code where both players (or N players) connect to that server. This would also remove the red Vs blue from your code, leaving "local" and "others" (or whatever you want to call them).

I modified your code to only send positions when the x and y co-ordinates have changed. There's no need to spam the network with the same unchanged co-ordinates 60 times a second.

Also consider adding comments, and maybe the occasional blank line to your code, it makes it much easier to follow. I added this to one of the files.

Server:

import pygame
import time
import random
import socket
import struct

import select
network_buffer = bytearray()

def socketReadIfAvailable( read_socket:socket.socket, packet_buffer:bytearray, amount:int=1, timeout:float=0 ):
    """ Given an open socket, and a packet-buffer, return a packet if one is available.
        Returns a tuple of:
            None        - if no packet is available
            data-packet - (of <amount> bytes) if data is available (of <amount> bytes length)
            -1          - if the socket has closed
            AND the current packet-buffer """
    result = None
    # If we already have enough data buffered, don't re-fetch
    if ( len( packet_buffer ) >= amount ):
        bytes_read  = packet_buffer[0:amount]
        packet_buffer = packet_buffer[amount:]
        result = bytes_read
    else:
        # There's not enough data, so try to read some, but only for <timeout> seconds
        # if timeout is zero, just read whatever is sitting in the socket buffer already
        read_events_on   = [ read_socket ]
        (read_list, write_list, except_list) = select.select( read_events_on, [], [], timeout )

        if ( len( read_list ) > 0 ):
            # New data arrived, read it
            incoming = read_socket.recv( 8192 )
            if ( len( incoming ) == 0 ):
                # No data arrived, meaning the socket has closed
                result = -1
            else:
                #print("%d bytes Rx'd" % ( len( incoming ) ) )
                #print("%d bytes buffered" % ( len( packet_buffer ) ) )
                packet_buffer += incoming  # new bytes arrived, is there enough data now?
                if ( len( packet_buffer ) >= amount ):
                    bytes_read  = packet_buffer[0:amount]
                    packet_buffer = packet_buffer[amount:]
                    result = bytes_read
    return result, packet_buffer





pygame.init()
s = socket.socket()
port = 12345
s.bind(('', port))
s.listen(5)
c, addr = s.accept()
print ("Socket Up and running with a connection from",addr)
screen = pygame.display.set_mode( ( 1200, 960 ) )
pygame.display.set_caption("Maze Server")

blocks = [(50, 50, 200, 25), (200, 50, 25, 250), (300, 200, 200, 25), (50, 200, 25, 200), (250, 100, 25, 200),
          (300, 250, 25, 200), (350, 225, 25, 200), (75, 325, 200, 25), (25, 150, 150, 25), (100, 200, 75, 100),
          (100,375,175,25), (250, 425,25,75),(250,500,175,25),(300,450,100,25),
          (100,425,25,150),(150,425,75,100),(100,550,300,25),(50,425,25,175),(50,600,150,25),
          (0,100,175,25),(0,200,25,200),(0,425,25,200),(0,0,25,100),(0,0,250,25),(50,400,25,25),
          (0,650,100,25),(125,625,25,100),(0,750,10000,28),(1350,0,16,10000),(225,600,25,125),
          (175,650,25,100),(25,700,100,25),(275,0,10000,25),(275,50,200,25),(300,75,175,25),
          (275,125,225,50),(500,200,25,300),(400,250,25,225),(425,450,25,25),(450,250,25,275),
          (425,550,100,25),  (500,500,25,25),(500,0,25,500),(275,25,25,25),(550,50,25,475),
          (275,575,25,175),  (325,600,25,125),(350,600,25,25),(375,600,25,150),(425,575,25,150),
          (475,600,25,150), (525,550,50,175), (600,525,25,225),(650,575,175,25),(650,525,175,25),
          (850,475,25,250), (650,625,200,25), (650,600,25,25),(650,675,175,25),(650,700,25,50),
          (700,725,125,25), (900,500,25,300),(850,450,200,25),(950,500,100,25),(950,225,25,200),
          (800,300,125,25),(850,300,25,100),(900,325,25,100),(800,250,125,25),(800,200,200,25),
          (600,475,225,25), (600,300,25,200),(650,275,25,175),(700,300,25,225),(550,250,200,25),
          (750,250,25,200), (600,200,175,25),(550,150,300,25),(875,0,25,200),(800,350,25,100),
          (600,100,500,25),(575,50,275,25),(925,150,100,25),(1025,150,25,200),(1050,150,100,25),(1125,50,25,375),
          (1075,200,25,200),(1025,350,25,75),(1050,450,275,25),(1075,400,25,50),(1175,200,250,25),(1150,150,175,25),
          (1175,100,175,25), (1150,50,175,25),(925,50,200,25),(1150,250,175,25),(1300,275,25,150),(1250,300,25,150),(1175,300,50,125),
          (1075,500,250,25),(950,500,25,225),(1000,550,25,225),(1050,450,25,275),(1100,550,25,275),
          (1150,525,25,200),(1150,700,175,25),(1200,650,175,25),(1150,600,175,25),(1200,550,175,25)
          ]

x,y = 0, 0
sent_x, sent_y = -1, -1
walls = [pygame.Rect(r) for r in blocks]
Svel = 5
Hvel = 5
red = pygame.Rect(225, 225, 25, 25)
blue = pygame.Rect(1100, 300, 25, 25)
SeekChoice = random.randint(1,2)
if SeekChoice == 1:
    seeker = "red"
    Snum = 2
if SeekChoice == 2:
    seeker = "blue"
    Snum = 1
Num = 0
c.send(struct.pack("2i", SeekChoice, Num))
RedScore = 0
BlueScore = 0
RTime = 0
BTime = 0
running = True
font = pygame.font.Font(None, 60)
RedWin = font.render("RED IS WINNER!", 100,(255,0,0))
BlueWin = font.render("BLUE IS WINNER!", 100,(0,0,255))
while running:

    ### Receive a fixed-size packet from the client (if any)
    ### Client only sends us an x, y
    #buf = c.recv(8, socket.MSG_WAITALL)
    buf, network_buffer = socketReadIfAvailable( s, network_buffer, 8 ) # 8 bytes in a full packet
    if ( buf != None ):   # *IF* a packet was received
        if ( buf == -1 ):
            print( "Client disconnected" )
            network_buffer = bytearray()
            # TODO
        else:
            # A packet was received, unpack it
            x, y = struct.unpack( "2i",  buf )  # unpack the co-ordinates
            print( "Rx: (%d, %d)" % ( x, y ) )

    red = pygame.Rect(x,y,25,25)
    
    if RedScore >= 20:
        screen.fill([255,255,255])
        screen.blit(RedWin, [500,250])
        pygame.display.flip()
        time.sleep(10)
        pygame.quit()
        running = False
    if BlueScore >= 20:
        screen.fill([255,255,255])
        screen.blit(BlueWin, [500,250])
        pygame.display.flip()
        time.sleep(10)
        pygame.quit()
        running = False
    RTime += 1
    BTime += 1
    x1 = red.x
    y1 = red.y
    x2 = blue.x
    y2 = blue.y
    
    if seeker == "red":
        RTime = 0
    elif RTime >= 2000:
        RTime = 0
        RedScore += 1
    if seeker == "blue":
        BTime = 0
    elif BTime >= 2000:
        BTime = 0
        BlueScore += 1
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                pygame.quit()
                running = False
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        blue.x = max(blue.x - Svel, 0)
        for wall in walls: 
            if blue.colliderect(wall):
                blue.left = max(blue.left, wall.right)
    if keys[pygame.K_RIGHT]:
        blue.x = min(blue.x + Svel, 1341)
        for wall in walls: 
            if blue.colliderect(wall):
                blue.right = min(blue.right, wall.left)
    if keys[pygame.K_UP]:
        blue.y = max(blue.y - Svel, 0)
        for wall in walls: 
            if blue.colliderect(wall):
                blue.top = max(blue.top, wall.bottom)
    if keys[pygame.K_DOWN]:
        blue.y = min(blue.y + Svel, 743)
        for wall in walls: 
            if blue.colliderect(wall):
                blue.bottom = min(blue.bottom, wall.top)
    screen.fill([0,0,0])
    #pygame.draw.circle(screen,[255,255,255],(x1+12,y1+12),125)
    pygame.draw.circle(screen,[255,255,255],(x2+12,y2+12),125)
    for wall in walls: 
        pygame.draw.rect(screen, [0,0,0], wall)
    #pygame.draw.rect(screen, [255,0,0], red)
    pygame.draw.rect(screen, [0,0,255], blue)
#    if seeker == "red":
#        pygame.draw.rect(screen,[0,0,0],(red.x+7,red.y+7,10,10))
#    if seeker == "blue":
#        pygame.draw.rect(screen,[0,0,0],(blue.x+7,blue.y+7,10,10))
    pygame.draw.rect(screen,[0,0,0],(red.x+7,red.y+7,10,10))
    pygame.draw.rect(screen,[0,0,0],(blue.x+7,blue.y+7,10,10))

    pygame.display.flip()
    if red.colliderect(blue):
        if seeker == "red":
            print ("red/blue")
            seeker = "blue"
            Snum = 1
            BTime = 0
            RedScore += 2
            BlueScore -= 3
            red = pygame.Rect(225, 225, 25, 25)
            blue = pygame.Rect(1166, 334, 25, 25)
        elif seeker == "blue":
            print ("blue/red")
            Snum = 2
            RTime = 0
            seeker = "red"
            BlueScore += 2
            RedScore -= 3
            red = pygame.Rect(225, 225, 25, 25)
            blue = pygame.Rect(1166, 334, 25, 25)

    ### Pack and send the fixed-size data to the client
    ### But only if the position x,y has changed
    if ( sent_x != blue.x or sent_y != blue.y ):
        seeker_name = "%-12s" % ( seeker ) # pad to 12 spaces, this never changes, move outside loop
        seeker_name = seeker_name.encode() # this too
        c.send( bytes( seeker_name ) )   # send the colour-name padded to 12 letters
        c.send( struct.pack("2i", blue.x, blue.y ) )       # send the x, y
        sent_x = blue.x
        sent_y = blue.y
        print( "Tx: [%s], (%d, %d)" % ( seeker, sent_x, sent_y ) )

Client:

import pygame
import time
import random
import socket
import struct
pygame.init()


import select
network_buffer = bytearray()

def socketReadIfAvailable( read_socket:socket.socket, packet_buffer:bytearray, amount:int=1, timeout:float=0 ):
    """ Given an open socket, and a packet-buffer, return a packet if one is available.
        Returns a tuple of:
            None        - if no packet is available
            data-packet - (of <amount> bytes) if data is available (of <amount> bytes length)
            -1          - if the socket has closed
            AND the current packet-buffer """
    result = None
    # If we already have enough data buffered, don't re-fetch
    if ( len( packet_buffer ) >= amount ):
        bytes_read  = packet_buffer[0:amount]
        packet_buffer = packet_buffer[amount:]
        result = bytes_read
    else:
        # There's not enough data, so try to read some, but only for <timeout> seconds
        # if timeout is zero, just read whatever is sitting in the socket buffer already
        read_events_on   = [ read_socket ]
        (read_list, write_list, except_list) = select.select( read_events_on, [], [], timeout )

        if ( len( read_list ) > 0 ):
            # New data arrived, read it
            incoming = read_socket.recv( 8192 )
            if ( len( incoming ) == 0 ):
                # No data arrived, meaning the socket has closed
                result = -1
            else:
                #print("%d bytes Rx'd" % ( len( incoming ) ) )
                #print("%d bytes buffered" % ( len( packet_buffer ) ) )
                packet_buffer += incoming  # new bytes arrived, is there enough data now?
                if ( len( packet_buffer ) >= amount ):
                    bytes_read  = packet_buffer[0:amount]
                    packet_buffer = packet_buffer[amount:]
                    result = bytes_read
    return result, packet_buffer




s = socket.socket()
s.connect(('127.0.0.1',12345))

screen = pygame.display.set_mode( ( 1200, 960 ) )
pygame.display.set_caption("Maze Client")

blocks = [(50, 50, 200, 25), (200, 50, 25, 250), (300, 200, 200, 25), (50, 200, 25, 200), (250, 100, 25, 200),
          (300, 250, 25, 200), (350, 225, 25, 200), (75, 325, 200, 25), (25, 150, 150, 25), (100, 200, 75, 100),
          (100,375,175,25), (250, 425,25,75),(250,500,175,25),(300,450,100,25),
          (100,425,25,150),(150,425,75,100),(100,550,300,25),(50,425,25,175),(50,600,150,25),
          (0,100,175,25),(0,200,25,200),(0,425,25,200),(0,0,25,100),(0,0,250,25),(50,400,25,25),
          (0,650,100,25),(125,625,25,100),(0,750,10000,28),(1350,0,16,10000),(225,600,25,125),
          (175,650,25,100),(25,700,100,25),(275,0,10000,25),(275,50,200,25),(300,75,175,25),
          (275,125,225,50),(500,200,25,300),(400,250,25,225),(425,450,25,25),(450,250,25,275),
          (425,550,100,25),  (500,500,25,25),(500,0,25,500),(275,25,25,25),(550,50,25,475),
          (275,575,25,175),  (325,600,25,125),(350,600,25,25),(375,600,25,150),(425,575,25,150),
          (475,600,25,150), (525,550,50,175), (600,525,25,225),(650,575,175,25),(650,525,175,25),
          (850,475,25,250), (650,625,200,25), (650,600,25,25),(650,675,175,25),(650,700,25,50),
          (700,725,125,25), (900,500,25,300),(850,450,200,25),(950,500,100,25),(950,225,25,200),
          (800,300,125,25),(850,300,25,100),(900,325,25,100),(800,250,125,25),(800,200,200,25),
          (600,475,225,25), (600,300,25,200),(650,275,25,175),(700,300,25,225),(550,250,200,25),
          (750,250,25,200), (600,200,175,25),(550,150,300,25),(875,0,25,200),(800,350,25,100),
          (600,100,500,25),(575,50,275,25),(925,150,100,25),(1025,150,25,200),(1050,150,100,25),(1125,50,25,375),
          (1075,200,25,200),(1025,350,25,75),(1050,450,275,25),(1075,400,25,50),(1175,200,250,25),(1150,150,175,25),
          (1175,100,175,25), (1150,50,175,25),(925,50,200,25),(1150,250,175,25),(1300,275,25,150),(1250,300,25,150),(1175,300,50,125),
          (1075,500,250,25),(950,500,25,225),(1000,550,25,225),(1050,450,25,275),(1100,550,25,275),
          (1150,525,25,200),(1150,700,175,25),(1200,650,175,25),(1150,600,175,25),(1200,550,175,25)
          ]

sent_x, sent_y = -1, -1
walls = [pygame.Rect(r) for r in blocks]
Svel = 5
Hvel = 5
red = pygame.Rect(225, 225, 25, 25)
blue = pygame.Rect(1100, 300, 25, 25)
buf = s.recv(8, socket.MSG_WAITALL)
SeekChoice, Num = struct.unpack("2i", buf)
if SeekChoice == 1:
    seeker = "red"
    Snum = 1
if SeekChoice == 2:
    seeker = "blue"
    Snum = 2
RedScore = 0
BlueScore = 0
RTime = 0
BTime = 0
running = True
font = pygame.font.Font(None, 60)
RedWin = font.render("RED IS WINNER!", 100,(255,0,0))
BlueWin = font.render("BLUE IS WINNER!", 100,(0,0,255))

#screen.fill([255,255,255])
#pygame.draw.circle(screen,[255,255,255],(red.x+12,red.y+12),75)
#pygame.draw.circle(screen,[255,255,255],(blue.x+12,blue.y+12),75)
#for wall in walls: 
#    pygame.draw.rect(screen, [0,0,0], wall)
#pygame.draw.rect(screen, [255,0,0], red)
#pygame.draw.rect(screen, [0,0,255], blue)
#if seeker == "red":
#    pygame.draw.rect(screen,[0,0,0],(red.x+7,red.y+7,10,10))
#if seeker == "blue":
#    pygame.draw.rect(screen,[0,0,0],(blue.x+7,blue.y+7,10,10))
#pygame.display.flip()

while running:
#    if Snum == 1:
#        seeker = "red"
#    if Snum == 2:
#        seeker = "blue"

    ### Send the x,y position only if it's changed
    if ( sent_x != red.x or sent_y != red.y ):
        s.send(struct.pack("2i", red.x, red.y))
        sent_x = red.x
        sent_y = red.y
        print( "Tx: (%d, %d)" % ( sent_x, sent_y ) )

    if RedScore >= 20:
        screen.fill([255,255,255])
        screen.blit(RedWin, [500,250])
        pygame.display.flip()
        time.sleep(10)
        pygame.quit()
        running = False
    if BlueScore >= 20:
        screen.fill([255,255,255])
        screen.blit(BlueWin, [500,250])
        pygame.display.flip()
        time.sleep(10)
        pygame.quit()
        running = False
    RTime += 1
    BTime += 1

    if seeker == "red":
        RTime = 0
    elif RTime >= 2000:
        RTime = 0
        RedScore += 1
    if seeker == "blue":
        BTime = 0
    elif BTime >= 2000:
        BTime = 0
        BlueScore += 1

    # Handle Events
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                pygame.quit()
                running = False

    # Handle movement keys
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        red.x = max(red.x - Svel, 0)
        for wall in walls: 
            if red.colliderect(wall):
                red.left = max(red.left, wall.right)
    if keys[pygame.K_RIGHT]:
        red.x = min(red.x + Svel, 1341)
        for wall in walls: 
            if red.colliderect(wall):
                red.right = min(red.right, wall.left)
    if keys[pygame.K_UP]:
        red.y = max(red.y - Svel, 0)
        for wall in walls: 
            if red.colliderect(wall):
                red.top = max(red.top, wall.bottom)
    if keys[pygame.K_DOWN]:
        red.y = min(red.y + Svel, 743)
        for wall in walls: 
            if red.colliderect(wall):
                red.bottom = min(red.bottom, wall.top)

    # Paint the screen
    screen.fill([0,0,0])
    pygame.draw.circle(screen,[255,255,255],(red.x+12,red.y+12),125)
    #pygame.draw.circle(screen,[255,255,255],(blue.x+12,blue.y+12),125)
    for wall in walls: 
        pygame.draw.rect(screen, [0,0,0], wall)
    pygame.draw.rect(screen, [255,0,0], red)
    #pygame.draw.rect(screen, [0,0,255], blue)
#    if seeker == "red":
#        pygame.draw.rect(screen,[0,0,0],(red.x+7,red.y+7,10,10))
#    if seeker == "blue":
#        pygame.draw.rect(screen,[0,0,0],(blue.x+7,blue.y+7,10,10))
    pygame.draw.rect(screen,[0,0,0],(red.x+7,red.y+7,10,10))
    pygame.draw.rect(screen,[0,0,0],(blue.x+7,blue.y+7,10,10))
    pygame.display.flip()

    #seeker = s.recv(1024)
    #seeker = seeker.decode()
    #buf = s.recv(8, socket.MSG_WAITALL)
    #x, y = struct.unpack("2i", buf)

    ### Receive a fixed-size packet from the server (if any)
    ### Server sends a 12-letter name, and x,y
    buf, network_buffer = socketReadIfAvailable( s, network_buffer, 20 ) # 20 bytes in a full packet
    if ( buf != None ):   # *IF* a packet was received
        if ( buf == -1 ):
            print( "Client disconnected" )
            network_buffer = bytearray()
            # TODO
        else:
            # A packet was received, unpack it
            seeker = buf[0:12].decode( "utf-8" ) # unpack the 12-letter (padded) name
            seeker = seeker.strip()
            x, y = struct.unpack( "2i",  buf[12:20] )  # unpack the co-ordinates
            blue = pygame.Rect(x,y,25,25)
            print( "Rx: [%s] at (%d, %d)" % ( seeker, x, y ) )


    red  = pygame.Rect(red.x,red.y,25,25)

Obviously you could move the socketReadIfAvailable() into a common file, probably also blocks too.

Upvotes: 1

Related Questions