Last active
December 16, 2023 13:40
-
-
Save PlainSight/5f324f9a215e72fb2539454c88ded5bc to your computer and use it in GitHub Desktop.
Simple Multiplayer Python Game and Server
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pygame, sys | |
from pygame.locals import * | |
import pickle | |
import select | |
import socket | |
WIDTH = 400 | |
HEIGHT = 400 | |
BUFFERSIZE = 2048 | |
screen = pygame.display.set_mode((WIDTH, HEIGHT)) | |
pygame.display.set_caption('Game') | |
clock = pygame.time.Clock() | |
sprite1 = pygame.image.load('images/BlueThing/BlueThing_front.png') | |
sprite2 = pygame.image.load('images/Ladette/Ladette_front.png') | |
sprite3 = pygame.image.load('images/TrashPanda/TrashPanda_front.png') | |
sprite4 = pygame.image.load('images/Tubby/Tubby_front.png') | |
serverAddr = '127.0.0.1' | |
if len(sys.argv) == 2: | |
serverAddr = sys.argv[1] | |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
s.connect((serverAddr, 4321)) | |
playerid = 0 | |
sprites = { 0: sprite1, 1: sprite2, 2: sprite3, 3: sprite4 } | |
class Minion: | |
def __init__(self, x, y, id): | |
self.x = x | |
self.y = y | |
self.vx = 0 | |
self.vy = 0 | |
self.id = id | |
def update(self): | |
self.x += self.vx | |
self.y += self.vy | |
if self.x > WIDTH - 50: | |
self.x = WIDTH - 50 | |
if self.x < 0: | |
self.x = 0 | |
if self.y > HEIGHT - 50: | |
self.y = HEIGHT - 50 | |
if self.y < 0: | |
self.y = 0 | |
if self.id == 0: | |
self.id = playerid | |
def render(self): | |
screen.blit(sprites[self.id % 4], (self.x, self.y)) | |
#game events | |
#['event type', param1, param2] | |
# | |
#event types: | |
# id update | |
# ['id update', id] | |
# | |
# player locations | |
# ['player locations', [id, x, y], [id, x, y] ...] | |
#user commands | |
# position update | |
# ['position update', id, x, y] | |
class GameEvent: | |
def __init__(self, vx, vy): | |
self.vx = vx | |
self.vy = vy | |
cc = Minion(50, 50, 0) | |
minions = [] | |
while True: | |
ins, outs, ex = select.select([s], [], [], 0) | |
for inm in ins: | |
gameEvent = pickle.loads(inm.recv(BUFFERSIZE)) | |
if gameEvent[0] == 'id update': | |
playerid = gameEvent[1] | |
print(playerid) | |
if gameEvent[0] == 'player locations': | |
gameEvent.pop(0) | |
minions = [] | |
for minion in gameEvent: | |
if minion[0] != playerid: | |
minions.append(Minion(minion[1], minion[2], minion[0])) | |
for event in pygame.event.get(): | |
if event.type == QUIT: | |
pygame.quit() | |
sys.exit() | |
if event.type == KEYDOWN: | |
if event.key == K_LEFT: cc.vx = -10 | |
if event.key == K_RIGHT: cc.vx = 10 | |
if event.key == K_UP: cc.vy = -10 | |
if event.key == K_DOWN: cc.vy = 10 | |
if event.type == KEYUP: | |
if event.key == K_LEFT and cc.vx == -10: cc.vx = 0 | |
if event.key == K_RIGHT and cc.vx == 10: cc.vx = 0 | |
if event.key == K_UP and cc.vy == -10: cc.vy = 0 | |
if event.key == K_DOWN and cc.vy == 10: cc.vy = 0 | |
clock.tick(60) | |
screen.fill((255,255,255)) | |
cc.update() | |
for m in minions: | |
m.render() | |
cc.render() | |
pygame.display.flip() | |
ge = ['position update', playerid, cc.x, cc.y] | |
s.send(pickle.dumps(ge)) | |
s.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import socket | |
import asyncore | |
import select | |
import random | |
import pickle | |
import time | |
BUFFERSIZE = 512 | |
outgoing = [] | |
class Minion: | |
def __init__(self, ownerid): | |
self.x = 50 | |
self.y = 50 | |
self.ownerid = ownerid | |
minionmap = {} | |
def updateWorld(message): | |
arr = pickle.loads(message) | |
print(str(arr)) | |
playerid = arr[1] | |
x = arr[2] | |
y = arr[3] | |
if playerid == 0: return | |
minionmap[playerid].x = x | |
minionmap[playerid].y = y | |
remove = [] | |
for i in outgoing: | |
update = ['player locations'] | |
for key, value in minionmap.items(): | |
update.append([value.ownerid, value.x, value.y]) | |
try: | |
i.send(pickle.dumps(update)) | |
except Exception: | |
remove.append(i) | |
continue | |
print ('sent update data') | |
for r in remove: | |
outgoing.remove(r) | |
class MainServer(asyncore.dispatcher): | |
def __init__(self, port): | |
asyncore.dispatcher.__init__(self) | |
self.create_socket(socket.AF_INET, socket.SOCK_STREAM) | |
self.bind(('', port)) | |
self.listen(10) | |
def handle_accept(self): | |
conn, addr = self.accept() | |
print ('Connection address:' + addr[0] + " " + str(addr[1])) | |
outgoing.append(conn) | |
playerid = random.randint(1000, 1000000) | |
playerminion = Minion(playerid) | |
minionmap[playerid] = playerminion | |
conn.send(pickle.dumps(['id update', playerid])) | |
SecondaryServer(conn) | |
class SecondaryServer(asyncore.dispatcher_with_send): | |
def handle_read(self): | |
recievedData = self.recv(BUFFERSIZE) | |
if recievedData: | |
updateWorld(recievedData) | |
else: self.close() | |
MainServer(4321) | |
asyncore.loop() |
When I run the app in the mac terminal on python 3.9.1, I get the error:
s.connect((serverAddr, 4321))
ConnectionRefusedError: [Errno 61] Connection refused
Is there a solution?
When I run the app in the mac terminal on python 3.9.1, I get the error:
s.connect((serverAddr, 4321)) ConnectionRefusedError: [Errno 61] Connection refused
Is there a solution?
You need to change the address to your local ip or your hoes machine name
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I had the same problem as zywek123.
The reason is that the server writes to the socket a second time before the client reads from the socket the first message. This means that the client reads multiple pickled objects at once and pickle.loads just returns the first object.
Fortunately concatenated pickled objects can be easily separated:
https://stackoverflow.com/a/61293678/7658204