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