Reputation: 11
I am relatively new to python and programming in general and would like to know how I can add multiplayer functionality to a single player game I made with pygame. I have already made both a functional server that accepts multiple connections and a client to make a system that can be used to send messages. I tried to make a client that runs a game and sends various game data (such as x position) to the server, while also receiving such data from other clients, but to no avail. This is my messaging client code:
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()
It serves its purpose perfectly fine but I can't combine it with my single player game code. I tried changing what the sendMSG function does and treating it as the game loop. I was able to get to the point where the client sent positional data when directional keys were pressed and the values changed but had trouble when trying to receive that data. I used threads but for some reason it doesn't run the game and receive data at the same time. Maybe my thought process was wrong? I would very much appreciate it if you could help me figure out how to add my game loop to the client. Thanks.
Upvotes: 1
Views: 1299
Reputation: 2335
Your client code has some issues that make it hard to integrate into a framework that as far as I understand has its own event loop (i.e. pygame, which I never used):
self.sock.send(bytes(input(''), 'utf-8'))
blocks on input
, thus blocking the game event loop. You try to avoid this by putting it into its own thread, but now you have moved the keyboard to that other thread entirely. The other thing is that
data = self.sock.recv(1024)
also blocks. Now the thread you just freed from blocking on input
is blocked by socket.recv
.
Your idea of using UDP is indeed promising. You can now build a game client class with two (almost) non-blocking methods send_message()
and 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
As I don't know pygame, I hacked up my own primitive game event loop, which handles my position (my_x, my_y
) and the other players' (positions
) and moves my player in a random direction once in a while, sending around and receiving as messages positions of players as dicts of the form `{"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)
The server itself just echos each message it receives to all the other clients it has ever heard of:
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))
You will have to somehow fit the idea of the game()
function into the pygame event loop. I wish you the best of luck.
Upvotes: 1