-1

I'm developing a two-player multiplayer game with pygame.
Here is the server code and client code:

<Server Code>

import json
import pygame  # Main module for the game
import pyautogui  # For slightly enhancing control sensitivity
import sys  # For console control
import socket  # Socket for network communication
import threading  # For multithreading
import math

pygame.init()  # Initialize Pygame
pygame.display.set_caption('SERVER')  # Set window title
height = 960  # Window height
width = 1200  # Window width
screen = pygame.display.set_mode((width, height))

class Bullet:
    def __init__(self, img, x, y, dx, dy, w, h, damage, team):
        self.__img = img
        self.__x = x
        self.__y = y
        self.__dx = dx
        self.__dy = dy
        self.__w = w
        self.__h = h
        self.__damage = damage
        self.__team = team

    def getdamage(self):
        return self.__damage

    def getteam(self):
        return self.__team

    def getrect(self):
        nrect = pygame.Rect(0, 0, self.__w, self.__h)
        nrect.center = (self.__x, self.__y)
        return nrect

    def getpos(self):
        return (self.__x, self.__y)

    def move(self):
        self.__x += self.__dx
        self.__y += self.__dy

    def show(self):
        newRect = self.getrect()
        screen.blit(self.__img, newRect)

enemy_img = pygame.image.load('char.png')
enemy_img = pygame.transform.flip(enemy_img, True, False)
bulletimg = pygame.image.load('a_bullet.png')
bulletw = bulletimg.get_size()[0]
bulleth = bulletimg.get_size()[1]
imgw = enemy_img.get_size()[0]
imgh = enemy_img.get_size()[1]
bulletlist = []
enex = 0
eney = imgh
playersize = imgw / 2
playerspeed = 3
enexdirec = 0
eneydirec = 0
playerxdirec = 0
playerydirec = 0
playerHp = 100
eneHp = 100

def getPlayerrect(pos):
    nrect = pygame.Rect(0, 0, imgw, imgh)
    nrect.center = (pos[0], pos[1])
    return nrect

def consoles():
    global enexdirec, eneydirec
    global eneHp, playerHp
    while True:
        msg = client.recv(1024)
        if not msg:
            break  # Connection closed
        decoded_msg = msg.decode()  # Decode message to string
        if decoded_msg == 'up':
            eneydirec = -1
        if decoded_msg == 'down':
            eneydirec = 1
        if decoded_msg == 'left':
            enexdirec = -1
        if decoded_msg == 'right':
            enexdirec = 1
        if decoded_msg == 'endx':
            enexdirec = 0
        if decoded_msg == 'endy':
            eneydirec = 0
        if decoded_msg == 'damaged':
            playerHp -= 10
            if playerHp <= 0:
                print("LOSE")
                quitgame()
        if decoded_msg.startswith('{'):  # If message is in JSON format, process as bullet data
            data = json.loads(decoded_msg)  #ERROR!jsondecodeerror
            if data.get("type") == "bullet":
                # Create and add Bullet object
                bulletlist.append(Bullet(
                    bulletimg,
                    data['x'],
                    data['y'],
                    data['dx'],
                    data['dy'],
                    data['width'],
                    data['height'],
                    data['damage'],
                    'E'
                ))

def acceptC():
    global client, server, addr
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('127.0.0.1', 7777))
    server.listen()
    client, addr = server.accept()

    thr = threading.Thread(target=consoles, args=())
    thr.Daemon = True
    thr.start()

def quitgame():
    pygame.quit()
    sys.exit()

def clamp(x, mi, ma):
    if x > ma:
        return ma
    elif x < mi:
        return mi
    return x

def calcvector(sp, fp):
    direcx = fp[0] - sp[0]
    direcy = fp[1] - sp[1]
    l = math.sqrt(direcx**2 + direcy**2)
    if l == 0:
        return (1, 0)
    else:
        return (direcx / l, direcy / l)

def GameMain():
    global playerHp, eneHp
    global enex, eney
    global playerxdirec, playerydirec
    fps = pygame.time.Clock()

    img = pygame.image.load('char.png')  # Load player image
    imgh = img.get_size()[1]
    imgw = img.get_size()[0]
    x = 0
    y = imgh

    while True:
        screen.fill((255, 255, 255))

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:#send bullet data to client
                    T = pygame.mouse.get_pos()
                    direcvec = calcvector((x, y), T)
                    speed = 8
                    newB = Bullet(bulletimg, x, y, direcvec[0] * speed, direcvec[1] * speed, 10, 10, 10, 'M')
                    bulletlist.append(newB)
                    bullet_data = json.dumps({
                        'type': 'bullet',
                        'x': x,
                        'y': y,
                        'dx': direcvec[0] * speed,
                        'dy': direcvec[1] * speed,
                        'width': 10,
                        'height': 10,
                        'damage': 10
                    })
                    client.sendall(bullet_data.encode())
        keys = pygame.key.get_pressed()
        if not(keys[pygame.K_a] ^ keys[pygame.K_d]):
            playerxdirec = 0
            msg = "endx"
            client.sendall(msg.encode())
        elif keys[pygame.K_a]:
            playerxdirec = -1
            msg = "left"
            client.sendall(msg.encode())
        elif keys[pygame.K_d]:
            playerxdirec = 1
            msg = "right"
            client.sendall(msg.encode())
        if not(keys[pygame.K_w] ^ keys[pygame.K_s]):
            playerydirec = 0
            msg = "endy"
            client.sendall(msg.encode())
        elif keys[pygame.K_w]:
            playerydirec = -1
            msg = "up"
            client.sendall(msg.encode())
        elif keys[pygame.K_s]:
            playerydirec = 1
            msg = "down"
            client.sendall(msg.encode())

        for i in bulletlist:
            i.move()
            bulpos = i.getpos()
            if bulpos[0] > width or bulpos[0] < 0 or bulpos[1] < 0 or bulpos[1] > height:
                bulletlist.remove(i)
                continue
            else:
                i.show()
            if i.getrect().colliderect(getPlayerrect((x, y))) and i.getteam() == 'E':
                bulletlist.remove(i)
                playerHp -= 10
                msg = json.dumps({
                    'type': 'hp_update',
                    'hp': eneHp,
                    'eneHp': playerHp
                })
                print('er')
                client.sendall(msg.encode())
                continue
            if i.getrect().colliderect(getPlayerrect((enex, eney))) and i.getteam() == 'M':
                bulletlist.remove(i)
                eneHp -= 10
                msg = json.dumps({
                    'type': 'hp_update',
                    'hp': eneHp,
                    'eneHp': playerHp
                })
                print('su')
                client.sendall(msg.encode())
                continue

        if eneHp <= 0:
            print('WIN')
            quitgame()
        if playerHp <= 0:
            print('WIN')
            quitgame()

        # Draw enemy HP bar
        enehpposrect = pygame.Rect(0, 0, 30, 8)
        enehpposrect.center = (enex, eney + 20)
        enehpposrect2 = pygame.Rect(enehpposrect.x, enehpposrect.y, 30 * (eneHp / 100), 8)
        pygame.draw.rect(screen, "red", enehpposrect)
        pygame.draw.rect(screen, "green", enehpposrect2)

        # Draw player HP bar
        playerhpposrect = pygame.Rect(0, 0, 30, 8)
        playerhpposrect.center = (x, y + 20)
        playerhpposrect2 = pygame.Rect(playerhpposrect.x, playerhpposrect.y, 30 * (playerHp / 100), 8)
        pygame.draw.rect(screen, "red", playerhpposrect)
        pygame.draw.rect(screen, "green", playerhpposrect2)

        x += playerxdirec * playerspeed
        y += playerydirec * playerspeed
        enex += enexdirec * playerspeed
        eney += eneydirec * playerspeed
        x = clamp(x, 0, width)
        y = clamp(y, 0, height)
        enex = clamp(enex, 0, width)
        eney = clamp(eney, 0, height)

        mRect = pygame.Rect(0, 0, imgw, imgh)
        mRect.center = (x, y)
        screen.blit(img, mRect)
        eRect = pygame.Rect(0, 0, imgw, imgh)
        eRect.center = (enex, eney)
        screen.blit(enemy_img, eRect)
        pygame.display.update()  # Update display
        fps.tick(60)  # Set FPS to 60

if __name__ == '__main__':
    acceptC() 
    GameMain()

<Client code>

import pygame
import pyautogui
import sys
import socket
import threading
import json
import math

pygame.init()
pygame.display.set_caption('CLIENT')
height = 960
width = 1200
screen = pygame.display.set_mode((width, height))

class Bullet:
    def __init__(self, img, x, y, dx, dy, w, h, damage, team):
        self.__img = img
        self.__x = x
        self.__y = y
        self.__dx = dx
        self.__dy = dy
        self.__w = w
        self.__h = h
        self.__damage = damage
        self.__team = team
    def getdamage(self):
        return self.__damage
    def getteam(self):
        return self.__team
    def getrect(self):
        nrect = pygame.Rect(0, 0, self.__w, self.__h)
        nrect.center = (self.__x, self.__y)
        return nrect
    def getpos(self):
        return (self.__x, self.__y)
    def move(self):
        self.__x += self.__dx
        self.__y += self.__dy
    def show(self):
        newRect = self.getrect()
        screen.blit(self.__img, newRect)

enemy_img = pygame.image.load('char.png')
enemy_img = pygame.transform.flip(enemy_img, True, False)
bulletimg = pygame.image.load('a_bullet.png')
bulletw = bulletimg.get_size()[0]
bulleth = bulletimg.get_size()[1]
imgw = enemy_img.get_size()[0]
imgh = enemy_img.get_size()[1]
bulletlist = []
enex = 0
eney = imgh
playersize = imgw / 2
playerspeed = 3
enexdirec = 0
eneydirec = 0
playerxdirec = 0
playerydirec = 0
playerHp = 100
eneHp = 100

def getPlayerrect(pos):
    nrect = pygame.Rect(0, 0, imgw, imgh)
    nrect.center = (pos[0], pos[1])
    return nrect

def consoles():
    global eneHp, playerHp
    global enexdirec, eneydirec
    while True:
        msg = client.recv(1024)
        if not msg:
            break  # If the connection is lost
        decoded_msg = msg.decode()  # Convert message to string
        if decoded_msg == 'up':
            eneydirec = -1
        if decoded_msg == 'down':
            eneydirec = 1
        if decoded_msg == 'left':
            enexdirec = -1
        if decoded_msg == 'right':
            enexdirec = 1
        if decoded_msg == 'endx':
            enexdirec = 0
        if decoded_msg == 'endy':
            eneydirec = 0
        if decoded_msg == 'damaged':
            playerHp -= 10
            if playerHp <= 0:
                print("LOSE")
                quitgame()
        if decoded_msg.startswith('{'):  # If JSON format, treat it as bullet data
            data = json.loads(decoded_msg)   #ERROR!jsondecodeerror
            if data.get("type") == 0:
                print('a')
                playerHp = data["hp"]
                eneHp = data["eneHp"]
            if data.get("type") == 1:
                # Create and add Bullet object
                bulletlist.append(Bullet(
                    bulletimg,
                    data['x'],
                    data['y'],
                    data['dx'],
                    data['dy'],
                    data['width'],
                    data['height'],
                    data['damage'],
                    'E'
                ))

def acceptC():
    global client
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(('127.0.0.1', 7777))

    thr = threading.Thread(target=consoles, args=())
    thr.Daemon = True
    thr.start()

def quitgame():
    pygame.quit()
    sys.exit()

def clamp(x, mi, ma):
    if x > ma:
        return ma
    elif x < mi:
        return mi
    return x

def calcvector(sp, fp):
    direcx = fp[0] - sp[0]
    direcy = fp[1] - sp[1]
    l = math.sqrt(direcx ** 2 + direcy ** 2)
    if l == 0:
        return (1, 0)
    else:
        return (direcx / l, direcy / l)

def GameMain():
    global playerHp, eneHp
    global playerxdirec, playerydirec
    global enex, eney
    fps = pygame.time.Clock()

    img = pygame.image.load('char.png')
    imgh = img.get_size()[1]
    imgw = img.get_size()[0]
    x = 0
    y = imgh

    while True:
        screen.fill((255, 255, 255))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:#send bullet data to server
                    T = pygame.mouse.get_pos()
                    direcvec = calcvector((x, y), T)
                    speed = 8
                    newB = Bullet(bulletimg, x, y, direcvec[0] * speed, direcvec[1] * speed, 10, 10, 10, 'M')
                    bulletlist.append(newB)
                    bullet_data = json.dumps({
                        'type': 1,
                        'x': x,
                        'y': y,
                        'dx': direcvec[0] * speed,
                        'dy': direcvec[1] * speed,
                        'width': 10,
                        'height': 10,
                        'damage': 10
                    })
                    client.sendall(bullet_data.encode())

        keys = pygame.key.get_pressed()
        if not(keys[pygame.K_a] ^ keys[pygame.K_d]):
            playerxdirec = 0
            msg = "endx"
            client.sendall(msg.encode())
        elif keys[pygame.K_a]:
            playerxdirec = -1
            msg = "left"
            client.sendall(msg.encode())
        elif keys[pygame.K_d]:
            playerxdirec = 1
            msg = "right"
            client.sendall(msg.encode())
        if not(keys[pygame.K_w] ^ keys[pygame.K_s]):
            playerydirec = 0
            msg = "endy"
            client.sendall(msg.encode())
        elif keys[pygame.K_w]:
            playerydirec = -1
            msg = "up"
            client.sendall(msg.encode())
        elif keys[pygame.K_s]:
            playerydirec = 1
            msg = "down"
            client.sendall(msg.encode())

        for i in bulletlist:
            i.move()
            bulpos = i.getpos()
            if bulpos[0] > width or bulpos[0] < 0 or bulpos[1] < 0 or bulpos[1] > height:
                bulletlist.remove(i)
                continue
            else:
                i.show()
            if i.getrect().colliderect(getPlayerrect((x, y))) and i.getteam() == 'E':
                bulletlist.remove(i)
                continue
            if i.getrect().colliderect(getPlayerrect((enex, eney))) and i.getteam() == 'M':
                bulletlist.remove(i)
                continue
        if playerHp <= 0:
            print('LOSE')
            quitgame()
        if eneHp <= 0:
            print('WIN')
            quitgame()
        enehpposrect = pygame.Rect(0, 0, 30, 8)
        enehpposrect.center = (enex, eney + 20)
        enehpposrect2 = pygame.Rect(enehpposrect.x, enehpposrect.y, 30 * (eneHp / 100), 8)
        pygame.draw.rect(screen, "red", enehpposrect)
        pygame.draw.rect(screen, "green", enehpposrect2)
        playerhpposrect = pygame.Rect(0, 0, 30, 8)
        playerhpposrect.center = (x, y + 20)
        playerhpposrect2 = pygame.Rect(playerhpposrect.x, playerhpposrect.y, 30 * (playerHp / 100), 8)
        pygame.draw.rect(screen, "red", playerhpposrect)
        pygame.draw.rect(screen, "green", playerhpposrect2)

        x += playerxdirec * playerspeed
        y += playerydirec * playerspeed
        enex += enexdirec * playerspeed
        eney += eneydirec * playerspeed
        x = clamp(x, 0, width)
        y = clamp(y, 0, height)
        enex = clamp(enex, 0, width)
        eney = clamp(eney, 0, height)
        mRect = pygame.Rect(0, 0, imgw, imgh)
        mRect.center = (x, y)
        screen.blit(img, mRect)
        eRect = pygame.Rect(0, 0, imgw, imgh)
        eRect.center = (enex, eney)
        screen.blit(enemy_img, eRect)

        pygame.display.update()
        fps.tick(60)

if __name__ == '__main__':
    acceptC()
    GameMain()

When I run the two codes on my computer and play the game, it works fine.

However, if I change the IP address and run it on two computers, the player moves fine, but as soon as I click the mouse to shoot a bullet, the following error occurs:

json.decoder.JSONDecodeError: Extra data: line 1 column 124 (char 123)

How can I solve this problem?

I tried using pickle instead of using decode/encode of JSON, but I get the following error:

_pickle.UnpicklingError: unpickling stack underflow

The same error occurred even if I increased the client.recv value.

2
  • 3
    There is absolutely no reason to expect that a call to .recv(1024) will return exactly one message, no more and no less. You need some sort of protocol to indicate where the message boundaries were in the stream - perhaps a terminator character, that cannot otherwise be present in your messages (newline looks like it would work for you), or perhaps a length indication (itself of fixed length) that precedes the actual message. Either way, you call .recv() in a loop until the entire message is retrieved, before making any attempt to interpret it. Commented Nov 8, 2024 at 0:30
  • Thank you very much! I solved the problem! Commented Nov 8, 2024 at 11:11

1 Answer 1

0

The problem is solved by changing the consoles() function as follows and adding a newline character to all data when passing data.

def consoles():
    global player, enemy
    while True:
        msg = client.recv(1024)
        if not msg:
            break
        decoded_msgs = msg.decode().split("\n")
        for decoded_msg in decoded_msgs:
            if decoded_msg == "up":
                enemy.direction_y = -1
            elif decoded_msg == "down":
                enemy.direction_y = 1
            elif decoded_msg == "left":
                enemy.direction_x = -1
            elif decoded_msg == "right":
                enemy.direction_x = 1
            elif decoded_msg == "endx":
                enemy.direction_x = 0
            elif decoded_msg == "endy":
                enemy.direction_y = 0
            elif decoded_msg == "damaged":
                player.take_damage(10)
                if player.hp <= 0:
                    print("LOSE")
                    quitgame()
            elif decoded_msg.startswith("{"):
                data = json.loads(decoded_msg)
                if data.get("type") == 1:
                    bulletlist.append(Bullet(
                        bulletimg,
                        data["x"],
                        data["y"],
                        data["dx"],
                        data["dy"],
                        data["width"],
                        data["height"],
                        data["damage"],
                        "E"
                    ))
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.