Добавление многопользовательской функциональности в одиночную игру с сокетами - PullRequest
0 голосов
/ 03 ноября 2018

Я относительно новичок в python и программировании в целом и хотел бы знать, как я могу добавить многопользовательскую функциональность в однопользовательскую игру, которую я сделал с помощью pygame. Я уже сделал и функциональный сервер, который принимает несколько соединений, и клиент для создания системы, которую можно использовать для отправки сообщений. Я попытался создать клиент, который запускает игру и отправляет на сервер различные игровые данные (например, положение x), а также получает такие данные от других клиентов, но безрезультатно. Это мой код клиента сообщений:

import socket
import threading

class Client:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def sendMSG(self):
        while True:
           self.sock.send(bytes(input(''), 'utf-8'))

    def __init__(self):
        port = 12345
        self.sock.connect(('127.0.0.1', port))

        iThread = threading.Thread(target=self.sendMSG)
        iThread.daemon = True
        iThread.start()

        while True:
           data = self.sock.recv(1024)
           if not data:
              break
           print(str(data, 'utf-8'))


client = Client()

Он отлично справляется со своей задачей, но я не могу совместить его с кодом моей одиночной игры. Я попытался изменить то, что делает функция sendMSG, и обработал ее как игровой цикл. Мне удалось добраться до точки, когда клиент отправлял позиционные данные, когда нажимались клавиши со стрелками и значения менялись, но у меня возникли проблемы при попытке получить эти данные. Я использовал потоки, но по какой-то причине он не запускает игру и не получает данные одновременно. Может быть, мой мыслительный процесс был неправильным? Я был бы очень признателен, если бы вы помогли мне понять, как добавить мой игровой цикл в клиент. Благодарю.

1 Ответ

0 голосов
/ 05 ноября 2018

В вашем клиентском коде есть некоторые проблемы, которые затрудняют интеграцию в среду, которая, насколько я понимаю, имеет собственный цикл обработки событий (то есть pygame, который я никогда не использовал):

self.sock.send(bytes(input(''), 'utf-8'))

блокирует input, таким образом блокируя цикл игровых событий. Вы пытаетесь избежать этого, помещая его в собственный поток, но теперь вы полностью переместили клавиатуру в этот другой поток. Другое дело, что

data = self.sock.recv(1024)

также блокирует. Теперь поток, который вы только что освободили от блокировки на input, заблокирован socket.recv.

Ваша идея использования UDP действительно многообещающая. Теперь вы можете создать игровой клиентский класс с двумя (почти) неблокирующими методами send_message() и check_message():

import socket
import json

class GameClient:
    def __init__(self, server=("127.0.0.1", 12000)):
        self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.client_socket.settimeout(0.1)
        self.server = server
    def send_message(self, msg):
        """ JSON-encode and send msg to server. """
        message = json.dumps(msg).encode("utf8")
        self.client_socket.sendto(message, self.server)
    def check_message(self):
        """ Check if there's a message from the server and return it, or None. """
        try:
            message, address = self.client_socket.recvfrom(1024)
            return json.loads(message.decode("utf8"))
        except socket.timeout:
            pass  # this passes as "non-blocking" for now
        return None

Поскольку я не знаю pygame, я взломал свой собственный примитивный игровой цикл, который обрабатывает мою позицию (my_x, my_y) и других игроков (positions) и перемещает моего игрока в случайном направлении один раз в некоторое время, отправляя и получая в качестве сообщений позиции игроков в виде диктов в виде `{" playername ": [xpos, ypos]} ':

import random
import time

def game(player):
    me = GameClient()
    positions = {}
    my_x = my_y = 0
    me.send_message({player: [my_x, my_y]})
    while True:
        move = random.randrange(80)
        if move == 0:
            my_x -= 1
        elif move == 1:
            my_x += 1
        elif move == 2:
            my_y -= 1
        elif move == 3:
            my_y += 1
        if move < 4:
            me.send_message({player: [my_x, my_y]})
            print("me: {}".format([my_x, my_y]))
        updates = me.check_message()
        while updates:
            positions.update(updates)
            print("updates: {}".format(updates))
            print("positions: {}".format(positions))
            updates = me.check_message()
        time.sleep(0.1)

Сам сервер просто передает каждое полученное сообщение всем другим клиентам, о которых он когда-либо слышал:

import socket
import json

def game_server():
    positions = {}
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_socket.bind(('', 12000))
    while True:
        message, address = server_socket.recvfrom(1024)
        print("update from {}".format(address))
        positions[address] = json.loads(message.decode("utf8"))
        for client in positions.keys():
            if client != address:
                server_socket.sendto(message, client)
        print("game positions: {}".format(positions))

Вы должны будете каким-то образом вписать идею функции game() в цикл событий Pygame. Я желаю вам удачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...