波涛汹涌的在线python游戏

波涛汹涌的在线python游戏,python,sockets,server,pygame,client,Python,Sockets,Server,Pygame,Client,我使用pygame和sockets在python中编程了一个简单的克隆,我有三个问题: 当我独自在笔记本电脑上玩游戏时,游戏会变得波涛汹涌。每十秒钟我的游戏就会被卡住一段时间(一毫秒),然后继续。这不是什么大问题,但很烦人 当我在本地网络中的两台计算机上玩游戏时,我看到另一个玩家(另一条蛇)也在起伏不定 最奇怪的问题是,当我在主笔记本电脑上运行服务器,然后在第二台笔记本电脑上运行游戏时,游戏开始,几秒钟后崩溃。客户端上的调试器表示,从服务器接收数据时,pickle数据被截断。但当我在第二台笔记本

我使用pygame和sockets在python中编程了一个简单的克隆,我有三个问题:

  • 当我独自在笔记本电脑上玩游戏时,游戏会变得波涛汹涌。每十秒钟我的游戏就会被卡住一段时间(一毫秒),然后继续。这不是什么大问题,但很烦人
  • 当我在本地网络中的两台计算机上玩游戏时,我看到另一个玩家(另一条蛇)也在起伏不定
  • 最奇怪的问题是,当我在主笔记本电脑上运行服务器,然后在第二台笔记本电脑上运行游戏时,游戏开始,几秒钟后崩溃。客户端上的调试器表示,从服务器接收数据时,pickle数据被截断。但当我在第二台笔记本电脑上运行服务器程序,在主笔记本电脑上运行游戏时,一切都正常。为什么?

  • 我试过:
    问题1。更改客户端上的FPS和服务器上的time.sleep
    问题2。在服务器上更改time.sleep
    问题3。更改recv()方法的输入值


    服务器代码:

    import socket
    import threading
    import pickle
    import random
    import time
    import math
    
    ip = socket.gethostbyname(socket.gethostname())
    port = 5555
    
    address = (ip, port)
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    snakes = []
    colors = [(80, 0, 0), (0, 80, 0), (0, 0, 140), (80, 80, 0), (80, 0, 80), (0, 80, 80)]
    food = []
    
    for i in range(0, 300, 1):
        food.append([random.randint(10, 3290), random.randint(10, 2090), (0, 0, 100), 10, 1])
    
    number_of_players = 0
    
    def start(server, address):
        server.bind(address)
    
    def new_clients(server):
        global number_of_players
        server.listen()
        while True:
            conn, addr = server.accept()
            thread = threading.Thread(target=client, args=(conn, addr, number_of_players))
            thread.start()
            number_of_players += 1
            print(f"Active connections: {threading.activeCount() - 1}")
    
    def client(conn, addr, player):
        print(f"New connection: {addr}\n")
        global snakes
        global colors
        global food
        try:
            snakes.append([])
            name = pickle.loads(conn.recv(1024))
            while True:
                verity = True
                x = random.randint(100, 3200)
                y = random.randint(100, 2000)
                for i in range(0, len(snakes), 1):
                    if snakes[i] != []:
                        if math.sqrt((snakes[i][0][0] - x)**2 + (snakes[i][0][1] - y)**2) > snakes[i][0][4]*2:
                            for a in range(0, len(snakes[i][1])):
                                if math.sqrt((snakes[i][1][a][0] - x)**2 + (snakes[i][1][a][1] - y)**2) < snakes[i][0][4]*2:
                                    verity = False
                                if verity == False:
                                    break
                        else:
                            verity = False
                        if verity == False:
                            break
                if verity == True:
                    break
    
            snakes[player] = [[x, y, random.choice(colors), 4, 30, name, 0, 3], []]
            conn.send(pickle.dumps(snakes[player]))
        except (ConnectionAbortedError, ConnectionResetError, EOFError):
            print("Connection lost")
            print(f"Active connections: {threading.activeCount() - 2}")
            conn.close()
            return
        while True:
            try:
                snakes_to_send = []
                for i in range(0, len(snakes), 1):
                    if (i != player) and (len(snakes[i]) != 0):
                        snakes_to_send.append(snakes[i])
                conn.send(pickle.dumps([snakes_to_send, food]))
    
                received = pickle.loads(conn.recv(8192*4))
                snakes[player] = received[0]
    
                eaten = received[1]
                for i in range(0, len(eaten), 1):
                    try:
                        food.remove(eaten[i])
                        if eaten[i][4] != 5:
                            food.append([random.randint(10, 3290), random.randint(10, 2090), (0, 0, 100), 10, 1])
                    except ValueError:
                        continue
    
                dead_snake = received[2]
                if dead_snake != []:
                    for i in range(0, len(dead_snake), 1):
                        food.append(dead_snake[i])
    
                time.sleep(1/1000)
            except (ConnectionAbortedError, ConnectionResetError, EOFError):
                snakes[player] = []
                print("Connection lost")
                print(f"Active connections: {threading.activeCount() - 2}")
                conn.close()
                break
    
    print(f"Server is starting on IP: {ip} and PORT: {port}")
    start(server, address)
    new_clients(server)
    

    这就是我在这里使用的库。

    您的第三个问题(被截断的pickle数据)是因为您使用的是TCP,并且您正在取消pickle recv返回的任何内容。您可能会认为,每当您
    发送
    某个内容,并且接收方调用
    recv
    ,都会返回完全相同的内容,但实际上它不会。TCP将数据拆分为数据包,因此接收方可能不会同时接收所有数据

    例如,如果发送“abcdefgh”,然后单独发送“ijkl”,则允许第一次接收返回“abcd”,第二次接收返回“efghijkl”。或者第一个可以返回“ab”,第二个可以返回“cde”,第三个可以返回“fghijkl”,依此类推

    你必须设计一种方法让接收者知道何时停止接收。例如,如果您先发送“8abcdefgh”,然后发送“4ijkl”,那么接收方可能会得到“8abcdefgh4ij”,然后它知道“8abcdefgh”是一个“发送”(因为它以8开头,然后又多了8个字节),并且它知道“4ij”是下一个“发送”的开始,但这不是全部(因为它以4开头,但没有4个字节)


    另一种方法是在每条消息后发送一个特殊字符,如换行符(回车键)。这可能不适用于pickle,因为pickle中可能有换行符。但是您可以选择pickles没有的另一个字节,比如0xFF。然后接收器知道继续接收直到看到字节0xFF。

    你怎么能注意到一毫秒?这只是一个比喻
    #import
    import pygame
    import sys
    import math
    import random
    import pygame_textinput
    from client import Client
    
    #pygame initialize
    pygame.init()
    win = pygame.display.set_mode((1100, 700))
    pygame.display.set_caption("Slither.io")
    
    #connect to the server
    ip = "10.0.0.3"
    port = 5555
    c = Client(ip, port)
    c.server()
    
    #fonts
    font = pygame.font.SysFont("comicsansms", 20)
    big_font = pygame.font.SysFont("comicsansms", 35)
    
    #time
    clock = pygame.time.Clock()
    
    class Snake:
        def __init__(self, x, y, color, length, diameter, name, score, step):
            self.x = x
            self.y = y
            self.color = color
            self.length = length
            self.diameter = diameter
            self.name = name
            self.score = score
            self.step = step
            self.pos_list = []
    
        #move the snake
        def move(self):
            self.mousex, self.mousey = pygame.mouse.get_pos()
            self.difx = self.mousex - 550
            self.dify = self.mousey - 350
            try:
                self.xstep = math.sqrt((self.step**2)/((self.dify/self.difx)**2 + 1))
                self.ystep = (self.dify/self.difx)*self.xstep
            except ZeroDivisionError:
                self.xstep = 0
                self.ystep = self.step
            if math.sqrt(self.difx**2 + self.dify**2) > self.diameter/3:
                if(self.difx > 0):
                    self.x += self.xstep
                    self.y += self.ystep
                elif(self.difx < 0):
                    self.x -= self.xstep
                    self.y -= self.ystep
                else:
                    if self.dify > 0:
                        self.y += self.ystep
                    else:
                        self.y -= self.ystep
            self.pos_list.append([self.x, self.y])
            if len(self.pos_list) == 100:
                del self.pos_list[0]
    
        #check if the snake isn't out of the area or not crashed
        def collision(self):
            global run
            if (self.x + self.diameter/3 >= 3299) or (self.x - self.diameter/3 <= 1) or (self.y + self.diameter/3 >= 2099) or (self.y - self.diameter/3 <= 1):
                run = False
            for i in range(0, len(other_snakes), 1):
                if math.sqrt((other_snakes[i].x - snake.x)**2 + (other_snakes[i].y - snake.y)**2) < other_snakes[i].diameter*(2/3):
                    run = False
            for i in range(0, len(other_segments), 1):
                if math.sqrt((other_segments[i].x - snake.x)**2 + (other_segments[i].y - snake.y)**2) < other_segments[i].diameter*(2/3):
                    run = False
    
        #draw all snakes
        def draw(self):
            pygame.draw.rect(win, self.color, [self.x - snake.x + 550 - self.diameter/2, self.y - snake.y + 350 - self.diameter/2, self.diameter, self.diameter], border_radius=int(self.diameter/2))
    
    class Segment(Snake):
        def __init__(self, x, y, diameter, step, circle):
            self.x = x
            self.y = y
            self.color = (80, 80, 80)
            self.diameter = diameter
            self.step = step
            self.circle = circle
            self.pos_list = []
    
        def move(self):
            self.diameter = snake.diameter
            try:
                self.x = self.circle.pos_list[-int(self.diameter/self.step)][0]
                self.y = self.circle.pos_list[-int(self.diameter/self.step)][1]
            except IndexError:
                self.x = self.circle.pos_list[0][0]
                self.y = self.circle.pos_list[0][1]
            self.pos_list.append([self.x, self.y])
            if len(self.pos_list) == 100:
                del self.pos_list[0]
    
    class Food(Snake):
        def __init__(self, x, y, color, diameter, points):
            self.x = x
            self.y = y
            self.color = color
            self.diameter = diameter
            self.points = points
    
    other_snakes = []
    segments = []
    other_segments = []
    food = []
    eaten = []
    dead_snake = []
    
    #get other snakes
    def return_snakes():
        global other_snakes
        global other_segments
        global food
        other_snakes = []
        other_segments = []
        food = []
        message = c.receive_message()
        received = message[0]
        for i in range(0, len(received), 1):
            s = Snake(received[i][0][0], received[i][0][1], received[i][0][2], received[i][0][3], received[i][0][4], received[i][0][5], received[i][0][6], snake.step)
            other_snakes.append(s)
            for a in range(0, len(received[i][1]), 1):
                sg = Segment(received[i][1][a][0], received[i][1][a][1], s.diameter, snake.step, 0)
                other_segments.append(sg)
        rec_fd = message[1]
        for i in range(0, len(rec_fd), 1):
            f = Food(rec_fd[i][0], rec_fd[i][1], rec_fd[i][2], rec_fd[i][3], rec_fd[i][4])
            food.append(f)
    
    #send the snake to server
    def send_snake():
        global snake
        global segments
        global food
        to_send = [[[snake.x, snake.y, snake.color, snake.length, snake.diameter, snake.name, snake.score], []]]
        for i in range(0, len(segments), 1):
            to_send[0][1].append([segments[i].x, segments[i].y])
        to_send.append(eaten)
        to_send.append(dead_snake)
        c.send_message(to_send)
    
    #function for ordering snakes by score
    def order(o):
        return o["score"]
    
    #draw screen
    def screen():
        for i in range(0, 3301, 100):
            pygame.draw.line(win, (60, 60, 60), [i - snake.x + 550, 0 - snake.y + 350], [i - snake.x + 550, 2100 - snake.y + 350], 1)
        for i in range(0, 2101, 100):
            pygame.draw.line(win, (60, 60, 60), [0 - snake.x + 550, i - snake.y + 350], [3300 - snake.x + 550, i - snake.y + 350], 1)
    
    #enter your nickname
    textinput = pygame_textinput.TextInput(font_family="comicsansms", max_string_length=12)
    while True:
        win.fill((100, 100, 100))
        events = pygame.event.get()
        for event in events:
            keys = pygame.key.get_pressed()
            if (event.type == pygame.QUIT) or keys[pygame.K_ESCAPE] or keys[pygame.K_F4] and pygame.key.get_mods() & pygame.KMOD_ALT:
                sys.exit()
        if keys[pygame.K_RETURN]:
            break
        name = big_font.render("Nickname:", True, (0, 0, 0))
        center = name.get_rect(center=(400, 350))
        win.blit(name, center)
        textinput.update(events)
        win.blit(textinput.get_surface(), (500, 325))
        pygame.display.update()
    
    c.send_message(textinput.get_text())
    received = c.receive_message()
    snake = Snake(received[0][0], received[0][1], received[0][2], received[0][3], received[0][4], received[0][5], received[0][6], received[0][7])
    
    segment = Segment(snake.x, snake.y, snake.diameter, snake.step, snake)
    segments.append(segment)
    for i in range(0, snake.length - 2, 1):
        segment = Segment(snake.x, snake.y, snake.diameter, snake.step, segment)
        segments.append(segment)
    
    while True:
        run = True
        while run:
            eaten = []
            #quit game
            for event in pygame.event.get():
                keys = pygame.key.get_pressed()
                if (event.type == pygame.QUIT) or keys[pygame.K_ESCAPE] or keys[pygame.K_F4] and pygame.key.get_mods() & pygame.KMOD_ALT:
                    sys.exit()
    
            return_snakes()
    
            snakes = [{"name": snake.name, "score": snake.score}]
            for i in range(0, len(other_snakes), 1):
                snakes.append({"name": other_snakes[i].name, "score": other_snakes[i].score})
    
            win.fill((100, 100, 100))
            screen()
    
            #draw all food
            for i in range(0, len(food), 1):
                food[i].draw()
    
            #draw the other snakes and describe
            for i in range(0, len(other_segments), 1):
                other_segments[i].draw()
    
            for i in range(0, len(other_snakes), 1):
                other_snakes[i].draw()
    
                name = font.render(other_snakes[i].name, True, (0, 0, 0))
                center = name.get_rect(center=(other_snakes[i].x - snake.x + 550, other_snakes[i].y - snake.y + 350))
                win.blit(name, center)
    
            #move, draw snake and check if the snake is alive
            snake.move()
            for i in range(0, len(segments), 1):
                segments[i].move()
            for i in range(0, len(segments), 1):
                segments[i].draw()
            snake.draw()
            snake.collision()
    
            #check if the snake ate food and add score
            for i in range(len(food) - 1, -1, -1):
                if math.sqrt((food[i].x - snake.x)**2 + (food[i].y - snake.y)**2) < (snake.diameter/2 + food[i].diameter/2):
                    snake.score += food[i].points
                    snake.length = int(snake.score/10) + 4
                    if snake.length - 1 > len(segments):
                        segment = Segment(snake.x, snake.y, snake.diameter, snake.step, segment)
                        segments.append(segment)
                    snake.diameter = int(snake.score/50)*0.5 + 30
                    eaten.append([food[i].x, food[i].y, food[i].color, food[i].diameter, food[i].points])
    
            #describe the snake
            name = font.render(snake.name, True, (0, 0, 0))
            center = name.get_rect(center=(550, 350))
            win.blit(name, center)
    
            send_snake()
    
            #sort the snakes by score
            snakes.sort(key=order, reverse=True)
    
            #create the table
            for i in range(0, len(snakes), 1):
                table = font.render(str(snakes[i]["name"]), True, (0, 0, 0))
                win.blit(table, (900, 50 + i*30))
            for i in range(0, len(snakes), 1):
                table = font.render(str(snakes[i]["score"]), True, (0, 0, 0))
                win.blit(table, (1050, 50 + i*30))
            for i in range(0, len(snakes), 1):
                table = font.render(str(i + 1) + ".", True, (0, 0, 0))
                win.blit(table, (870, 50 + i*30))
    
            pygame.display.update()
            clock.tick(140)
    
        return_snakes()
    
        #create food from head of dead snake
        ds = [snake.x, snake.y, snake.color, 15, 5]
        dead_snake.append(ds)
    
        #create food from segments of dead snake
        for i in range(0, len(segments), 1):
            ds = [segments[i].x, segments[i].y, snake.color, 15, 5]
            dead_snake.append(ds)
    
        send_snake()
        dead_snake = []
    
        clock.tick(140)
    
        c.client.close()
    
        while True:
            #quit game
            for event in pygame.event.get():
                keys = pygame.key.get_pressed()
                if (event.type == pygame.QUIT) or keys[pygame.K_ESCAPE] or keys[pygame.K_F4] and pygame.key.get_mods() & pygame.KMOD_ALT:
                    sys.exit()
            if keys[pygame.K_SPACE]:
                c = Client(ip, port)
                c.server()
                c.send_message(snake.name)
                received = c.receive_message()
                snake = Snake(received[0][0], received[0][1], received[0][2], received[0][3], received[0][4], received[0][5], received[0][6], received[0][7])
                segments = []
                segment = Segment(snake.x, snake.y, snake.diameter, snake.step, snake)
                segments.append(segment)
                for i in range(0, snake.length - 2, 1):
                    segment = Segment(snake.x, snake.y, snake.diameter, snake.step, segment)
                    segments.append(segment)
                break
    
            win.fill((100, 100, 100))
            screen()
    
            #press space bar to continue
            cont = big_font.render("Press space bar to continue", True, (0, 0, 0))
            center = cont.get_rect(center=(550, 350))
            win.blit(cont, center)
    
            pygame.display.update()
            clock.tick(140)
    
    import socket
    import pickle
    
    ip = "10.0.0.3"
    port = 5555
    
    class Client:
        def __init__(self, ip, port):
            self.ip = ip
            self.port = port
            self.address = (self.ip, self.port)
            self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
        def server(self):
            try:
                self.client.connect(self.address)
                print(f"Successfully connected to {self.address}")
            except:
                pass
    
        def send_message(self, message):
            self.client.sendall(pickle.dumps(message))
    
        def receive_message(self):
            return pickle.loads(self.client.recv(8192*4))