Python Server for GameBoard

actionfunctionfieldsdescription
joinadd playerusername, password,
player
adds new player to gameboard
updateupdate player player, password,
vx, vy
updates player velocity
statusshow statusnone requiredgets player status, including player name,
color, position, and velocity
moveplaynone requiredcalculates player position since the last update
removeremove playerplayer, passwordremoves player from gameboard

All actions return HTML content.

add player

The join action uses the following HTML form: join.html.

The function looks up the user in the table of users. The color and location of the player is also taken from this table.

update player

The update action takes form input (either GET or POST). The URL can be entered manually as:

   https://johnloomis.org/python/db/gameboard.py?action=update&player=name&password=***&vx=vx&vy=vy

show status

The status action returns a list of data fields for each player. The URL can be entered manually as:

   https://johnloomis.org/python/db/gameboard.py?action=status

Follow this link

Example output:

"2008-04-05 18:24:48" "one" 1 255 0 0 94.29 334.66 -40 -10 "update"
"2008-04-05 18:24:48" "two" 2 0 255 0 45.64 64.46 20 30 "move"
"2008-04-05 18:24:48" "three" 4 0 0 255 38.82 125.64 10 20 "update"

move players

The move action returns a list of data fields for each player. The URL can be entered manually as:

   https://johnloomis.org/python/db/gameboard.py?action=move

Follow this link

Example output:

Datetime: 2008-04-05 19:49:22 

id 1 94.29 334.66 -40 -10
id 2 45.64 64.46 20 30
id 4 38.82 125.64 10 20

The status and move actions are used by GameBoard.java to display player positions, updated every couple of seconds.

See the GameBoard program documentation.

Contents

gameboard.py
game.py
logon.py

gameboard.py

This Python CGI script manages the database interaction.


001: #!/usr/bin/python
002: import cgi, cgitb, sys, math
003: import MySQLdb
004: import dbname
005: import logon
006: import game
007: 
008: 
009: def add_player(form,cur):
010:     uid = logon.logon(form,cur)
011:     if not uid:
012:         print '<p>login failed.'
013:         print '</html>'
014:         sys.exit()
015: 
016:     try:
017:         cur.execute("SELECT red, green, blue, xloc, yloc FROM users WHERE uid = %d" % uid)
018:         rows = cur.fetchall()
019:         if cur.rowcount==0:
020:             print '<p>user data not found'
021:             print '</html>'
022:             sys.exit()
023:         row = rows[0]
024:         username = form["username"].value
025:         password = form["password"].value
026:         player = form["player"].value
027:         if  not player: player="player"
028:         xpos = row[3]
029:         ypos = row[4]
030:         if xpos<0 or xpos>400: xpos=200
031:         if ypos<0 or ypos>400: ypos=200
032:         rs = (username, password, player, row[0], row[1], row[2], xpos, ypos)
033:         cur.execute("""INSERT INTO gameboard ( username, password, player, modified,
034:         red, green, blue, px, py, vx, vy, status )
035:         VALUES (
036:         '%s',
037:         '%s',
038:         '%s',
039:         now(),
040:         %d, %d, %d,
041:         %g, %g, 20, 30, 'join'
042:         )""" % rs )
043:         print '<p>player <b>%s</b> has been added to the gameboard.' % player
044:     except MySQLdb.Error, e:
045:        print e
046:        print '</html>'
047:        sys.exit()
048:        
049: def remove_player(form,cur):
050:     try:
051:         player = form['player'].value
052:         password = form['password'].value
053:         cur.execute("DELETE FROM gameboard WHERE player='%s' AND password='%s'" %
054:                 (player,password) )
055:         print 'player <b>%s</b> removed from gameboard' % player
056:     except MySQLdb.Error, e:
057:         print e
058:         
059:        
060: def update_player(form,cur):
061:     try:
062:         player = form['player'].value
063:         password = form['password'].value
064:         vx=float(form['vx'].value)
065:         vy=float(form['vy'].value)
066:         # limit velocity to 140
067:         vn = math.hypot(vx,vy)/140.0
068:         if (vn>1.0):
069:             vx = vx/vn
070:             vy = vy/vn
071:         rs = (vx,vy,player,password)
072:         cur.execute ("UPDATE gameboard SET vx=%g, vy=%g, status='update', modified=now() where player='%s' and password='%s'" % rs)
073:         print '<p>player <b>%s</b> velocity set to (%s, %s)' % (player, vx, vy)
074:         print '</html>'
075:     except MySQLdb.Error, e:
076:         print e
077:  
078: def show_status(cur):
079:     try:
080:         cur.execute ("SELECT modified, player, red, green, blue, px, py, vx, vy, status, id FROM gameboard")
081:     except MySQLdb.Error, e:
082:         print e
083: 
084:     rows = cur.fetchall()
085:     print '<pre>'
086:     for row in rows:
087:         modified = row[0]
088:         player = row[1]
089:         red = int(row[2])
090:         green = int(row[3])
091:         blue = int(row[4])
092:         px = float(row[5])
093:         py = float(row[6])
094:         vx = float(row[7])
095:         vy = float(row[8])
096:         status = row[9]
097:         id = int(row[10])
098:         rs = (modified, player, id, red, green, blue, px, py, vx, vy, status)
099:         print '"%s" "%s" %d %d %d %d %g %g %g %g "%s"' % rs
100:     print '</pre>'
101:     
102: def move_player(cur, rs):
103:     try:
104:         cur.execute("""UPDATE gameboard SET modified='%s',
105:         px=%g, py=%g, vx=%g, vy=%g WHERE id=%d""" % rs)
106:     except MySQLdb.Error, e:
107:         print e
108:         print '</html>'
109:         sys.exit()
110:         
111:     
112: def play(cur):
113:     import time
114:     try:
115:         cur.execute("SELECT now()")
116:     except MySQLdb.Error, e:
117:         print e
118:         print '</html>'
119:         sys.exit()
120:     rows = cur.fetchall()
121:     now = rows[0][0];
122:     fmt = '%Y-%m-%d %H:%M:%S'
123:     d = time.strptime(now,fmt)
124:     sn = (d[3]*60+d[4])*60+d[5]
125:     #dtn = datetime.datetime(*d[0:6])
126:    
127:     print '<p>Datetime: '
128:     print now
129:       
130:     try:
131:         cur.execute("SELECT modified, id, px, py, vx, vy FROM gameboard")
132:     except MySQLdb.Error, e:
133:         print e
134:         print '</html>'
135:         sys.exit()
136:     rows = cur.fetchall()
137:     print '<p><pre>'
138:     for row in rows:
139:         modified = row[0]
140:         d = time.strptime(modified,fmt)
141:         s = (d[3]*60+d[4])*60+d[5]
142:         #dt = datetime.datetime(*d[0:6])
143:         diff = max(sn - s,1.0)
144:         id = int(row[1])
145:         #print '%d %s' % (diff,player)
146:         px = float(row[2])
147:         py = float(row[3])
148:         vx = float(row[4])
149:         vy = float(row[5])
150:         ball = game.Ball(17,px,py,vx,vy)
151:         game.update_ball(ball,diff)
152:         rs = (now, ball.x, ball.y, ball.vx, ball.vy, id)
153:         move_player(cur,rs)
154:         print 'id %d %g %g %g %g' % (id, px, py, vx, vy)
155:         
156:     print '</pre>'
157:     
158:     
159:     
160: 
161: #-------main program
162: cgitb.enable() # formats errors in HTML
163: 
164: sys.stderr = sys.stdout
165: print "Content-type: text/html"
166: print
167: print '<html>'
168: 
169: 
170: form = cgi.FieldStorage()
171: db = dbname.dbopen()
172: cur = db.cursor()
173: 
174: if form.has_key("action"): action = form["action"].value
175: else: action = 'status'
176: 
177: #print '<p>action: <b>%s</b>' % action
178: 
179: 
180: if action=='status': show_status(cur)
181: elif action=='join': add_player(form,cur)
182: elif action=='update': update_player(form,cur)
183: elif action=='move': play(cur)
184: elif action=='remove': remove_player(form,cur)
185: else: print '<p>action <b>%s</b> not recognized' % action
186: print '</html>'
187: cur.close()
188: db.close()


game.py

game.py defines the Ball class to represent players.

intersect window calculates the time required to strike the closest window border in the direction of travel.

wall collision changes the ball velocity after collision.

update ball calculates, in order, the wall collisions during the specified interval and advances the ball to its final position (which is then used by move player in updating the gameboard database.)


01: #!/usr/bin python
02: 
03: tscale = 0.5
04: 
05: class Ball(object):
06:     'bouncing ball objects'
07:     def __init__(self, radius, px, py, vx, vy):
08:         self.radius = radius
09:         self.x = px
10:         self.y = py
11:         self.vx = vx
12:         self.vy = vy
13:     def __str__(self):
14:         rs = (self.x, self.y, self.vx, self.vy)
15:         str = 'pos (%g, %g) vel (%g, %g)' % rs
16:         return str
17:     def move(self,tstep):
18:         dt = tstep*tscale;
19:         self.x = self.x + self.vx*dt;
20:         self.y = self.y + self.vy*dt;
21:  
22: 
23: left=0
24: right=400
25: bottom=0
26: top=400
27: 
28: def intersect_window(ball,tstep):
29:   'find nearest wall intersection for times < tstep'
30:   tx = tstep
31:   idx = -1
32:   if ball.vx<0:
33:     t = (ball.x-ball.radius - left)/(-ball.vx)
34:     if t<tx:
35:         tx = t
36:         idx = 0
37:   if ball.vx>0:
38:     t = (right-ball.x - ball.radius)/(ball.vx)
39:     if t<tx:
40:         tx = t
41:         idx = 1
42:   if ball.vy<0:
43:     t = (ball.y-ball.radius - bottom)/(-ball.vy)
44:     if t<tx:
45:         tx = t
46:         idx = 2
47:   if ball.vy>0:
48:     t = (top - ball.y  - ball.radius)/(ball.vy)
49:     if t<tx:
50:         tx = t
51:         idx = 3
52:   tx = tx/tscale;
53:   if tx>tstep: return
54:   return [tx, idx]
55: 
56: def wall_collision(ball,idx):
57:   if idx<2: ball.vx = -ball.vx
58:   else: ball.vy = -ball.vy
59: 
60: 
61: def update_ball(ball,tstep):
62:     tmore = tstep
63:     while tmore>0:
64:         c = intersect_window(ball,tmore)
65:         if c:
66:           ball.move(c[0])
67:           wall_collision(ball,c[1])
68:           tmore = tmore - c[0]
69:         else:
70:           ball.move(tmore)
71:           tmore = 0.0
72:       
73: 
74: #if __name__ == '__main__':
75: def test():
76:     ball = Ball(7,100,100,20,30)
77:     for n in range(1,20):
78:         update_ball(ball,1)
79:         print ball


logon.py


01: #!/usr/bin/python
02: import cgi
03: import MySQLdb
04: 
05: def logon(form, cur):
06:     fields = ["username", "password"]
07:     for field in fields:
08:         if not form.has_key(field): return
09:     try:
10:         cur.execute("""SELECT uid from users
11:           where username='%s' and password='%s'""" % (form["username"].value, form["password"].value) )
12:         rows = cur.fetchall()
13:         if (cur.rowcount > 0): return rows[0]
14:     except MySQLdb.Error, e:
15:         print e


Maintained by John Loomis, updated Sat Apr 05 21:42:11 2008