#CMPS 128 - Project Phase 3 #Josep Valls - 6/8/2008 #jvallsva - 1139975 #Term project, Multiplayer Pong import socket import struct import time,datetime import sys,os import random import threading,thread import math import Tkinter #Constants magicNumberP=0x1f2e3d4c magicNumberB=0x2f3e4d5c players=[] playerThreads=[] playerThread=0 numPlayers=8 numBalls=8 epidemicPropagation=2 updateRate=0.01 #Classes to keep track of the world data class pServer: "Server info and player information" def __init__(self): self.ip="" self.port=0 self.secs=0 self.msecs=0 self.speed=0 self.position=0 self.playerid=0 self.score=0 def guessPosition(self): timetime=time.time() secs=int(timetime) msecs=int((timetime-secs)*1000000) position=self.position+int(self.speed*((secs+msecs/10000000)-(self.secs+self.msecs/10000000))) if position<0: position=0 elif position>9*10**6: position=9*10**6 #position=position%9*10**6 return position def trace(self): print self.log() def log(self): return self.playerid, self.ip, self.port, self.secs, self.msecs, self.speed, self.position, self.score def pack(self): return struct.pack('!4sIIIII',socket.inet_aton(self.ip),self.port,self.secs,self.msecs,int(self.speed),int(self.position)) class pPlayer(pServer): "Current server and player information" def __init__(self): pServer.__init__(self) self.speed=int((random.random()*10-5)*10**6) #meters per second ranging -5 to 5 *10**6 self.position=int(4.5*10**6) #ranging from 0 to (10-1) *10**6 def courtCheck(self): #if the paddle reaches the end of its wall, its speed is set to zero. #If the paddle's speed is currently zero and it's at one end of its wall, it always changes speed in the appropriate direction (away from the wall) at a random speed of 0-5 meters per second. if self.speed<0 and self.position+self.speed*updateRate<0: self.speed=int((random.random()*5)*10**6) return True elif self.speed>0 and self.position+self.speed*updateRate>(10-1)*10**6: self.speed=int((random.random()*5)*-10**6) return True else: return False def guessPosition(self): #No need to guess! return self.position def pack(self): return pServer.pack(self) class pWorld(threading.Thread): "World info" def __init__(self): global players global playerThread self.servers=[] self.logfilename="log.temp.txt" self.thread=playerThread self.thisServer=playerThread playerThread+=1 self.calcPoly() #Server binding self.ssocket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) self.ssocket.settimeout(0.1) self.ssocket.bind(('127.0.0.1',0)) print "Server started at:",self.ssocket.getsockname(),self.thread,len(players) for index,i in enumerate(players): self.addServer(index,i[0],i[1]) self.addSelf(self.thread,self.ssocket.getsockname()) players.append(self.ssocket.getsockname()) global nunBalls self.balls=[] for i in xrange(numBalls): #if self.thread==0: self.balls.append(pAutoBall(i)) #else: self.balls.append(pBall(i)) self.balls.append(pBall(i,self.c[0],self.c[1])) threading.Thread.__init__ (self) def calcPoly(self): global numPlayers self.poly=[] angle=3.14159265/2-3.14159265/numPlayers radius=5*10**6/math.sin(3.14159265/numPlayers) c=(math.cos(angle)*radius,math.sin(angle)*radius) self.c=c angleInc=2*3.14159265/numPlayers angle=-90*3.14159265/180-angleInc/2 for k in xrange(numPlayers): self.poly.append((radius*math.cos(angle)+c[0],radius*math.sin(angle)+c[1])) angle+=angleInc def addSelf(self,playerid,rsocket): newServer=pPlayer() newServer.playerid=playerid newServer.ip=rsocket[0] newServer.port=int(rsocket[1]) self.servers.append(newServer) self.paddle=newServer def addServer(self,playerid,ip,port,secs=0,msecs=0,speed=0,position=0): newServer=pServer() newServer.playerid=playerid newServer.ip=ip newServer.port=int(port) newServer.secs=secs newServer.msecs=msecs newServer.speed=speed newServer.position=position self.servers.append(newServer) def numServers(self): return len(self.servers) def updateServer(self,serverid,playerid,ip,port,secs,msecs,speed,position): for i in self.servers: if i.ip==ip and i.port==port: if (i.secs+i.msecs/1000000)<(secs+msecs/1000000): i.secs=secs i.msecs=msecs i.speed=speed i.position=position break else: if serverid==playerid: self.addServer(playerid,ip,port,secs,msecs,speed,position) def log(self): return [i.log() for i in self.servers] def trace(self,msg=''): if self.thread==0: self.updatePosition() if not msg=='': #print msg pass else: os.system("cls") print "Current view of the world:" print "This thread is", self.thread, self.thread print "This server is", self.thisServer for index,i in enumerate(self.servers): print index, i.trace() def trace2(self): while True: time.sleep(0.5) self.trace() def packPaddles(self): return struct.pack('!IHH',magicNumberP, self.numServers(),self.thisServer)+"".join([i.pack() for i in self.servers]) def packBalls(self): return struct.pack('!IHH',magicNumberB, self.numServers(),self.thisServer)+"".join([i.pack() for i in self.servers]) def receivePackets(self): #Receive any pending incoming packets (there might be more than one!) #Update its own view of the state of the world based on the information received. #Note that a server may already have more current knowledge of another server's state than that received in a packet. while 1: try: req,(addr,port)=self.ssocket.recvfrom(1024) except: #print "No incoming packets" break m=struct.unpack('!I',req[0:4]) if(m[0]==magicNumberP): #Got a paddle update message m=struct.unpack('!HH',req[4:8]) if not(len(req)==8+24*m[0]): print "Packet length mismatch" else: n=struct.unpack('!'+'4sIIIII'*m[0],req[8:8+24*m[0]]) for i in range(0,len(n),6): self.updateServer(m[1],i/6,socket.inet_ntoa(n[i]),n[i+1],n[i+2],n[i+3],n[i+4],n[i+5]) elif(m[0]==magicNumberB): #Got a ball update message pass else: print "Magic number mismatch", "%#x" % magicNumber, "%#x" % m[0] def sendPackets(self): selectt=range(0,len(self.servers)) selectt.pop(self.thisServer) if epidemicPropagation0.7: #or change speed by a randomly chosen number between plus 5 and -5 meters per second. nspeed=self.paddle.speed+(random.random()*10-5)*10**6 #meters per second ranging -5 to 5 *10**6 #Obviously, if speed switches sign, the paddle reverses direction. The paddle cannot exceed 10 m/s in either direction if abs(nspeed)>10*10**6: if nspeed>0: self.paddle.speed=int(10*10**6) else: self.paddle.speed=int(-10*10**6) else: self.paddle.speed=int(nspeed) #Select k random servers (other than itself), and send its current view of the state of the world to those servers. self.sendPackets() def updatePosition(self): while True: timetime=time.time() time.sleep(updateRate) if self.paddle.courtCheck(): self.sendPackets() self.paddle.position+=int(self.paddle.speed*updateRate) self.paddle.secs=int(timetime) self.paddle.msecs=int((timetime-self.paddle.secs)*1000000) def updatePositionBalls(self): while True: time.sleep(updateRate) for i in self.balls: if not i.courtCheck(self.poly): if i.vx<0: i.vx=random.random()*5*10**6 else: i.vx=random.random()*-5*10**6 if i.vy<0: i.vy=random.random()*5*10**6 else: i.vy=random.random()*-5*10**6 #i.vx*=-1 #i.vy*=-1 i.x+=i.vx*updateRate i.y+=i.vy*updateRate def run(self): #Main server loop thread.start_new_thread(self.makeDecisions,()) thread.start_new_thread(self.updatePosition,()) if self.thread==0: thread.start_new_thread(self.updatePositionBalls,()) thread.start_new_thread(self.trace2,()) while True: #Receive any pending incoming packets (there might be more than one!) and update its own view of the state of the world based on the information received. Note that a server may already have more current knowledge of another server's state than that received in a packet. self.receivePackets() def __del__(self): #Cleanup self.ssocket.close() class pBall(): "Ball information" def __init__(self,id,x,y): self.id=id self.x=x self.y=y self.vx=(random.random()*10-5)*10**6 #meters per second ranging -5 to 5 *10**6 self.vy=(random.random()*10-5)*10**6 #meters per second ranging -5 to 5 *10**6 def trace(self): print self.id,self.x,self.y,self.vx,self.vy def pack(self): # Ball number (4 bytes) # Ball velocity (x,y) (8 bytes) # Ball position (x,y) (8 bytes) return struct.pack('!IIIII',self.id,self.x,self.y,self.vx,self.vy) def courtCheck(self,poly): x=self.x y=self.y n=len(poly) inside =False p1x,p1y = poly[0] for i in range(n+1): p2x,p2y = poly[i % n] if y > min(p1y,p2y): if y <= max(p1y,p2y): if x <= max(p1x,p2x): if p1y != p2y: xinters = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x if p1x == p2x or x <= xinters: inside = not inside p1x,p1y = p2x,p2y return inside def updatePosition(self): while True: time.sleep(updateRate) self.courtCheck() self.x+=self.vx*updateRate self.y+=self.vy*updateRate #if i%500==0: print self.id,self.x,self.y class pGui(threading.Thread): global playerThreads global canvas def __init__(self): self.cx=300 self.cy=300 self.radius=200 self.pfaces=len(playerThreads) self.angleInc=2*3.14159265/self.pfaces self.angleStart=90*3.14159265/180+self.angleInc/2 self.psize=10 self.bsize=6 self.pwidth=3 self.traceColor='gray20' self.pcolors=['red','blue','green','yellow','magenta','cyan','orange','purple'] if len(playerThreads)>len(self.pcolors): for i in range(len(playerThreads)-len(self.pcolors)+1): self.pcolors.append("#%02x" % (int(random.random()*0xFFFFFF))) self.w0=(self.radius * math.cos(self.angleStart)+self.cx,self.radius * math.sin(self.angleStart)+self.cy) self.w1=abs(self.radius * math.cos(self.angleStart)*2/10) #,,self.radius * math.cos(angle)+self.cx,self.radius * math.sin(angle)+self.cy, fill = self.pcolors[k], tag = 'border' threading.Thread.__init__ (self) def poly(self): angle = self.angleStart for k in xrange(self.pfaces): canvas.create_line(self.cx,self.cy,self.radius * math.cos(angle)+self.cx,self.radius * math.sin(angle)+self.cy, fill = self.traceColor, tag = 'radius') canvas.create_line(self.radius * math.cos(angle-self.angleInc)+self.cx,self.radius * math.sin(angle-self.angleInc)+self.cy,self.radius * math.cos(angle)+self.cx,self.radius * math.sin(angle)+self.cy, fill = self.pcolors[k], tag = 'border') angle-=self.angleInc def paddle(self,n,pos): angle = self.angleStart angle -= self.angleInc*n ax=self.radius * math.cos(angle-self.angleInc)+self.cx ay=self.radius * math.sin(angle-self.angleInc)+self.cy bx=self.radius * math.cos(angle)+self.cx by=self.radius * math.sin(angle)+self.cy canvas.create_line( ax+pos*(bx-ax)/100, ay+pos*(by-ay)/100, ax+(pos+self.psize)*(bx-ax)/100, ay+(pos+self.psize)*(by-ay)/100, width=self.pwidth, fill = self.pcolors[n], tag = 'paddle') def ball(self,cx,cy): canvas.create_oval(self.ccx(cx)-self.bsize/2, self.ccy(cy)-self.bsize/2, self.ccx(cx)+self.bsize/2, self.ccy(cy)+self.bsize/2, fill = 'white', tag = 'ball') def label(self,cx,cy,msg=''): if not msg=='': msg=' '+str(msg) canvas.create_text(self.ccx(cx),self.ccy(cy), text = "(%.2f, %.2f)%s" % (cx,cy,msg), font=("MS Sans Serif", 4), fill=self.traceColor) def ccx(self,x): return x/10**6*self.w1+self.w0[0] def ccy(self,y): return y/10**6*self.w1*-1+self.w0[1] def run(self): self.poly() self.label(0,0) self.label(10*10**6,0) for index,i in enumerate(playerThreads[0].poly): self.label(i[0],i[1],index) while True: for i in playerThreads[0].servers: self.paddle(i.playerid,90-(i.guessPosition()/10**5)) for i in playerThreads[0].balls: self.ball(i.x,i.y) canvas.update() canvas.after(40) canvas.delete('paddle') canvas.delete('ball') canvas.delete('label') #Initialization print "1" for i in xrange(numPlayers): playerThreads.append(pWorld()) for i in playerThreads: i.start() print "2" #Create the window and launch GUI thread w=pGui() win = Tkinter.Tk() canvas = Tkinter.Canvas(win, width=w.cx*2, height=w.cy*2, background='black') canvas.pack() w.start() win.mainloop() print "3"