#!/usr/bin/env python3
# Filename: A08_multiplayer_demo_server.py
import sys, os
import pygame
import socket
import platform, subprocess
# PyGame Constants
from pygame.locals import (
K_ESCAPE,
K_c, K_1, K_2, K_3,
K_a, K_s, K_d, K_w,
K_j, K_k, K_l, K_i, K_SPACE,
K_LSHIFT, K_t, K_b, K_y
)
from pygame.color import THECOLORS
from A08_network import GameServer, RunningAvg, setClientColors
#=====================================================================
# Classes
#=====================================================================
class Client:
def __init__(self, cursor_color, ID):
self.cursor_color = cursor_color
self.ID = ID
self.mouseXY = (0,0) # (x_px, y_px)
#self.mouse_button = 1 # 1, 2, or 3
self.mouseB1 = "U"
self.active = False
self.key_w = "U"
self.key_a = "U"
self.key_s = "U"
self.key_d = "U"
self.historyXY = []
self.sendCount = 0
def draw_cursor(self, color, edge_px=0):
# An edge_px of 0 indicates the polygon will be filled.
outline_vertices = []
outline_vertices.append( self.mouseXY )
outline_vertices.append( (self.mouseXY[0] + 10, self.mouseXY[1] + 10) )
outline_vertices.append( (self.mouseXY[0] + 0, self.mouseXY[1] + 14) )
pygame.draw.polygon( server_display, color, outline_vertices, edge_px)
def draw_fancy_cursor(self):
# First, a filled polygon.
self.draw_cursor( color=self.cursor_color)
# The edge_px of 1 indicate no filling and a thin black outline.
self.draw_cursor( color=THECOLORS["black"], edge_px=1)
def draw_cursor_history(self):
for mouse_xy in self.historyXY:
pygame.draw.circle( server_display, self.cursor_color, mouse_xy, 10, 0)
def render_keys(self):
y_offset = 10 + (self.ID - 0) * 37
# The W row...
pygame.draw.rect(server_display, self.cursor_color, pygame.Rect(10, y_offset, 30, 20))
pygame.draw.rect(server_display, THECOLORS["black"], pygame.Rect(19, y_offset, 12, 20))
txt_string = self.key_w
txt_surface = font_keys.render(txt_string, 1, THECOLORS["yellow"])
server_display.blit( txt_surface, [20, y_offset])
# The ASD row...
pygame.draw.rect(server_display, THECOLORS["black"], pygame.Rect(10, y_offset + 20, 30, 20))
txt_string = self.key_a + self.key_s + self.key_d
txt_surface = font_keys.render(txt_string, 1, THECOLORS["yellow"])
server_display.blit( txt_surface, [11, y_offset + 20])
#=======================================================================
# Functions
#=======================================================================
def checkForLocalUserInput():
global background_color
local_user = clients['local']
# Get all the events since the last call to get().
for event in pygame.event.get():
if (event.type == pygame.QUIT):
sys.exit()
elif (event.type == pygame.KEYDOWN):
if (event.key == K_ESCAPE):
sys.exit()
elif (event.key==K_c):
background_color = THECOLORS["cyan"]
elif (event.key==K_b):
background_color = THECOLORS["blue"]
elif (event.key==K_y):
background_color = THECOLORS["yellow"]
elif (event.key==K_a):
local_user.key_a = 'D'
elif (event.key==K_s):
local_user.key_s = 'D'
elif (event.key==K_d):
local_user.key_d = 'D'
elif (event.key==K_w):
local_user.key_w = 'D'
elif (event.type == pygame.KEYUP):
if (event.key==K_a):
local_user.key_a = 'U'
elif (event.key==K_s):
local_user.key_s = 'U'
elif (event.key==K_d):
local_user.key_d = 'U'
elif (event.key==K_w):
local_user.key_w = 'U'
elif event.type == pygame.MOUSEBUTTONDOWN:
local_user.mouseB1 = 'D'
elif event.type == pygame.MOUSEBUTTONUP:
local_user.mouseB1 = 'U'
# cursor x,y
local_user.mouseXY = pygame.mouse.get_pos()
def custom_update_example(self, client_name, state_dict):
# Store current client state
self.CS_data[client_name].mouseXY = state_dict['mouseXY']
self.CS_data[ client_name].key_a = state_dict['a']
self.CS_data[ client_name].key_s = state_dict['s']
self.CS_data[ client_name].key_d = state_dict['d']
self.CS_data[ client_name].key_w = state_dict['w']
# Add to drawing history if client is drawing
if state_dict['mouseB1'] == 'D':
self.CS_data[client_name].historyXY.append(state_dict['mouseXY'])
# Maintain fixed history length
if len(self.CS_data[client_name].historyXY) > 200:
self.CS_data[client_name].historyXY.pop(0)
self.CS_data[client_name].sendCount += 1
if self.CS_data[client_name].sendCount > 100:
print(f"{client_name} at {state_dict['mouseXY']}")
self.CS_data[client_name].sendCount = 0
def signInOut_function(self, client_name, activate=True):
if activate:
self.CS_data[client_name].active = True
else:
self.CS_data[client_name].active = False
self.CS_data[client_name].historyXY = []
#=======================================================================
# Main Program
#=======================================================================
def main():
global clients, client_colors, server_display, font_keys, background_color
pygame.init()
server_display = pygame.display.set_mode((600,400))
pygame.display.set_caption("SERVER: render state of network clients")
# Hide the system cursor since we're drawing our own
pygame.mouse.set_visible(False)
# Instantiate clock to help control the framerate.
server_clock = pygame.time.Clock()
framerate_limit = 120
background_color = THECOLORS["yellow"]
# Font object for rendering text onto display surface.
font_keys = pygame.font.SysFont("Arial", 16)
# For displaying a smoothed framerate.
fr_avg = RunningAvg(100, pygame)
client_colors = setClientColors()
# Initialize the network clients, a dictionary of client objects.
clients = {}
for m in range(1,11):
c_name = 'C' + str(m)
clients[ c_name] = Client( client_colors[ c_name], m)
# Add a local (non-network) client to the client dictionary.
clients['local'] = Client( THECOLORS["green"], 0)
clients['local'].active = True
# Setup network server.
if platform.system() == 'Linux':
local_ip = subprocess.check_output(["hostname", "-I"]).decode().strip()
else:
local_ip = socket.gethostbyname(socket.gethostname())
print("Server IP address:", local_ip)
server = GameServer(host='0.0.0.0', port=5000,
update_function=custom_update_example, clientStates=clients,
signInOut_function=signInOut_function)
while True:
dt_s = server_clock.tick(framerate_limit) * 1e-3
if server.running:
server.accept_clients()
checkForLocalUserInput()
if clients['local'].mouseB1 == 'D':
clients['local'].historyXY.append(clients['local'].mouseXY)
# Maintain fixed history length
if len(clients['local'].historyXY) > 200:
clients['local'].historyXY.pop(0)
server_display.fill( background_color)
fr_avg.update( server_clock.get_fps())
fr_avg.draw( server_display, 550, 9)
for clientName in clients:
client = clients[ clientName]
if client.active:
client.render_keys()
client.draw_cursor_history()
client.draw_fancy_cursor()
pygame.display.flip()
if __name__ == "__main__":
main()