-
Notifications
You must be signed in to change notification settings - Fork 11
/
client_receiver.py
115 lines (99 loc) · 3.92 KB
/
client_receiver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""
Client receives game state from server and draws it.
"""
import asyncio
import aiohttp
import pyglet
import click
from time import monotonic
from util_network import tick_asyncio
from backend import State
from frontend import draw_state, create_window
# How long one state from the log should be displayed (in seconds)
LOG_FRAME_TIME = 0.2
class Receiver:
def __init__(self, hostname):
self.window = None
self.state = None
self.available_robots = None
self.winner_time = 0
self.hostname = hostname
# Log of states to display in the future
self.log_to_play = []
# Starting point of the current animation
self.last_robots = None
# Time at which the current animation started
self.animation_start = 0
def window_draw(self):
"""
Draw the game state (board and robots).
"""
self.window.clear()
animation_pos = (monotonic() - self.animation_start) / LOG_FRAME_TIME
if animation_pos < 0:
animation_pos = 0
if animation_pos > 1:
animation_pos = 1
draw_state(
self.state, self.winner_time, self.available_robots, self.window,
last_robots=self.last_robots, animation_pos=animation_pos,
)
def reset_last_robots(self):
"""Set the starting point of the animation to the current state.
"""
self.last_robots = {robot.name: robot for robot in self.state.robots}
async def tick_log(self):
"""
Set the game state for the first element from the recorded game log
and delete it, meaning effectively play the step.
After the given delay (in seconds), repeat.
"""
while True:
if self.state:
self.reset_last_robots()
if self.log_to_play:
self.animation_start = monotonic()
new_state = self.log_to_play.pop(0)
if new_state == None:
if self.winner_time == 0:
self.winner_time = monotonic()
else:
self.state.robots = self.state.robots_from_dict(new_state)
await asyncio.sleep(LOG_FRAME_TIME)
async def get_game_state(self):
"""
Connect to server and receive messages.
Process information from server.
"""
task = asyncio.create_task(self.tick_log())
async with aiohttp.ClientSession() as session:
async with session.ws_connect('http://' + self.hostname + ':8080/receiver/') as ws:
# for loop is finished when client disconnects from server
async for message in ws:
message = message.json()
if "game_state" in message:
self.state = State.whole_from_dict(message)
self.reset_last_robots()
if self.window is None:
self.window = create_window(self.state, self.window_draw)
if "available_robots" in message:
self.available_robots = self.state.robots_from_dict({"robots": message["available_robots"]})
if 'log' in message:
self.log_to_play.extend(message['log'])
if "winner" in message:
self.state.winners = message["winner"]
self.log_to_play.append(None)
task.cancel()
@click.command()
@click.option("-h", "--hostname", default="localhost",
help="Server's hostname.")
def main(hostname):
receiver = Receiver(hostname)
pyglet.clock.schedule_interval(tick_asyncio, 1/30)
# Schedule the "client" task
# More about Futures - official documentation
# https://docs.python.org/3/library/asyncio-future.html
asyncio.ensure_future(receiver.get_game_state())
pyglet.app.run()
if __name__ == "__main__":
main()