mirror of https://github.com/dirtbags/moth.git
Uberserv now does everything; simplify heartbeat
This commit is contained in:
parent
f3f5e797b3
commit
d638a3be0c
93
game.py
93
game.py
|
@ -9,43 +9,9 @@ import time
|
|||
from errno import EPIPE
|
||||
|
||||
|
||||
# The current time of day
|
||||
now = time.time()
|
||||
|
||||
# Heartbeat frequency (in seconds)
|
||||
pulse = 2.0
|
||||
|
||||
##
|
||||
## Heartbeat stuff
|
||||
##
|
||||
|
||||
hearts = set()
|
||||
last_beat = 0
|
||||
|
||||
def add_heart(cb):
|
||||
global hearts
|
||||
|
||||
hearts.add(cb)
|
||||
|
||||
|
||||
def del_heart(cb):
|
||||
global hearts
|
||||
|
||||
hearts.remove(cb)
|
||||
|
||||
|
||||
def beat_heart():
|
||||
global hearts, last_beat, now
|
||||
|
||||
if now - last_beat > pulse:
|
||||
last_beat = now
|
||||
for cb in hearts:
|
||||
try:
|
||||
cb()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
##
|
||||
## Network stuff
|
||||
##
|
||||
|
@ -59,6 +25,7 @@ class Listener(asyncore.dispatcher):
|
|||
self.listen(4)
|
||||
self.player_factory = player_factory
|
||||
self.manager = manager
|
||||
self.last_beat = 0
|
||||
|
||||
def handle_accept(self):
|
||||
conn, addr = self.accept()
|
||||
|
@ -66,6 +33,12 @@ class Listener(asyncore.dispatcher):
|
|||
# We don't need to keep the player, asyncore.socket_map already
|
||||
# has a reference to it for as long as it's open.
|
||||
|
||||
def readable(self):
|
||||
now = time.time()
|
||||
if now > self.last_beat + pulse:
|
||||
self.manager.heartbeat(now)
|
||||
return True
|
||||
|
||||
|
||||
class Flagger(asynchat.async_chat):
|
||||
"""Connection to flagd"""
|
||||
|
@ -115,11 +88,12 @@ class Manager:
|
|||
self.games = set()
|
||||
self.lobby = set()
|
||||
self.contestants = []
|
||||
add_heart(self.heartbeat)
|
||||
self.last_beat = 0
|
||||
|
||||
def heartbeat(self):
|
||||
def heartbeat(self, now):
|
||||
# Called by listener to beat heart
|
||||
for game in list(self.games):
|
||||
game.heartbeat()
|
||||
game.heartbeat(now)
|
||||
|
||||
def enter_lobby(self, player):
|
||||
self.lobby.add(player)
|
||||
|
@ -143,6 +117,7 @@ class Manager:
|
|||
"""Start a new contest."""
|
||||
|
||||
self.contestants = list(self.lobby)
|
||||
print('new playoff:', [c.name for c in self.contestants])
|
||||
|
||||
def run_contest(self):
|
||||
# Purge any disconnected players
|
||||
|
@ -175,7 +150,7 @@ class Manager:
|
|||
player.attach_game(game)
|
||||
|
||||
def declare_winner(self, game, winner=None):
|
||||
print('winner', game, winner)
|
||||
print('Winner:', winner and winner.name)
|
||||
self.games.remove(game)
|
||||
|
||||
# Winner stays in the contest
|
||||
|
@ -199,8 +174,6 @@ class Player(asynchat.async_chat):
|
|||
timeout = 10.0
|
||||
|
||||
def __init__(self, sock, manager):
|
||||
global now
|
||||
|
||||
asynchat.async_chat.__init__(self, sock=sock)
|
||||
self.manager = manager
|
||||
self.game = None
|
||||
|
@ -209,14 +182,12 @@ class Player(asynchat.async_chat):
|
|||
self.blocked = None
|
||||
self.name = None
|
||||
self.pending = None
|
||||
self.last_activity = now
|
||||
self.last_activity = time.time()
|
||||
|
||||
def readable(self):
|
||||
global now, timeout
|
||||
|
||||
ret = (not self.blocked) and asynchat.async_chat.readable(self)
|
||||
if ret:
|
||||
if now - self.last_activity > self.timeout:
|
||||
if time.time() - self.last_activity > self.timeout:
|
||||
# They waited too long.
|
||||
self.err('idle timeout')
|
||||
self.close()
|
||||
|
@ -229,10 +200,8 @@ class Player(asynchat.async_chat):
|
|||
|
||||
def unblock(self):
|
||||
"""Unblock reads"""
|
||||
global now
|
||||
|
||||
self.blocked = False
|
||||
self.last_activity = now
|
||||
self.last_activity = time.time()
|
||||
|
||||
def attach_game(self, game):
|
||||
self.game = game
|
||||
|
@ -330,7 +299,7 @@ class Game:
|
|||
def setup(self):
|
||||
pass
|
||||
|
||||
def heartbeat(self):
|
||||
def heartbeat(self, now):
|
||||
pass
|
||||
|
||||
def declare_winner(self, winner):
|
||||
|
@ -382,8 +351,7 @@ class TurnBasedGame(Game):
|
|||
game_timeout = 6.0
|
||||
|
||||
def __init__(self, manager, players):
|
||||
global now
|
||||
|
||||
now = time.time()
|
||||
self.ended_turn = set()
|
||||
self.running = True
|
||||
self.winner = None
|
||||
|
@ -391,10 +359,8 @@ class TurnBasedGame(Game):
|
|||
self.began = now
|
||||
Game.__init__(self, manager, players)
|
||||
|
||||
def heartbeat(self):
|
||||
global now
|
||||
|
||||
if now - self.began > self.game_timeout:
|
||||
def heartbeat(self, now=None):
|
||||
if now and (now - self.began > self.game_timeout):
|
||||
self.running = False
|
||||
|
||||
# Idle players forfeit. They're also booted, so we don't have
|
||||
|
@ -443,7 +409,7 @@ class TurnBasedGame(Game):
|
|||
def end_turn(self, player):
|
||||
"""End player's turn."""
|
||||
|
||||
global now
|
||||
now = time.time()
|
||||
|
||||
self.ended_turn.add(player)
|
||||
self.lastmoved[player] = now
|
||||
|
@ -483,18 +449,13 @@ class TurnBasedGame(Game):
|
|||
## Running a game
|
||||
##
|
||||
|
||||
def loop():
|
||||
global pulse, now
|
||||
|
||||
while asyncore.socket_map:
|
||||
now = time.time()
|
||||
beat_heart()
|
||||
asyncore.poll2(timeout=pulse, map=asyncore.socket_map)
|
||||
|
||||
|
||||
def run(game_factory, port, auth, minplayers, maxplayers=None):
|
||||
def start(game_factory, port, auth, minplayers, maxplayers=None):
|
||||
flagger = Flagger(('localhost', 6668), auth)
|
||||
manager = Manager(game_factory, flagger, minplayers, maxplayers)
|
||||
listener = Listener(('', port), Player, manager)
|
||||
loop()
|
||||
return (flagger, manager, listener)
|
||||
|
||||
def run(game_factory, port, auth, minplayers, maxplayers=None):
|
||||
start(game_factory, port, auth, minplayers, maxplayers)
|
||||
asyncore.loop(use_poll=True)
|
||||
|
||||
|
|
29
histogram.py
29
histogram.py
|
@ -46,19 +46,22 @@ def main(s=None):
|
|||
write_scores(when)
|
||||
scoresfile.flush()
|
||||
|
||||
gp = os.popen('gnuplot 2> /dev/null', 'w')
|
||||
gp.write('set style data lines\n')
|
||||
gp.write('set xdata time\n')
|
||||
gp.write('set timefmt "%s"\n')
|
||||
gp.write('set format ""\n')
|
||||
gp.write('set border 3\n')
|
||||
gp.write('set xtics nomirror\n')
|
||||
gp.write('set ytics nomirror\n')
|
||||
gp.write('set nokey\n')
|
||||
gp.write('set terminal png transparent size 640,200 x000000 xffffff\n')
|
||||
gp.write('set output "histogram.png"\n')
|
||||
gp.write('plot %s\n' % ','.join(plotparts))
|
||||
gp.close()
|
||||
instructions = tempfile.NamedTemporaryFile('w')
|
||||
instructions.write('''
|
||||
set style data lines
|
||||
set xdata time
|
||||
set timefmt "%%s"
|
||||
set format ""
|
||||
set border 3
|
||||
set xtics nomirror
|
||||
set ytics nomirror
|
||||
set nokey
|
||||
set terminal png transparent size 640,200 x000000 xffffff
|
||||
set output "histogram.png"
|
||||
plot %(plot)s\n''' % {'plot': ','.join(plotparts)})
|
||||
instructions.flush()
|
||||
|
||||
gp = os.system('gnuplot %s' % instructions.name)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -17,11 +17,11 @@ def submit(sock, cat, team, score):
|
|||
break
|
||||
b = sock.recv(500)
|
||||
try:
|
||||
when, txt = points.decode_response(b)
|
||||
when, cat_, txt = points.decode_response(b)
|
||||
except ValueError:
|
||||
# Ignore invalid packets
|
||||
continue
|
||||
if when != mark:
|
||||
if (when != mark) or (cat_ != cat):
|
||||
# Ignore wrong timestamp
|
||||
continue
|
||||
if txt == 'OK':
|
||||
|
|
10
roshambo.py
10
roshambo.py
|
@ -35,9 +35,13 @@ class Roshambo(game.TurnBasedGame):
|
|||
self.make_move(player, 'paper')
|
||||
|
||||
|
||||
def main():
|
||||
game.run(Roshambo, 5388, b'roshambo:::984233f357ecac03b3e38b9414cd262b', 2)
|
||||
def start():
|
||||
return game.start(Roshambo, 5388, b'roshambo:::984233f357ecac03b3e38b9414cd262b', 2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
import asyncore
|
||||
|
||||
start()
|
||||
asyncore.loop(use_poll=True)
|
||||
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ class Client:
|
|||
if not line:
|
||||
return
|
||||
if self.debug:
|
||||
print ('<-- %s' % line)
|
||||
print (self, '<-- %s' % line)
|
||||
return json.loads(line)
|
||||
|
||||
def command(self, *val):
|
||||
|
@ -41,29 +41,33 @@ class Client:
|
|||
return ret
|
||||
|
||||
|
||||
class RandomBot(threading.Thread):
|
||||
def __init__(self, team):
|
||||
class IdiotBot(threading.Thread):
|
||||
def __init__(self, team, move):
|
||||
threading.Thread.__init__(self)
|
||||
self.team = team
|
||||
self.move = move
|
||||
|
||||
def get_move(self):
|
||||
return self.move
|
||||
|
||||
def run(self):
|
||||
c = Client(('localhost', 5388))
|
||||
c.debug = True
|
||||
c.debug = False
|
||||
#print('lobby', c.command('^', 'lobby'))
|
||||
c.command('login', self.team, 'furble')
|
||||
while True:
|
||||
move = random.choice(['rock', 'scissors', 'paper'])
|
||||
move = self.get_move()
|
||||
ret = c.command(move)
|
||||
if ret == ['WIN']:
|
||||
print('%s wins' % self.team)
|
||||
amt = random.uniform(0.2, 2.1)
|
||||
amt = random.uniform(0.1, 1.2)
|
||||
if c.debug:
|
||||
print(c, 'sleep %f' % amt)
|
||||
time.sleep(amt)
|
||||
|
||||
def main():
|
||||
bots = []
|
||||
for i in ['zebra', 'aardvark', 'wembly']:
|
||||
bots.append(RandomBot(i).start())
|
||||
for team, move in (('rockbot', 'rock'), ('cutbot', 'scissors'), ('paperbot', 'paper')):
|
||||
bots.append(IdiotBot(team, move).start())
|
||||
|
||||
main()
|
||||
|
|
|
@ -35,7 +35,7 @@ for cat, total in categories:
|
|||
for points, team in scores:
|
||||
color = teamcolors[team]
|
||||
print('<div style="height: %f%%; overflow: hidden; background: #%s; color: black;">' % (float(points * 100)/total, color))
|
||||
print('<!-- %s --> %s: %d' % (cat, team, points))
|
||||
print('<!-- category: %s --> %s: %d' % (cat, team, points))
|
||||
print('</div>')
|
||||
print('</td>')
|
||||
print('</tr>')
|
||||
|
|
|
@ -2,12 +2,15 @@
|
|||
|
||||
import asyncore
|
||||
import pointsd
|
||||
import roshambo
|
||||
import game
|
||||
import flagd
|
||||
import histogram
|
||||
|
||||
def main():
|
||||
pointsrv = pointsd.start()
|
||||
flagsrv = flagd.start()
|
||||
roshambosrv = roshambo.start()
|
||||
s = pointsrv.store
|
||||
slen = 0
|
||||
while True:
|
||||
|
|
Loading…
Reference in New Issue