class Environment(): #in an MVC system , this would be a controller ''' The environment class contains the state of the game. The server has the master version, the clients have slave versions (updated through the network) ''' NEXT_PLAYER_ID=1 NEXT_BUILDING_ID=1 FPS=30 ATTACK_DISTANCE =3 BUILDING_DISTANCE =6 GAME_DURATION = 10#15 seconds #15 * 60 # 15 minutes def __init__(self): '''State: Players,Buildings, Time, Resourse Pool''' self.players = {} self.buildings = {} self.TimeLeft = 0 self.TrueTimeLeft = 0 self.Tick = 0 self.scores =[0,0] self.GameOver =False self.GameStarted =False self.width = 80.0 self.height = 48.0 self.view =None self.team =None self.actions =None self.IsServer = True self.ResourcePool = ResourcePool() #Helper Functions def createPlayer(self, player_id,team): '''add a player to the given team''' player = Player() player.team = team playerId = id(player) player.player_id = player_id #player.player_id = Environment.NEXT_PLAYER_ID #Environment.NEXT_PLAYER_ID = Environment.NEXT_PLAYER_ID + 1 self.players[playerId] = player return player def createBuilding(self, team,pos): '''add a building to the given team''' building = Building() building.team = team building.position =pos bid = id(building) building.building_id = Environment.NEXT_BUILDING_ID Environment.NEXT_BUILDING_ID = Environment.NEXT_BUILDING_ID + 1 self.buildings[bid] = building return building def StartGame(self): self.Tick=0 self.GameStarted=True self.GameOver = False self.TrueTimeLeft=Environment.GAME_DURATION self.TimeLeft = int(self.TrueTimeLeft) for playerId in self.players: self.players[playerId].sides=3 self.players[playerId].resources=0 self.buildings.clear() def updateTime(self): self.Tick += 1.0/Environment.FPS if(self.GameStarted): self.TrueTimeLeft-=1.0/Environment.FPS self.TimeLeft = int(self.TrueTimeLeft) if( self.TrueTimeLeft<=0): self.GameOver =True self.TrueTimeLeft =0 def updatePositions(self): for playerId in self.players: self.players[playerId].updatePosition( 1.0/Environment.FPS) def Update(self): startTime = time.time() self.updateTime() self.scores =self.calculateScores() self.updatePositions() if(self.actions<>None): self.processNewState() self.writeStateToServer() self.view.paint(self.Tick) for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() #sys.exit() if event.type == pygame.KEYDOWN: if event.key==pygame.K_s: self.StartGame() #print time.time()-startTime def processNewState(self): for action in self.actions: found= False for playerId in self.players: if(self.players[playerId].player_id)==int(action[0]): self.players[playerId].action=int(action[2]) pos = action[3].split(',') self.players[playerId].targetPosition = Vector2D(float(pos[0]),float(pos[1])) found =True break if(not found): self.createPlayer(int(action[0]),int(action[1])) for playerId in self.players: player = self.players[playerId] if(player.action == Player.ATTACK): #ATTACK self.handleAttack(player) elif(player.action == Player.BUILD): #building self.handleBuild(player) elif(player.action == Player.UPGRADE): #building self.handleUpgrade(player) elif(player.action == Player.SCAN): #building self.handleScan(player) elif(player.action == Player.IDLE): self.handleIdle(player) for b in self.buildings.itervalues(): if (b.getPosition() - player.getPosition()).length < b.size and b.isTrap() and b.team<>player.team: b.explode(player,self.Tick) def handleAttack(self,player): if(player.sides>=3): player.performAttack(self.Tick) for p in self.players.itervalues(): if (p.team != player.team) and (p.getPosition() - player.getPosition()).length < Environment.ATTACK_DISTANCE: p.hit(self.Tick) for b in self.buildings.itervalues(): if (b.team != player.team) and (b.getPosition() - player.getPosition()).length < Environment.ATTACK_DISTANCE: b.hit(self.Tick) def handleBuild(self,player): ACTION = "BUILD" if((self.ResourcePool.getPosition()-player.getPosition()).length< self.ResourcePool.size): ACTION ="MINE" else: for b in self.buildings.itervalues(): if(b.team == player.team and b.isPolyFactory() and b.resources == 5 and (b.getPosition()- player.getPosition()).length <b.size): ACTION ="MINE" break if( ACTION =="MINE"): player.performBuild(self.Tick) player.mine(self.Tick) else: if(player.resources>0): BUILDING =None for b in self.buildings.itervalues(): if (b.getPosition() - player.getPosition()).length < b.size: BUILDING =b break if BUILDING ==None : self.createBuilding( player.team, player.getPosition()) player.resources-=1 elif BUILDING.team ==player.team: player.performBuild(self.Tick) BUILDING.build(player,self.Tick) def handleUpgrade(self,player): allowedUpgradeLoc = False if((self.ResourcePool.getPosition()-player.getPosition()).length< self.ResourcePool.size): allowedUpgradeLoc=True else: for b in self.buildings.itervalues(): if(b.team == player.team and b.isPolyFactory() and b.resources == 5 and (b.getPosition()- player.getPosition()).length <b.size): allowedUpgradeLoc=True break if(allowedUpgradeLoc): player.upgrade(self.Tick) def handleScan(self,player): player.scan(self.Tick) def handleIdle(self,player): pass def start(self): '''controls the environment by initiating the looping calls''' self.TrueTimeLeft=Environment.GAME_DURATION self.TimeLeft = int(self.TrueTimeLeft) self.view.start('Server') if os.path.exists(SERVERDATA): os.remove(SERVERDATA) self._renderCall = LoopingCall(self.Update) self._renderCall.start(1.0/Environment.FPS) self._readCall = LoopingCall( self.readStateFromServer) self._readCall.start(1.0/Environment.FPS) def calculateScores(self): score=[0,0] for team in range(1,3): for playerId in self.players: player = self.players[playerId] if player.team == team: score[team-1] += player.sides score[team-1] += player.resources for buildingId in self.buildings: building = self.buildings[buildingId] if building.team == team: score[team-1] += building.sides score[team-1] += building.resources score[team-1] *= 1000 return score ; #FUNCTIONS FOR NETWORKING def writeStateToServer(self): string =self.Serialize() serv_db = shelve.open(SERVERDATA) try: serv_db['data']= { 'time': str(time.time()), 'string': string } finally: serv_db.close() for p in self.players: if( len(self.players[p].animations) >10): self.players[p].animations = self.players[p].animations[1:] def readStateFromServer(self): client_db = shelve.open(CLIENTDATA) try: self.actions =[] for key in client_db: self.actions.append(client_db[key]['string'].split('$')) finally: client_db.close() def TransmitStateUDP(self): for c in self.clients.itervalues(): sock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) # UDP sock.sendto( MESSAGE, (c[0], c[1]) ) def ReceiveStatesUDP(self): for c in self.clients.itervalues(): sock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) # UDP sock.sendto( MESSAGE, (c[0], c[1]) ) def cSerialize(self): #deprecated s=pickle.dumps(self.players)+'$'+pickle.dumps(self.buildings)+'$'+\ pickle.dumps(self.ResourcePool)+'$'+pickle.dumps(self.scores)+'$'+str(self.TimeLeft)+'$'+str(self.Tick)+'$'+str(self.GameOver) #print len(s),s return s def Serialize(self): s='' for p in self.players.itervalues(): s+=str(p.player_id)+'&' s+=str(p.team)+'&' s+=str(p.sides)+'&' s+=str(p.resources)+'&' s+=str(p.partialResources)+'&' s+=str(p.targetPosition.x)+'^'+str(p.targetPosition.y)+'&' for a in p.animations: s+=str(a[0])+'#'+str(a[1])+'#'+str(a[2])+'^' s+='&' s+=str(p.action)+'@' s+='$' for b in self.buildings.itervalues(): s+=str(b.building_id)+'&' s+=str(b.team)+'&' s+=str(b.sides)+'&' s+=str(b.resources)+'&' s+=str(b.partialResources)+'&' s+=str(b.position.x)+'^'+str(b.position.y)+'&' for a in b.animations: s+=str(a[0])+'#'+str(a[1])+'#'+str(a[2])+'^' s+='@' s+='$' s+=str(self.scores[0])+'^'+str(self.scores[1])+'$' s+=str(self.TimeLeft)+'$' s+=str(self.GameOver) return s
class Environment(LoopingThread): #in an MVC system , this would be a controller ''' The environment class contains the state of the game. The server has the master version, the clients have slave versions (updated through the network) ''' ATTACK_RADIUS = 3 SCAN_RADIUS = 3 FPS=30 def __init__(self,player_id,team,serverIP,serverPort): '''State: Players,Buildings, Time, Resourse Pool''' LoopingThread.__init__(self) self.players = {} self.buildings = {} self.TimeLeft = 15*60 self.width = 80.0 self.height = 48.0 self.view =None self.GameOver =False self.playerID =player_id self.player = None self.action = 0 self.attemptedAction = 0 self.lastAction = 0 self.ActionTimeout = 1 self.team =team self.otherTeam = 2 if self.team==1 else 1 self.scores =[0,0] self.IsServer = False self.ResourcePool = ResourcePool() self.client = UDPClient(self) self.serverIP =serverIP self.serverPort = serverPort self.Tick = 0 self.Position = (0,0) self.lastUpdate = 0 self.controller = None def createPlayer(self, player_id,team): '''add a player to the given team''' player = Player() player.team = team playerId = id(player) player.player_id = player_id self.players[playerId] = player return player def createBuilding(self, team,pos): '''add a building to the given team''' building = Building() building.team = team building.position =pos bid = id(building) building.building_id = 0 self.buildings[bid] = building return building def readGestures(self): self.controller._handleInput() if(self.player<>None): #print self.player.action if(self.player.action == Player.ATTACK): #ATTACK self.handleAttack() elif(self.player.action == Player.BUILD): #building self.handleBuild() elif(self.player.action == Player.UPGRADE): #building self.handleUpgrade() elif(self.player.action == Player.SCAN): #building self.handleScan() elif(self.player.action == Player.IDLE): self.handleIdle() def handleAttack(self): if(self.player.sides>=3): self.player.performAttack(self.Tick) def handleBuild(self): ACTION = "BUILD" if((self.ResourcePool.getPosition()-self.player.getPosition()).length< self.ResourcePool.size): ACTION ="MINE" else: for b in self.buildings.itervalues(): if(b.team == self.player.team and b.isPolyFactory() and b.resources == 5 and (b.getPosition()- player.getPosition()).length <b.size): ACTION ="MINE" break if( ACTION =="MINE"): self.player.performBuild(self.Tick) else: if(self.player.resources>0): BUILDING =None for b in self.buildings.itervalues(): if (b.getPosition() - self.player.getPosition()).length < b.size: BUILDING =b break if BUILDING.team ==self.player.team: self.player.performBuild(self.Tick) def handleUpgrade(self): allowedUpgradeLoc = False if((self.ResourcePool.getPosition()-self.player.getPosition()).length< self.ResourcePool.size): allowedUpgradeLoc=True else: for b in self.buildings.itervalues(): if(b.team == self.player.team and b.isPolyFactory() and b.resources == 5 and (b.getPosition()- self.player.getPosition()).length <b.size): allowedUpgradeLoc=True break if(allowedUpgradeLoc): self.player.upgrade(self.Tick) def handleScan(self): self.player.scan(self.Tick) def handleIdle(self): pass def updateTime(self): self.Tick+= 1.0/Environment.FPS #if( self.TimeLeft<=0): # self.GameOver =True def task(self): #self.deSerialize() self.readGestures() self.updateTime() self.updatePositions() self.makeRequest() self.view.paint(self.Tick ) for event in pygame.event.get(): if event.type == pygame.QUIT: self.Exit() def Exit(self): self.shutdown() pygame.quit() def makeRequest(self): #print self.action self.client.MakeRequest(self.playerID,self.team,self.action,self.Position) self.action = 0 def updatePositions(self): for playerId in self.players: if self.players[playerId].player_id <> self.playerID: self.players[playerId].updatePosition( 1.0/Environment.FPS) def start(self): '''controls the environment by initiating the looping calls''' self.lastUpdate =time.time() self.view.start('client-'+str(self.playerID)) self.client.start(self.serverIP,int(self.serverPort),self.playerID) #if os.path.exists(CLIENTLOCALDATA.split('.')[0]+str(self.playerID)+'.'+CLIENTLOCALDATA.split('.')[1]): # os.remove(CLIENTLOCALDATA.split('.')[0]+str(self.playerID)+'.'+CLIENTLOCALDATA.split('.')[1]) self.setInterval(1.0/Environment.FPS) self.run() #self._renderCall = LoopingCall(self.Update) #self._requestCall = LoopingCall(self.makeRequest) #self._renderCall.start(1.0/Environment.FPS) #self._requestCall.start(1.0/Environment.FPS) #FUNCTIONS FOR NETWORKING def deSerialize(self,state): try: ''' assume the following message structure players$buildings$resourcepool$scores$timeleft$gameover players->player1@player2@..@playern player1->id&Team&sides&resources&partialResources&pos_x^pos_y&anim1^anim2^anim3&action ''' if(state<>None): t = state.split('$') if(len(t)>0): players = t[0].split('@') #update players players.remove('') for p in players: found =False pkey = 0 player = p.split('&') pId = int(player[0]) pTeam = int (player[1]) pSides = int(player[2]) pResources = int(player[3]) pPartialResources = int(player[4]) pPosition = Vector2D( (float(player[5].split('^')[0]),(float(player[5].split('^')[1])))) AnimationList = player[6].split('^') AnimationList.remove('') pAnimations=[] for a in AnimationList: a = a.split('#') pAnimations.append( (int(a[0]),bool(a[1]),float(a[2]))) pAction = int(player[7]) for ep in self.players.itervalues(): if ep.player_id == pId: found = True pkey = id(ep) break if found: self.players[pkey].sides = pSides self.players[pkey].resources = pResources self.players[pkey].partialResources = pPartialResources if pId == self.playerID: self.players[pkey].position = Vector2D(self.Position) self.players[pkey].action = self.action else: self.players[pkey].animations.extend(pAnimations) self.players[pkey].targetPosition = pPosition self.players[pkey].action = pAction else: newplayer = self.createPlayer(pId,pTeam) if(pId == self.playerID): self.player=newplayer newplayer.targetPosition=pPosition newplayer.sides = pSides newplayer.resources = pResources newplayer.partialResources = pPartialResources newplayer.animations.extend(pAnimations) newplayer.action = pAction ''' assume the following message structure players$buildings$resourcepool$scores$timeleft$gameover buildings->buildings@buildings@..@buildings buildings->id&Team&sides&resources&partialResources&pos_x^pos_y&anim1^anim2^anim3 ''' if(t[1]<>''): buildings = t[1].split('@') self.buildings.clear() for b in buildings: building = b.split('&') bId = int(building[0]) bTeam = int(building[1]) bSides = int(building[2]) bResources = int(building[3]) bPartialResources = int(building[4]) AnimationList = building[6].split('^') AnimationList.remove('') bAnimations=[] for a in AnimationList: a = a.split('#') bAnimations.append( (int(a[0]),bool(a[1]),float(a[2]))) bPosition = Vector2D( (float(building[5].split('^')[0]),(float(building[5].split('^')[1])))) newbuilding = self.createBuilding(bTeam,bPosition) newbuilding.building_id = bId newbuilding.sides = bSides newbuilding.resources = bResources newbuilding.animations.extend(bAnimations) if(t[2]<>''): self.scores =(int(t[2].split('^')[0]),int(t[2].split('^')[1])) if(t[3]<>''): self.TimeLeft =int(t[3]) if(t[4]<>''): self.GameOver = not bool(t[4]) except: print "Unexpected error:", sys.exc_info()[0]