Headless server, separate view client

pull/2/head
kaqu 3 years ago
parent b63330291a
commit 1fb57dd0ed

@ -10,7 +10,7 @@
"request": "launch",
"program": "${file}",
//"args": ["--sizeable", "--server"],
"args": ["--sizeable", "--client", "1", "127.0.0.1"],
"args": ["--sizeable", "--player", "1", "127.0.0.1"],
"console": "integratedTerminal"
}
]

@ -49,7 +49,7 @@ The game tries to identify the event inputs for two gamepads automatically (othe
Have all shell files (*.sh) executable!
Then to run a game server instance use
Then to run a headless game server instance use
./srv.sh
@ -72,6 +72,11 @@ the cl1.sh or cl2.sh file respectively.
Also, you may want to play a client in fullscreen.
To achieve this, substitute --sizeable by --fullscreen in both files.
Finally, you may also connect one pure view client as
./view.sh
### 2.3 Scoring ###
A game is won by the player who first reaches 10 points.

@ -1,2 +1,2 @@
# Client, player #1
./pandemic_pong.py --sizeable --client 1 127.0.0.1
./pandemic_pong.py --sizeable --player 1 127.0.0.1

@ -1,2 +1,2 @@
# Client, player #2
./pandemic_pong.py --sizeable --client 2 127.0.0.1
./pandemic_pong.py --sizeable --player 2 127.0.0.1

@ -7,6 +7,8 @@ Pandemic Pong global constants
"""
DEVLOCAL = True # TODO: For client/server local development set to 'True'! For publication to 'False'!
"""Buttons in 'state<n>'"""
BTN_STATE_NONE = 0 # No Button pressed ...
BTN_TRIGGER = 1 # [Blue/X]

@ -29,7 +29,7 @@ class PongGame:
self.playsound = pgc.NOSOUND # Play the provided sound & reset locally
if bIsServer == True:
vs.init_server_socket() # Create the TCP server socket for streaming
[vs.Thread(i, self) for i in range(2)] # Spawn player listeners & streams
[vs.Thread(i, self) for i in range(3)] # Spawn player listeners & streams
print("Game server started & listening ...")
def game_fsm(self, player1, player2):

@ -7,5 +7,8 @@ Pandemic Pong global variables
"""
bSizeable = False # Qt5 window sizeable
bIsServer = False # Global server indication
bIsLocal = True # By default
bIsViewer = False # Global viewer indication
bIsLocal = True # Local game only, by default

@ -0,0 +1,245 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
pong_viewer.py
Pandemic Pong view client only
"""
import sys, time
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
from PyQt5.QtGui import QPainter, QBrush, QPen, QFont, QFontMetrics
import game_objects.pong_constants as pgc # Pong global constants
import game_objects.pong_globalvars as pgv # Pong global variables
from game_objects.pong_player import PongPlayer
from game_objects.pong_object import PongObject
from game_objects.pong_game import PongGame
import game_objects.pong_viewserver as vs # Pong TCP view server (threads)
def draw_buttonstate(x, y, player, painter, scale_x, scale_y):
"""View function: Indicate last button pressed (not strictly necessary ...)"""
if (player.state & pgc.BTN_TRIGGER) == pgc.BTN_TRIGGER:
painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.blue, Qt.SolidPattern)) # Fill
painter.drawEllipse(int(x * scale_x), int(y * scale_y), int(20 * scale_x), int(20 * scale_y)) # Blue button
if (player.state & pgc.BTN_TOP) == pgc.BTN_TOP:
painter.setPen(QPen(Qt.green, 1, Qt.SolidLine)) # Green
painter.setBrush(QBrush(Qt.green, Qt.SolidPattern))
painter.drawEllipse(int((x + 30) * scale_x), int(y * scale_y), int(20 * scale_x), int(20 * scale_y))
if (player.state & pgc.BTN_THUMB) == pgc.BTN_THUMB:
painter.setPen(QPen(Qt.red, 1, Qt.SolidLine)) # Red
painter.setBrush(QBrush(Qt.red, Qt.SolidPattern))
painter.drawEllipse(int((x + 60) * scale_x), int(y * scale_y), int(20 * scale_x), int(20 * scale_y))
if (player.state & pgc.BTN_THUMB2) == pgc.BTN_THUMB2:
painter.setPen(QPen(Qt.yellow, 1, Qt.SolidLine)) # Yellow
painter.setBrush(QBrush(Qt.yellow, Qt.SolidPattern))
painter.drawEllipse(int((x + 90) * scale_x), int(y * scale_y), int(20 * scale_x), int(20 * scale_y))
class pongWindow(QMainWindow):
"""Qt5 base class"""
def __init__(self, debug_x, debug_y, viewserver, game, ball, player1, player2):
super().__init__()
self.title = "Pandemic Pong"
if pgv.bIsLocal:
self.title = "Pandemic Pong (Local)"
if pgv.bIsViewer:
self.title = "Pandemic Pong (Viewer)"
self.top = 0
self.left = 0
self.win_width = 1920
self.win_height = 960
self.viewserver = viewserver
self.game = game
self.ball = ball
self.player1 = player1
self.player2 = player2
self.InitWindow(debug_x, debug_y) # s.b. !
def InitWindow(self, debug_x, debug_y):
"""Prepare window for later drawing"""
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.win_width, self.win_height)
if pgv.bSizeable:
if pgc.DEVLOCAL == True: # Small windows for convenience ...
self.resize(int(self.win_width * 2 / 3 / 3), int(self.win_height * 2 / 2.8 / 3))
#self.move(int(self.win_width / 2.5), int(self.win_height / 16))
if pgc.DEVLOCAL == True:
self.move(debug_x, debug_y)
else:
self.resize(int(self.win_width * 2 / 3), int(self.win_height * 2 / 2.8))
self.move(int(self.win_width / 6), int(self.win_height / 8))
else:
self.showFullScreen() # Regular full screen play by default
self.pic = QPixmap("pictures/pong_background.png")
self.show()
# Don't annoy developers (except for local play - to test game mechanics !) ...
if (pgc.DEVLOCAL == False) and (pgv.bIsServer == False):
# Enforce caching ...
playsound(pgc.sounds[pgc.PLAYERCONTACTSOUND])
playsound(pgc.sounds[pgc.PLAYERMISSSOUND])
playsound(pgc.sounds[pgc.WALLCONTACTSOUND])
playsound(pgc.sounds[pgc.GAMEEXITSOUND])
time.sleep(0.5)
playsound(pgc.sounds[pgc.GAMESPLASHSOUND])
self.timer = QTimer() # Start processing 'loop'
self.timer.setInterval(25) # 25ms
self.timer.setTimerType(Qt.PreciseTimer)
self.timer.timeout.connect(self.on_timer)
self.timer.start()
def paintEvent(self, event):
"""Called whenever drawing is necessary ..."""
scale_x = self.width() / pgc.GAMEAREA_MAX_X
scale_y = self.height() / (pgc.GAMEAREA_MAX_Y + 20) # Compensate header
painter = QPainter(self) # Painter need's to be freshly picked ...
painter.setPen(QPen(Qt.white, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.white, Qt.SolidPattern)) # Fill
painter.drawPixmap(self.rect(), self.pic) # Game background
# Font metrics assume font is monospaced!
fntLarge = QFont("Monospace", int(120 * min(scale_x, scale_y)))
fntLarge.setKerning(False)
fntLarge.setFixedPitch(True)
fm = QFontMetrics(fntLarge)
fntMedium = QFont("Monospace", int(40 * min(scale_x, scale_y)))
fntMedium.setKerning(False)
fntMedium.setFixedPitch(True)
if self.game.state == pgc.STATE_WELCOME: # Welcome screen
painter.drawPixmap(self.rect(), QPixmap("pictures/pandemic_pong.png"))
painter.setFont(fntLarge)
painter.drawText(int(50 * scale_x), int((pgc.GAMEAREA_MAX_Y / 2 - 100) * scale_y), "PANDEMIC")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 300) * scale_x), int((pgc.GAMEAREA_MAX_Y / 2 + 100) * scale_y), "PONG")
elif self.game.state == pgc.STATE_START: # Wait for start button pressed
painter.setFont(fntMedium)
painter.drawText(int(150 * scale_x), int((pgc.GAMEAREA_MAX_Y / 2 - 100) * scale_y), "Press [Start] ...")
painter.drawText(int(50 * scale_x), int((pgc.GAMEAREA_MAX_Y / 2 + 100) * scale_y), "Press [Select] to abort")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 100) * scale_x), int(pgc.GAMEAREA_MAX_Y / 5 * scale_y), "Press [color] button")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 100) * scale_x), int(pgc.GAMEAREA_MAX_Y / 5 * 2 * scale_y), "to infect player,")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 100) * scale_x), int(pgc.GAMEAREA_MAX_Y / 5 * 3 * scale_y), "view will pickup virus!")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 100) * scale_x), int(pgc.GAMEAREA_MAX_Y / 5 * 4 * scale_y), "Match color to deflect!")
elif self.game.state == pgc.STATE_PLAY: # Actual play 'til score reaches 10 points
# Ball gives NO indication ...
painter.drawRect(int(self.ball.x * scale_x), int(self.ball.y * scale_y), int(self.ball.w * scale_x), int(self.ball.h * scale_y)) # Ball
if self.player1.color == pgc.COL_WHITE:
painter.setPen(QPen(Qt.white, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.white, Qt.SolidPattern)) # Fill
elif self.player1.color == pgc.COL_BLUE:
painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.blue, Qt.SolidPattern)) # Fill
elif self.player1.color == pgc.COL_GREEN:
painter.setPen(QPen(Qt.green, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.green, Qt.SolidPattern)) # Fill
elif self.player1.color == pgc.COL_RED:
painter.setPen(QPen(Qt.red, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.red, Qt.SolidPattern)) # Fill
elif self.player1.color == pgc.COL_YELLOW:
painter.setPen(QPen(Qt.yellow, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.yellow, Qt.SolidPattern)) # Fill
painter.drawRect(int(self.player1.x * scale_x), int(self.player1.y * scale_y), int(self.player1.w * scale_x), int(self.player1.h * scale_y)) # Player left
if self.player2.color == pgc.COL_WHITE:
painter.setPen(QPen(Qt.white, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.white, Qt.SolidPattern)) # Fill
elif self.player2.color == pgc.COL_BLUE:
painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.blue, Qt.SolidPattern)) # Fill
elif self.player2.color == pgc.COL_GREEN:
painter.setPen(QPen(Qt.green, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.green, Qt.SolidPattern)) # Fill
elif self.player2.color == pgc.COL_RED:
painter.setPen(QPen(Qt.red, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.red, Qt.SolidPattern)) # Fill
elif self.player2.color == pgc.COL_YELLOW:
painter.setPen(QPen(Qt.yellow, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.yellow, Qt.SolidPattern)) # Fill
painter.drawRect(int(self.player2.x * scale_x), int(self.player2.y * scale_y), int(self.player2.w * scale_x), int(self.player2.h * scale_y)) # Player right
painter.setPen(QPen(Qt.white, 1, Qt.SolidLine)) # Reset standard colour
painter.setBrush(QBrush(Qt.white, Qt.SolidPattern))
# Game scores
painter.setFont(fntLarge)
# Score player #1
msg = str(self.player1.score)
msg_width = fm.width(msg)
painter.drawText(int((800 - msg_width) * scale_x), int(154 * scale_y), "{}".format(msg))
# Score player #2
painter.drawText(int((pgc.GAMEAREA_MAX_X - 840) * scale_x), int(154 * scale_y), "{}".format(self.player2.score))
# Indicate Button states
draw_buttonstate(60, 50, self.player1, painter, scale_x, scale_y)
draw_buttonstate(pgc.GAMEAREA_MAX_X - 180, 50, self.player2, painter, scale_x, scale_y)
elif self.game.state == pgc.STATE_GAMERESULTS: # Display winner of this game
painter.setFont(fntMedium)
if self.player1.score > 9:
msg = "Game player #1 Total score {}:{}".format(self.game.p1_game, self.game.p2_game)
else:
msg = "Game player #2 Total score {}:{}".format(self.game.p1_game, self.game.p2_game)
painter.drawText(int(200 * scale_x), int(pgc.GAMEAREA_MAX_Y / 2 * scale_y), msg)
elif self.game.state == pgc.STATE_FINALRESULTS: # Display set winner
painter.setFont(fntMedium)
if self.game.p1_game > 2:
msg = "Match player #1 Total score {}:{}".format(self.game.p1_game, self.game.p2_game)
else:
msg = "Match player #2 Total score {}:{}".format(self.game.p1_game, self.game.p2_game)
painter.drawText(int(200 * scale_x), int(pgc.GAMEAREA_MAX_Y / 2 * scale_y), msg)
elif self.game.state == pgc.STATE_EXIT: # Indicate good bye ...
painter.setFont(fntMedium)
painter.drawText(int(50 * scale_x), int(pgc.GAMEAREA_MAX_Y / 2 * scale_x), "Bye!")
# Force font memory free (indication GC)
del fm
del fntMedium
del fntLarge
@pyqtSlot()
def on_timer(self):
"""Qt5 timer event runs the game ..."""
self.timer.stop() # Block overrun
if (pgv.bIsServer == False) and (pgv.bIsLocal == False): # Player 1 or 2?
rc = self.viewserver.receive_data() # Retrieve server stati
if rc < 0: # Server down, abort game
self.player2.exit()
self.player1.exit()
sys.exit(0)
#if rc > 0: # May be useful later ...
# bChanged = True
if self.game.pong_game(self.ball, self.player1, self.player2) == False:
# Quit gracefully
self.player2.exit()
self.player1.exit()
sys.exit(0)
self.update() # Enforce redraw (explicitely)
self.timer.start() # Re-enable timer event
def run_Qt5_viewer(debug_x, debug_y, viewserver, game, ball, player1, player2):
"""Qt5 viewer"""
app = QApplication(sys.argv) # Eval command line args
window = pongWindow(debug_x, debug_y, viewserver, game, ball, player1, player2) # Create Qt5 GUI
return app.exec_() # & run logic

@ -13,18 +13,11 @@ History:
02.12.20/KQ Streamlined version published
"""
DEVLOCAL = False # TODO: For client/server local development set to 'True'!
import sys, os, fcntl, time, random
from glob import glob
from playsound import playsound
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import *
from PyQt5.QtCore import Qt, QTimer, pyqtSlot
from PyQt5.QtGui import QPainter, QBrush, QPen, QFont, QFontMetrics
import game_objects.pong_constants as pgc # Pong global constants
import game_objects.pong_globalvars as pgv # Pong global variables
from game_objects.pong_player import PongPlayer
@ -32,241 +25,46 @@ from game_objects.pong_object import PongObject
from game_objects.pong_game import PongGame
import game_objects.pong_viewserver as vs # Pong TCP view server (threads)
def draw_buttonstate(x, y, player, painter, scale_x, scale_y):
"""View function: Indicate last button pressed (not strictly necessary ...)"""
if (player.state & pgc.BTN_TRIGGER) == pgc.BTN_TRIGGER:
painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.blue, Qt.SolidPattern)) # Fill
painter.drawEllipse(int(x * scale_x), int(y * scale_y), int(20 * scale_x), int(20 * scale_y)) # Blue button
if (player.state & pgc.BTN_TOP) == pgc.BTN_TOP:
painter.setPen(QPen(Qt.green, 1, Qt.SolidLine)) # Green
painter.setBrush(QBrush(Qt.green, Qt.SolidPattern))
painter.drawEllipse(int((x + 30) * scale_x), int(y * scale_y), int(20 * scale_x), int(20 * scale_y))
if (player.state & pgc.BTN_THUMB) == pgc.BTN_THUMB:
painter.setPen(QPen(Qt.red, 1, Qt.SolidLine)) # Red
painter.setBrush(QBrush(Qt.red, Qt.SolidPattern))
painter.drawEllipse(int((x + 60) * scale_x), int(y * scale_y), int(20 * scale_x), int(20 * scale_y))
if (player.state & pgc.BTN_THUMB2) == pgc.BTN_THUMB2:
painter.setPen(QPen(Qt.yellow, 1, Qt.SolidLine)) # Yellow
painter.setBrush(QBrush(Qt.yellow, Qt.SolidPattern))
painter.drawEllipse(int((x + 90) * scale_x), int(y * scale_y), int(20 * scale_x), int(20 * scale_y))
class pongWindow(QMainWindow):
"""Qt5 base class"""
def __init__(self):
super().__init__()
self.title = "Pandemic Pong"
self.top = 0
self.left = 0
self.win_width = 1920
self.win_height = 960
self.InitWindow() # s.b. !
def InitWindow(self):
"""Prepare window for later drawing"""
global debug_x, debug_y
self.setWindowTitle(self.title)
self.setGeometry(self.top, self.left, self.win_width, self.win_height)
if bSizeable:
if DEVLOCAL == True: # Small windows for convenience ...
self.resize(int(self.win_width * 2 / 3 / 3), int(self.win_height * 2 / 2.8 / 3))
#self.move(int(self.win_width / 2.5), int(self.win_height / 16))
self.move(debug_x, debug_y)
else:
self.resize(int(self.win_width * 2 / 3), int(self.win_height * 2 / 2.8))
self.move(int(self.win_width / 6), int(self.win_height / 8))
else:
self.showFullScreen() # Regular full screen play by default
self.pic = QPixmap("pictures/pong_background.png")
self.show()
# Don't annoy developers (except for local play - to test game mechanics !) ...
if (DEVLOCAL == False) and (pgv.bIsServer == False):
# Enforce caching ...
playsound(pgc.sounds[pgc.PLAYERCONTACTSOUND])
playsound(pgc.sounds[pgc.PLAYERMISSSOUND])
playsound(pgc.sounds[pgc.WALLCONTACTSOUND])
playsound(pgc.sounds[pgc.GAMEEXITSOUND])
time.sleep(0.5)
playsound(pgc.sounds[pgc.GAMESPLASHSOUND])
self.timer = QTimer() # Start processing 'loop'
self.timer.setInterval(25) # 25ms
self.timer.setTimerType(Qt.PreciseTimer)
self.timer.timeout.connect(self.on_timer)
self.timer.start()
def paintEvent(self, event):
"""Called whenever drawing is necessary ..."""
global game, ball, player1, player2
scale_x = self.width() / pgc.GAMEAREA_MAX_X
scale_y = self.height() / (pgc.GAMEAREA_MAX_Y + 20) # Compensate header
painter = QPainter(self) # Painter need's to be freshly picked ...
painter.setPen(QPen(Qt.white, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.white, Qt.SolidPattern)) # Fill
painter.drawPixmap(self.rect(), self.pic) # Game background
# Font metrics assume font is monospaced!
fntLarge = QFont("Monospace", int(120 * min(scale_x, scale_y)))
fntLarge.setKerning(False)
fntLarge.setFixedPitch(True)
fm = QFontMetrics(fntLarge)
fntMedium = QFont("Monospace", int(40 * min(scale_x, scale_y)))
fntMedium.setKerning(False)
fntMedium.setFixedPitch(True)
if game.state == pgc.STATE_WELCOME: # Welcome screen
painter.drawPixmap(self.rect(), QPixmap("pictures/pandemic_pong.png"))
painter.setFont(fntLarge)
painter.drawText(int(50 * scale_x), int((pgc.GAMEAREA_MAX_Y / 2 - 100) * scale_y), "PANDEMIC")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 300) * scale_x), int((pgc.GAMEAREA_MAX_Y / 2 + 100) * scale_y), "PONG")
elif game.state == pgc.STATE_START: # Wait for start button pressed
painter.setFont(fntMedium)
painter.drawText(int(150 * scale_x), int((pgc.GAMEAREA_MAX_Y / 2 - 100) * scale_y), "Press [Start] ...")
painter.drawText(int(50 * scale_x), int((pgc.GAMEAREA_MAX_Y / 2 + 100) * scale_y), "Press [Select] to abort")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 100) * scale_x), int(pgc.GAMEAREA_MAX_Y / 5 * scale_y), "Press [color] button")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 100) * scale_x), int(pgc.GAMEAREA_MAX_Y / 5 * 2 * scale_y), "to infect player,")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 100) * scale_x), int(pgc.GAMEAREA_MAX_Y / 5 * 3 * scale_y), "ball will pickup virus!")
painter.drawText(int((pgc.GAMEAREA_MAX_X / 2 + 100) * scale_x), int(pgc.GAMEAREA_MAX_Y / 5 * 4 * scale_y), "Match color to deflect!")
elif game.state == pgc.STATE_PLAY: # Actual play 'til score reaches 10 points
# Ball gives NO indication ...
painter.drawRect(int(ball.x * scale_x), int(ball.y * scale_y), int(ball.w * scale_x), int(ball.h * scale_y)) # Ball
if player1.color == pgc.COL_WHITE:
painter.setPen(QPen(Qt.white, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.white, Qt.SolidPattern)) # Fill
elif player1.color == pgc.COL_BLUE:
painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.blue, Qt.SolidPattern)) # Fill
elif player1.color == pgc.COL_GREEN:
painter.setPen(QPen(Qt.green, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.green, Qt.SolidPattern)) # Fill
elif player1.color == pgc.COL_RED:
painter.setPen(QPen(Qt.red, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.red, Qt.SolidPattern)) # Fill
elif player1.color == pgc.COL_YELLOW:
painter.setPen(QPen(Qt.yellow, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.yellow, Qt.SolidPattern)) # Fill
painter.drawRect(int(player1.x * scale_x), int(player1.y * scale_y), int(player1.w * scale_x), int(player1.h * scale_y)) # Player left
if player2.color == pgc.COL_WHITE:
painter.setPen(QPen(Qt.white, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.white, Qt.SolidPattern)) # Fill
elif player2.color == pgc.COL_BLUE:
painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.blue, Qt.SolidPattern)) # Fill
elif player2.color == pgc.COL_GREEN:
painter.setPen(QPen(Qt.green, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.green, Qt.SolidPattern)) # Fill
elif player2.color == pgc.COL_RED:
painter.setPen(QPen(Qt.red, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.red, Qt.SolidPattern)) # Fill
elif player2.color == pgc.COL_YELLOW:
painter.setPen(QPen(Qt.yellow, 1, Qt.SolidLine)) # Color/Linewidth/Pattern
painter.setBrush(QBrush(Qt.yellow, Qt.SolidPattern)) # Fill
painter.drawRect(int(player2.x * scale_x), int(player2.y * scale_y), int(player2.w * scale_x), int(player2.h * scale_y)) # Player right
painter.setPen(QPen(Qt.white, 1, Qt.SolidLine)) # Reset standard colour
painter.setBrush(QBrush(Qt.white, Qt.SolidPattern))
# Game scores
painter.setFont(fntLarge)
# Score player #1
msg = str(player1.score)
msg_width = fm.width(msg)
painter.drawText(int((800 - msg_width) * scale_x), int(154 * scale_y), "{}".format(msg))
# Score player #2
painter.drawText(int((pgc.GAMEAREA_MAX_X - 840) * scale_x), int(154 * scale_y), "{}".format(player2.score))
# Indicate Button states
draw_buttonstate(60, 50, player1, painter, scale_x, scale_y)
draw_buttonstate(pgc.GAMEAREA_MAX_X - 180, 50, player2, painter, scale_x, scale_y)
elif game.state == pgc.STATE_GAMERESULTS: # Display winner of this game
painter.setFont(fntMedium)
if player1.score > 9:
msg = "Game player #1 Total score {}:{}".format(game.p1_game, game.p2_game)
else:
msg = "Game player #2 Total score {}:{}".format(game.p1_game, game.p2_game)
painter.drawText(int(200 * scale_x), int(pgc.GAMEAREA_MAX_Y / 2 * scale_y), msg)
elif game.state == pgc.STATE_FINALRESULTS: # Display set winner
painter.setFont(fntMedium)
if game.p1_game > 2:
msg = "Match player #1 Total score {}:{}".format(game.p1_game, game.p2_game)
else:
msg = "Match player #2 Total score {}:{}".format(game.p1_game, game.p2_game)
painter.drawText(int(200 * scale_x), int(pgc.GAMEAREA_MAX_Y / 2 * scale_y), msg)
elif game.state == pgc.STATE_EXIT: # Indicate good bye ...
painter.setFont(fntMedium)
painter.drawText(int(50 * scale_x), int(pgc.GAMEAREA_MAX_Y / 2 * scale_x), "Bye!")
# Force font memory free (indication GC)
del fm
del fntMedium
del fntLarge
@pyqtSlot()
def on_timer(self):
"""Qt5 timer event runs the game ..."""
global game, ball, player1, player2
self.timer.stop() # Block overrun
if (pgv.bIsServer == False) and (pgv.bIsLocal == False): # Player 1 or 2?
rc = viewserver.receive_data() # Retrieve server stati
if rc < 0: # Server down, abort game
player2.exit()
player1.exit()
sys.exit(0)
#if rc > 0: # May be useful later ...
# bChanged = True
if game.pong_game(ball, player1, player2) == False:
# Quit gracefully
player2.exit()
player1.exit()
sys.exit(0)
self.update() # Enforce redraw (explicitely)
self.timer.start() # Re-enable timer event
if __name__ == '__main__':
bSizeable = False
pgv.bSizeable = False
pgv.bIsServer = False
pgv.bIsViewer = False
pgv.bIsLocal = True # by default
player_index = 0 # Illegal, local
player_server = ""
debug_x = 0 # Debug window positions
debug_y = 0
sMsg = "usage: ./pandemic_pong.py --[sizeable|fullscreen] --[server|client <1|2> <ip_adress>]"
if pgc.DEVLOCAL == True:
debug_x = 0 # Debug window positions
debug_y = 0
sMsg = "usage: ./pandemic_pong.py --[--sizeable|fullscreen] --[server | [viewer | player <1|2>] <ip_adress>]"
if len(sys.argv) > 1:
if sys.argv[1] == "--sizeable": # else=default=fullscreen assumed ...
bSizeable = True
pgv.bSizeable = True
if len(sys.argv) > 2:
if sys.argv[2] == "--server":
pgv.bIsServer = True
pgv.bIsLocal = False
print("*** PANDEMIC PONG (server) ***")
debug_x = int(pgc.GAMEAREA_MAX_X / 2.5) # Top centered window
debug_y = int(pgc.GAMEAREA_MAX_Y / 40)
elif len(sys.argv) > 4:
if sys.argv[2] == "--client":
if pgc.DEVLOCAL == True:
debug_x = int(pgc.GAMEAREA_MAX_X / 2.5) # Top centered window
debug_y = int(pgc.GAMEAREA_MAX_Y / 40)
elif len(sys.argv) == 4:
if sys.argv[2] == "--viewer":
pgv.bIsViewer = True
pgv.bIsLocal = False
player_server = sys.argv[3]
print("*** PANDEMIC PONG (viewer talking to game server @{}) ***".format(player_server))
if pgc.DEVLOCAL == True:
debug_x = int(pgc.GAMEAREA_MAX_X / 2.5) # Bottom centered window
debug_y = int(pgc.GAMEAREA_MAX_Y / 2)
elif len(sys.argv) == 5:
if sys.argv[2] == "--player":
pgv.bIsLocal = False
player_index = int(sys.argv[3])
player_server = sys.argv[4]
print("*** PANDEMIC PONG (player #{} talking to game server @{}) ***".format(player_index,player_server))
debug_x = int(pgc.GAMEAREA_MAX_X / 3 * 4 * (player_index-1)) # Left(0) or right top window
debug_y = int(pgc.GAMEAREA_MAX_Y / 40)
if pgc.DEVLOCAL == True:
debug_x = int(pgc.GAMEAREA_MAX_X / 3 * 4 * (player_index-1)) # Left(0) or right top window
debug_y = int(pgc.GAMEAREA_MAX_Y / 40)
else:
print(sMsg)
else:
@ -282,35 +80,38 @@ if __name__ == '__main__':
sEventQueue1 = "/dev/input/event" + str(maxEvent-1)
sEventQueue2 = "/dev/input/event" + str(maxEvent)
print("I'm a slow starter, please be patient!") # Caching actually ...
if pgc.DEVLOCAL == False:
print("I'm a slow starter, please be patient!") # Caching actually ...
try:
# Initialize
random.seed()
game = PongGame(pgv.bIsServer)
ball = PongObject(pgc.GAMEAREA_MAX_X/2, pgc.GAMEAREA_MAX_Y/2, 20, 20, 8.0, 2.5)
if player_index == 0: # Local or server?
if pgv.bIsServer == True: # Server version, no gamepads locally avail.
if player_index == 0: # Local or viewer or server?
if pgv.bIsServer == True: # Server & viewer version, no gamepads locally avail.
player1 = PongPlayer(None, True, 1, "", 10, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
player2 = PongPlayer(None, True, 2, "", pgc.GAMEAREA_MAX_X-40, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
player2 = PongPlayer(None, True, 2, "", pgc.GAMEAREA_MAX_X-40, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
else: # Local version (both gamepads assumed locally connected)
player1 = PongPlayer(sEventQueue1, False, 1, "", 10, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
player2 = PongPlayer(sEventQueue2, False, 2, "", pgc.GAMEAREA_MAX_X-40, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
elif player_index == 1: # Client/server version, player #1, only one queue (the last one!) avail. ...
player1 = PongPlayer(sEventQueue2, False, player_index, player_server, 10, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
if pgc.DEVLOCAL == True:
player1 = PongPlayer(sEventQueue1, False, player_index, player_server, 10, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
else:
player1 = PongPlayer(sEventQueue2, False, player_index, player_server, 10, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
player2 = PongPlayer(None, False, 2, "", pgc.GAMEAREA_MAX_X-40, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
elif player_index == 2: # Client/server version, player #2, only one queue (the last one!) avail. ...
player1 = PongPlayer(None, False, 1, "", 10, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
if DEVLOCAL == True: # 2 gamepads required FOR LOCAL DEVELOPMENT ONLY!
player2 = PongPlayer(sEventQueue2, False, player_index, player_server, pgc.GAMEAREA_MAX_X-40, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
else: # 2nd player assumed on 1st gamepad (only one pad required ... !)
player2 = PongPlayer(sEventQueue2, False, player_index, player_server, pgc.GAMEAREA_MAX_X-40, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
elif player_index == 2: # Client/server version, player #2, only one queue (the last one!) avail. anyway ...
player1 = PongPlayer(None, False, 1, "", 10, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
player2 = PongPlayer(sEventQueue2, False, player_index, player_server, pgc.GAMEAREA_MAX_X-40, pgc.GAMEAREA_MAX_Y/2-50, 20, 160)
# TODO: Pretty ugly patch for handler data access ... (better: separate sender class)
vs.init_send_structures(game, player1, player2, ball)
# 3 View clients permissible
if player_index > 0: # i.e. NOT local only
if (player_index > 0) or (pgv.bIsViewer): # i.e. NOT local only
viewserver = vs.ViewServer() # Create a view server
else: # Local
viewserver = None
except IOError as e:
import errno
@ -320,15 +121,25 @@ if __name__ == '__main__':
print("Device not found?")
else:
raise e
sys.exit(-1)
app = QApplication(sys.argv) # Eval command line args
sys.exit(-1)
window = pongWindow() # Create Qt5 GUI
rc = app.exec_() # & run logic
rc = 0
if pgv.bIsServer == False: # This is NOT the headless server!
from game_objects.pong_viewer import run_Qt5_viewer
rc = run_Qt5_viewer(debug_x, debug_y, viewserver, game, ball, player1, player2)
else: # Headless server-side game loop
while True:
try:
if game.pong_game(ball, player1, player2) == False:
break # Regular game exit
time.sleep(0.025) # Yield to other threads ...
except KeyboardInterrupt:
print("Server stopped by user.")
break
# Quit gracefully
player2.exit()
player1.exit()
player1.exit()
sys.exit(rc)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 126 KiB

@ -0,0 +1,2 @@
# View client only
./pandemic_pong.py --sizeable --viewer 127.0.0.1
Loading…
Cancel
Save