# Filename: A08_multiplayer_demo_server.py # Written by: James D. Miller import sys, os import pygame # PyGame Constants from pygame.locals import * from pygame.color import THECOLORS from PodSixNet.Server import Server from PodSixNet.Channel import Channel import socket #======================================================================= # Classes #======================================================================= class ClientChannel(Channel): def __init__(self, *args, **kwargs): Channel.__init__(self, *args, **kwargs) def Network_CN(self, data): global CND # This function runs when "CN" data is recieved from a client. I believe this # runs once for each data "Send" by each client. # The naming of this function must correspond with the value of the 'action' key # sent in the data dictionary from the client. All the clients are using # an action key value of "CN", so this function must end with "CN". # "data" is a dictionary of state info sent by each client. # The "ID" value, in the dictionary, identifies who (which client) # this dictionary is coming from. clientname = 'C' + str(data['ID']) # Update the client state in the CN_data dictionary based on the incoming data. CND.CN_data[clientname]['state'] = data # Keep track of client activity. CND.CN_data[clientname]['sendCount'] += 1 # If the left mouse button was down, append the mouse location tuple to the FIFO list. if data['mouseB1'] == 'D': CND.CN_data[clientname]['historyXY'].append(data['mouseXY']) # Displace the oldest part of the FIFO; pop it off. if len(CND.CN_data[clientname]['historyXY']) > 200: CND.CN_data[clientname]['historyXY'].pop(0) # You should be reluctant to print here. Printing can slow the server frame rate. It's # tempting to try and observe this data flow through printing, but... def Close(self): print "deleting player" class GameServer(Server): channelClass = ClientChannel def __init__(self, *args, **kwargs): Server.__init__(self, *args, **kwargs) self.client_count = 0 # This runs when each client connects. def Connected(self, channel, addr): print 'new connection (channel, addr):', channel, addr self.client_count += 1 if (self.client_count <= 10): channel.Send({"action": "hello", "P_ID":self.client_count}) else: channel.Send({"action": "hello", "P_ID":0}) class ClientData: def __init__(self): # CN_data is a dictionary of dictionaries. self.loopsSinceLastQuietCheck = 0 self.CN_data = {} for m in range(1,11): # Set the player's Cm key value to a dictionary. self.CN_data['C' + str(m)] = {'state':None, 'sendCount':0, 'previousSendCount':0, 'active':False, 'historyXY':[]} def checkForQuietClients(self): self.loopsSinceLastQuietCheck += 1 if self.loopsSinceLastQuietCheck > 20: self.loopsSinceLastQuietCheck = 0 for clientname in CND.CN_data: # Check for the no change case (client is quiet). countChange = CND.CN_data[clientname]['sendCount'] - CND.CN_data[clientname]['previousSendCount'] if countChange == 0: CND.CN_data[clientname]['active'] = False else: CND.CN_data[clientname]['active'] = True # Update the previous value for use in the next comparison. CND.CN_data[clientname]['previousSendCount'] = CND.CN_data[clientname]['sendCount'] #======================================================================= # Functions. #======================================================================= def checkforLocalUserInput(): # 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): server_display.fill(THECOLORS["blue"]) def draw_cursor_histories( CN_data): for player in CN_data: if CND.CN_data[player]['active']: draw_cursor_history( CN_data[player]['historyXY'], player_colors[player]) def draw_cursor_history( cursor_history, color): for mouse_xy in cursor_history: pygame.draw.circle(server_display, color, mouse_xy, 10, 0) def draw_server_cursors( CN_data): for player in CN_data: if CND.CN_data[player]['active']: draw_fancy_server_cursor(CN_data[player]['state']['mouseXY'], player_colors[player]) def draw_fancy_server_cursor( cursor_position, color): draw_server_cursor( cursor_position, color, 0) draw_server_cursor( cursor_position, THECOLORS["black"], 1) def draw_server_cursor( cursor_position, color, edge_px): cursor_outline_vertices = [] cursor_outline_vertices.append( cursor_position ) cursor_outline_vertices.append( (cursor_position[0] + 10, cursor_position[1] + 10) ) cursor_outline_vertices.append( (cursor_position[0] + 0, cursor_position[1] + 14) ) pygame.draw.polygon(server_display, color, cursor_outline_vertices, edge_px) def render_all_player_keys( CN_data): for player in CN_data: if CND.CN_data[player]['active']: render_keys( CN_data[player]['state']) def render_keys( user_state): y_offset = 10 + (user_state['ID'] - 1) * 37 # The W row... pygame.draw.rect(server_display, THECOLORS["black"], pygame.Rect(20, y_offset , 10, 20)) txt_string = user_state['w'] txt_surface = fnt.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 = user_state['a'] + user_state['s'] + user_state['d'] txt_surface = fnt.render(txt_string, 1, THECOLORS["yellow"]) server_display.blit(txt_surface, [10, y_offset + 20]) #======================================================================= # Main Program statements #======================================================================= pygame.init() server_display = pygame.display.set_mode((600,400)) pygame.display.set_caption("Server: Rendering Window.") # Instantiate clock to help control the framerate. server_clock = pygame.time.Clock() framerate_limit = 100 server_display.fill(THECOLORS["yellow"]) # Font object for rendering text onto display surface. fnt = pygame.font.SysFont("Arial", 14) # Setup network server. local_ip = socket.gethostbyname(socket.gethostname()) local_port = 4330 print "Server IP address and port:", local_ip, local_port drawBoard_server = GameServer(localaddr=(local_ip, local_port)) # Initialize the client states and mouse histories. CND = ClientData() # Colors player_colors = {'C1': THECOLORS["green"],'C2': THECOLORS["tan"],'C3': THECOLORS["black"],'C4': THECOLORS["blue"],'C5': THECOLORS["pink"], 'C6': THECOLORS["red"],'C7': THECOLORS["coral"],'C8': THECOLORS["orangered1"],'C9': THECOLORS["grey80"],'C10': THECOLORS["rosybrown3"]} # Set a limit for the quietness demerits. qCount_limit = 100 while True: server_display.fill(THECOLORS["yellow"]) # Background for text pygame.draw.rect(server_display, THECOLORS["black"], pygame.Rect(550, 9, 60, 20)) # Text fps_string = "%.0f" % server_clock.get_fps() txt_surface = fnt.render(fps_string, True, THECOLORS["white"]) server_display.blit(txt_surface, [550, 10]) drawBoard_server.Pump() dt_s = float(server_clock.tick(framerate_limit) * 1e-3) CND.checkForQuietClients() checkforLocalUserInput() draw_cursor_histories( CND.CN_data) draw_server_cursors( CND.CN_data) render_all_player_keys( CND.CN_data) pygame.display.flip()