External Exam Download Resources Web Applications Games Recycle Bin

Network multiplayer pygame

server.py

import socket               # Import socket module
server_ip = "127.0.0.1"     # Server host IP address on the network
s = socket.socket()         # Create a socket object
s.bind((server_ip, 5000))   # Bind to the IP and service port
s.listen(5)                 # Listen and set maximum connections

pool = set()                # For tracking concurrent connections
import threading
import json

def gameThread(client, address):
  try:
    pool.add(client)        # On connect join the pool
    print(address, 'joined the game.')
    while True:
      raw = client.recv(1024).decode("utf-8") # Recieve data into a string buffer with a maximum size of 1024 bytes at one time
      # "For best match with hardware and network realities, the value of bufsize should be a relatively small power of 2, for example, 4096"
      # Source: https://docs.python.org/3/library/socket.html
      data = json.loads(raw) #e.g.: {'x': 287, 'y': 192}
      data['addr'] = address #now: {'x': 287, 'y': 192, 'addr': ('127.0.0.1', 57861)}
      for connection in pool:
        if connection != client:                # For all OTHER connections listening in the pool:
          connection.send(json.dumps(data).encode("utf-8")) # Broadcast the data received 
  except (BrokenPipeError, IOError):            # Client socket disconnect, triggers a "SIGPIPE" or "broken pipe" signal
    print(address, 'left the game.')
  except Exception as e:
    print(address, 'had an error:', e)
  finally:
    pool.remove(client)                         # try except finally always comes last, remove from pool
    client.close()

## MAIN ##
while True:
  client, address = s.accept()     # Establish connection with client.
  # Create an activity that will run in a separate flow (or control) of execution (i.e., a Thread):
  t = threading.Thread(target=gameThread, args=(client, address))
  t.start() # This program will therefore have multiple Threads (seemingly) happening at once

client.py

import socket
server_ip = "127.0.0.1"     # Server host IP address on the network
s = socket.socket()
s.connect((server_ip, 5000))
import time

import pygame
pygame.init()
screen = pygame.display.set_mode((400, 300))
done = False
clock = pygame.time.Clock()

x,y = 170,120 #start square in middle centre
import json #for sending and receiving data using a clean syntax

other_players = {}
def updateOtherPlayersMovements():
  while True:
    try:
      raw = s.recv(1024).decode('utf-8')
      if raw: #e.g., data = ['275', '216', ('127.0.0.1', 57752)]
        data = json.loads(raw) #e.g.: {'x': 170, 'y': 120, 'addr': ['127.0.0.1', 57284]}
        ip,port = data['addr'] #other addr, [0] is ip, [1] is port int, unpacks to 2 var
        x,y = data['x'],data['y'] #other x,y
        other_players.update( {ip+':'+str(port):{'x':int(x),'y':int(y)}} )
    except:
        pass
    
import threading
listener = threading.Thread(target=updateOtherPlayersMovements)
listener.start()

while not done:
    pygame.event.clear()
    pressed = pygame.key.get_pressed()
    if pressed[pygame.K_UP]: y = y - 3
    if pressed[pygame.K_DOWN]: y = y + 3
    if pressed[pygame.K_LEFT]: x = x - 3
    if pressed[pygame.K_RIGHT]: x = x + 3
    if pressed:
        s.send( json.dumps({'x':x, 'y':y}).encode('utf-8') )
        
    screen.fill((255, 255, 255)) #white background
    pygame.draw.rect(screen, (0,0,0), pygame.Rect(x, y, 60, 60))
    for player in other_players:
        other_x, other_y = other_players[player]['x'], other_players[player]['y']
        pygame.draw.rect(screen, (255,0,0), pygame.Rect(other_x, other_y, 60, 60))
    pygame.display.flip()
    clock.tick(60)
    
pygame.quit()