Esempio n. 1
0
def step(d):
    startTime = time.perf_counter()

    d.state['gameStep'] += 1
    d.state['serverSteps'] += 1

    # for each bot that is alive, copy health to so we know what it was at the start of the step.
    aliveBots = {}
    for src, bot in d.bots.items():
        if bot['health'] != 0:
            aliveBots[src] = bot['health']

    # for all bots that are alive
    for src, bot in d.bots.items():
        if src in aliveBots:
            # change speed if needed
            if bot['currentSpeed'] > bot['requestedSpeed']:
                bot['currentSpeed'] -= d.getClassValue('botAccRate', bot['class'])
                if bot['currentSpeed'] < bot['requestedSpeed']:
                    bot['currentSpeed'] = bot['requestedSpeed']
            elif bot['currentSpeed'] < bot['requestedSpeed']:
                bot['currentSpeed'] += d.getClassValue('botAccRate', bot['class'])
                if bot['currentSpeed'] > bot['requestedSpeed']:
                    bot['currentSpeed'] = bot['requestedSpeed']

            # change direction if needed
            if bot['currentDirection'] != bot['requestedDirection']:
                if bot['currentDirection'] != bot['requestedDirection'] and bot['currentSpeed'] == 0:
                    # turn instanly if bot is not moving
                    bot['currentDirection'] = bot['requestedDirection']
                else:
                    # how much can we turn at the speed we are going?
                    turnRate = d.getClassValue('botMinTurnRate', bot['class']) \
                        + (d.getClassValue('botMaxTurnRate', bot['class']) -
                           d.getClassValue('botMinTurnRate', bot['class'])) \
                        * (1 - bot['currentSpeed'] / 100)

                    # if turn is negative and does not pass over 0 radians
                    if bot['currentDirection'] > bot['requestedDirection'] and \
                            bot['currentDirection'] - bot['requestedDirection'] <= math.pi:
                        bot['currentDirection'] -= turnRate
                        if bot['currentDirection'] <= bot['requestedDirection']:
                            bot['currentDirection'] = bot['requestedDirection']

                    # if turn is negative and passes over 0 radians, so we may need to normalize angle
                    elif bot['requestedDirection'] > bot['currentDirection'] and \
                            bot['requestedDirection'] - bot['currentDirection'] >= math.pi:
                        bot['currentDirection'] = nbmath.normalizeAngle(bot['currentDirection'] - turnRate)
                        if bot['currentDirection'] <= bot['requestedDirection'] and bot['currentDirection'] >= bot['requestedDirection'] - math.pi:
                            bot['currentDirection'] = bot['requestedDirection']

                    # if turn is positive and does not pass over 0 radians
                    elif bot['requestedDirection'] > bot['currentDirection'] and \
                            bot['requestedDirection'] - bot['currentDirection'] <= math.pi:
                        bot['currentDirection'] += turnRate
                        if bot['requestedDirection'] <= bot['currentDirection']:
                            bot['currentDirection'] = bot['requestedDirection']

                    # if turn is positive and passes over 0 radians
                    elif bot['currentDirection'] > bot['requestedDirection'] and \
                            bot['currentDirection'] - bot['requestedDirection'] >= math.pi:
                        bot['currentDirection'] = nbmath.normalizeAngle(bot['currentDirection'] + turnRate)
                        if bot['currentDirection'] >= bot['requestedDirection'] and bot['currentDirection'] <= bot['requestedDirection'] + math.pi:
                            bot['currentDirection'] = bot['requestedDirection']
            # move bot
            if bot['currentSpeed'] != 0:
                bot['x'], bot['y'] = nbmath.project(bot['x'], bot['y'],
                                        bot['currentDirection'],
                                        bot['currentSpeed'] / 100.0 * d.getClassValue('botMaxSpeed', bot['class']))

    # set starting hitSeverity to 0 for all robots. hitSeverity == 0 means robot did not 
    # hit anything this step.
    for src, bot in d.bots.items():
        bot['hitSeverity'] = 0.0

    # do until we get one clean pass where no bot hitting wall, obstacle or other bot.
    foundOverlap = True
    while foundOverlap:
        foundOverlap = False

        # detect if bots hit walls. if they, did move them so they are just barely not touching,
        for src, bot in d.bots.items():
            hitSeverity = 0
            if bot['x'] - d.conf['botRadius'] < 0:
                # hit left side
                bot['x'] = d.conf['botRadius'] + 1
                hitSeverity = getHitSeverity(d, bot, math.pi)
            if bot['x'] + d.conf['botRadius'] > d.conf['arenaSize']:
                # hit right side
                bot['x'] = d.conf['arenaSize'] - d.conf['botRadius'] - 1
                hitSeverity = getHitSeverity(d, bot, 0)
            if bot['y'] - d.conf['botRadius'] < 0:
                # hit bottom side
                bot['y'] = d.conf['botRadius'] + 1
                hitSeverity = getHitSeverity(d, bot, math.pi * 3 / 2)
            if bot['y'] + d.conf['botRadius'] > d.conf['arenaSize']:
                # hit top side
                bot['y'] = d.conf['arenaSize'] - d.conf['botRadius'] - 1
                hitSeverity = getHitSeverity(d, bot, math.pi/2)
    
            if hitSeverity:
                foundOverlap = True
                bot['hitSeverity'] = max(bot['hitSeverity'], hitSeverity)

        # detect if bots hit obstacles, if the did move them so they are just barely not touching,
        overlap = findOverlapingBotsAndObstacles(d, d.bots)
        while overlap:
            foundOverlap = True
            b = d.bots[overlap[0]]
            o = overlap[1]
            # find angle to move bot directly away from obstacle
            a = nbmath.angle(o['x'], o['y'], b['x'], b['y'])
            # find min distance to move bot so it don't touch (plus 0.5 for safety).
            distance = d.conf['botRadius'] + o['radius'] + 0.5 - nbmath.distance(o['x'], o['y'], b['x'], b['y'])
            # move bot
            b['x'], b['y'] = nbmath.project(b['x'], b['y'], a, distance)
            # record damage
            hitSeverity = getHitSeverity(d, b, a + math.pi)
            b['hitSeverity'] = max(b['hitSeverity'], hitSeverity)
            # check for more bots overlapping
            overlap = findOverlapingBotsAndObstacles(d, d.bots)
                    
        # detect if bots hit other bots, if the did move them so they are just barely not touching,
        overlap = findOverlapingBots(d, d.bots)
        while overlap:
            foundOverlap = True
            b1 = d.bots[overlap[0]]
            b2 = d.bots[overlap[1]]
            # find angle to move bot directly away from each other
            a = nbmath.angle(b1['x'], b1['y'], b2['x'], b2['y'])
            # find min distance to move each bot so they don't touch (plus 0.5 for saftly).
            between = nbmath.distance(b1['x'], b1['y'], b2['x'], b2['y'])
            distance = between / 2 - (between - d.conf['botRadius']) + 0.5
            # move bots
            b1['x'], b1['y'] = nbmath.project(b1['x'], b1['y'], a + math.pi, distance)
            b2['x'], b2['y'] = nbmath.project(b2['x'], b2['y'], a, distance)
            # record damage
            hitSeverity = getHitSeverity(d, b1, a, b2)
            b1['hitSeverity'] = max(b1['hitSeverity'], hitSeverity)
            b2['hitSeverity'] = max(b2['hitSeverity'], hitSeverity)
            # check for more bots overlapping
            overlap = findOverlapingBots(d, d.bots)

    # give damage (only once this step) to bots that hit things. Also stop them.
    for src, bot in d.bots.items():
        if bot['hitSeverity']:
            if d.conf['simpleCollisions']:
                bot['hitSeverity'] = 1
            bot['health'] = max(0, bot['health'] - bot['hitSeverity'] * d.conf['hitDamage'] * d.getClassValue('botArmor', bot['class']))
            bot['currentSpeed'] = 0
            bot['requestedSpeed'] = 0
        del bot['hitSeverity']

    # for all shells
    for src in list(d.shells.keys()):
        shell = d.shells[src]

        # remember shells start point before moving
        oldx = shell['x']
        oldy = shell['y']

        # move shell
        distance = min(d.getClassValue('shellSpeed', d.bots[src]['class']), shell['distanceRemaining'])
        shell['x'], shell['y'] = nbmath.project(shell['x'], shell['y'], shell['direction'], distance)
        shell['distanceRemaining'] -= distance

        # did shell hit an obstacle?
        shellHitObstacle = False
        for o in d.conf['obstacles']:
            if nbmath.intersectLineCircle(oldx, oldy, shell['x'], shell['y'], o['x'], o['y'], o['radius']):
                shellHitObstacle = True

        # if did not hit an obstacle and shell's explosion would touch inside of arena
        if not shellHitObstacle and \
           (shell['x'] > d.getClassValue('explRadius', d.bots[src]['class']) * -1 and shell['x'] < d.conf['arenaSize'] + d.getClassValue('explRadius', d.bots[src]['class']) and
                shell['y'] > d.getClassValue('explRadius', d.bots[src]['class']) * -1 and shell['y'] < d.conf['arenaSize'] + d.getClassValue('explRadius', d.bots[src]['class'])):

            # if shell has reached it destination then explode.
            if shell['distanceRemaining'] <= 0:
                # apply damage to bots.
                for k, bot in d.bots.items():
                    if bot['health'] > 0:
                        distance = nbmath.distance(bot['x'], bot['y'], shell['x'], shell['y'])
                        if distance < d.getClassValue('explRadius', d.bots[src]['class']):
                            damage = d.getClassValue('explDamage', d.bots[src]['class']) * (1 - distance / d.getClassValue('explRadius', d.bots[src]['class']))
                            bot['health'] = max(0, bot['health'] - (damage * d.getClassValue('botArmor', bot['class'])))
                            # allow recording of inflicting damage that is greater than health of hit robot.
                            # also record damage to oneself.
                            d.bots[src]['shellDamage'] += damage

                # store the explosion so viewers can display it. we can't use src as index because it is possible for two explosions
                # from same bot to exist (but not likly).
                d.explosions[d.state['explIndex']] = {
                    'x': shell['x'],
                    'y': shell['y'],
                    'stepsAgo': 0,
                    'src': src  # this is needed by viewer to color this explosion based on the bot who fired it.
                    }
                d.state['explIndex'] += 1
                if d.state['explIndex'] > 65000:
                    d.state['explIndex'] = 0

                # this shell exploed so remove it
                del d.shells[src]
        else:
            # shell hit obstacle or left arena so remove it without exploding
            del d.shells[src]

    # Remove old explosions and add 1 to other explosions stepsAgo.
    # Note, We only keep these around so the viewer can do a nice animation
    # over a number of steps before they are removed.
    for key in list(d.explosions.keys()):
        expl = d.explosions[key]
        if expl['stepsAgo'] == d.conf['keepExplosionSteps']:
            del d.explosions[key]
        else:
            expl['stepsAgo'] += 1

    # find how many points bots that died this step will get. (Based on how many bots have died previouly)
    if len(aliveBots) == d.conf['botsInGame']:
        points = 0  # first to die
    elif len(aliveBots) > d.conf['botsInGame'] / 2:
        points = 2  # died in first half
    else:
        points = 5  # died in second half

    # Kill all bots if we have reached the max steps and there is still more than one bot alive.
    if d.state['gameStep'] == d.conf['stepMax'] and len(aliveBots) != 1:
        log("Game reached stepMax with more than one bot alive. Killing all bots.")
        for src in aliveBots:
            d.bots[src]['health'] = 0

    # Assign points to bots that died this turn
    for src in list(aliveBots.keys()):
        if d.bots[src]['health'] == 0:
            d.bots[src]['points'] += points
            del aliveBots[src]

    # If only one bot is left then end game.
    if len(aliveBots) == 1:
        src = list(aliveBots.keys())[0]
        d.bots[src]['winHealth'] += d.bots[src]['health']
        d.bots[src]['winCount'] += 1
        d.bots[src]['health'] = 0
        d.bots[src]['points'] += 10  # last robot (winner)
        del aliveBots[src]

    d.state['stepTime'] += time.perf_counter() - startTime
Esempio n. 2
0
def checkForUpdates(d):
    msg = {"type": "Error", "result": "We never got any new data from server."}
    try:
        # keep getting messages until we get the last one and then an exception is thrown.
        while True:
            msg, ip, port = d.viewerSocket.recvMessage()
            
            d.replayData.append(msg)
            while len(d.replayData) > d.replaySaveSteps / d.replaySaveEveryNth:
                d.replayData.pop(0)
    except nbipc.NetBotSocketException as e:
        # if message type is Error and we have not got good data for 100 steps then quit
        if msg['type'] == 'Error' and d.lastViewData + d.conf['stepSec'] * 100 < time.time():
            # We didn't get anything from the buffer or it was an invalid message.
            d.canvas.itemconfigure(d.bigMsg, text="Server stopped sending data.")
    except Exception as e:
        log(str(e), "ERROR")
        quit()

    if msg['type'] == 'viewData':
        
        # turn replay on or off if space bar pressed
        if d.toggleReplaying:
            if d.isReplaying:
                d.playingData = []
                d.toggleReplaying = False
                d.isReplaying = False
            else:
                d.playingData = []
                d.playingData.extend(d.replayData)
                d.toggleReplaying = False
                d.isReplaying = True

        # play back and then remove data
        if d.isReplaying:
            if len(d.playingData) > 0:
                msg = d.playingData[0]
                d.playingData.pop(0)

            # replay is over
            else:
                d.isReplaying = False

        # draw red border on arena and red instant replay widget
        if d.isReplaying:
            d.canvas.config(highlightbackground='#FF0000')

            if d.replayWidget is None:
                d.replayWidget = t.Message(d.frame, width=200, justify='center')
                d.replayWidget.config(highlightbackground='#FF0000')
                d.replayWidget.config(highlightthickness=d.borderSize)
                d.replayWidget.pack(fill=t.X)
                d.replayWidget.config(text="Instant Replay!")

        else:
            d.canvas.config(highlightbackground='#000')

            if d.replayWidget is not None:
                d.replayWidget.destroy()
                d.replayWidget = None
                
        # if gameNumber == 0 then post message
        if msg['state']['gameNumber'] == 0:
            leftToJoin = d.conf['botsInGame'] - len(msg['bots'])
            if leftToJoin == 1:
                s = ""
            else:
                s = "s"
            d.canvas.itemconfigure(d.bigMsg, text="Waiting for " +
                                   str(leftToJoin) +
                                   " robot" + s + " to join.")
        else:
            d.canvas.itemconfigure(d.bigMsg, text="")

        for src, bot in msg['bots'].items():
            # ensure all bots on server have widgets
            if not src in d.botStatusWidgets:
                # pick color for this bot
                c = d.colors.pop()

                # create bot status widget
                d.botStatusWidgets[src] = t.Message(d.frame, width=200, justify='center')
                d.botStatusWidgets[src].config(highlightbackground=c)
                d.botStatusWidgets[src].config(highlightthickness=d.borderSize)
                d.botStatusWidgets[src].pack(fill=t.X)

                # create bot widgets
                d.botScan[src] = d.canvas.create_arc(0, 0, 50, 50, start=0, extent=0,
                             style='arc', width=4, outline='#bbb')
                d.botTrackLeft[src] = d.canvas.create_line(0, 0, 50, 50, width=
                    d.conf['botRadius'] * (10 / 24.0), fill='grey')
                d.botTrackRight[src] = d.canvas.create_line(0, 0, 50, 50, width=
                    d.conf['botRadius'] * (10 / 24.0), fill='grey')
                d.botWidgets[src] = d.canvas.create_oval(0, 0, 0, 0, fill=c)
                d.botCanon[src] = d.canvas.create_line(0, 0, 50, 50, width=
                    d.conf['botRadius'] * (1/3.0), fill=c)
                d.botRequestedDirection[src] = d.canvas.create_line(0, 0, 50, 50, width=
                    d.conf['botRadius'] * (5 / 24.0), arrow=t.LAST, fill=colorVariant(c,-100))
                d.botCurrentDirection[src] = d.canvas.create_line(0, 0, 50, 50, width=
                    d.conf['botRadius'] * (5 / 24.0), arrow=t.LAST, fill=colorVariant(c,100))

            # update text for each bot
            d.botStatusWidgets[src].config(text=bot['name'] +
                                           "\n" + "__________________________________" +
                                           "\nPoints: " + str(bot['points']) +
                                           "\nCanon Fired: " + str(bot['firedCount']) +
                                           "\nShell Damage Inflicted: " + '%.1f' % (bot['shellDamage']) +
                                           "\n" + "__________________________________" +
                                           "\nHealth: " + '%.1f' % (bot['health']) + "%"
                                           "   Speed: " + '%.1f' % (bot['currentSpeed']) + "%")

            # update location of bot widgets or hide if health == 0
            if bot['health'] == 0:
                d.canvas.itemconfigure(d.botWidgets[src], state='hidden')
                d.canvas.itemconfigure(d.botRequestedDirection[src], state='hidden')
                d.canvas.itemconfigure(d.botCurrentDirection[src], state='hidden')
                d.canvas.itemconfigure(d.botTrackLeft[src], state='hidden')
                d.canvas.itemconfigure(d.botTrackRight[src], state='hidden')
                d.canvas.itemconfigure(d.botScan[src], state='hidden')
                d.canvas.itemconfigure(d.botCanon[src], state='hidden')
            else:
                centerX = bot['x'] * d.scale + d.borderSize
                centerY = d.conf['arenaSize'] - bot['y'] * d.scale + d.borderSize
                d.canvas.coords(d.botWidgets[src],
                                centerX - d.conf['botRadius'],
                                centerY - d.conf['botRadius'],
                                centerX + d.conf['botRadius'],
                                centerY + d.conf['botRadius'])

                d.canvas.coords(d.botRequestedDirection[src], centerX + d.conf['botRadius'] 
                                * bot['requestedSpeed']/100 * (19.0 / 24.0)
                                * math.cos(-bot['requestedDirection']),  # 19
                                centerY + d.conf['botRadius'] 
                                * bot['requestedSpeed']/100 * (19.0 / 24.0) * math.sin(
                                    -bot['requestedDirection']),
                                d.conf['botRadius'] * bot['requestedSpeed']/100 * math.cos(-bot['requestedDirection']) + centerX,  # 24
                                d.conf['botRadius'] * bot['requestedSpeed']/100 * math.sin(-bot['requestedDirection']) + centerY)

                d.canvas.coords(d.botCurrentDirection[src], centerX + d.conf['botRadius'] 
                                * bot['currentSpeed']/100 * (19.0 / 24.0)
                                * math.cos(-bot['currentDirection']),  # 19
                                centerY + d.conf['botRadius'] 
                                * bot['currentSpeed']/100 * (19.0 / 24.0) * math.sin(
                                    -bot['currentDirection']),
                                d.conf['botRadius'] * bot['currentSpeed']/100 * math.cos(-bot['currentDirection']) + centerX,  # 24
                                d.conf['botRadius'] * bot['currentSpeed']/100 * math.sin(-bot['currentDirection']) + centerY)

                d.canvas.coords(d.botTrackLeft[src],
                                centerX + d.conf['botRadius'] * (30.0 / 24.0)
                                * math.cos(-bot['currentDirection'] - math.pi / 4),
                                centerY + d.conf['botRadius'] * (30.0 / 24.0)
                                * math.sin(-bot['currentDirection'] - math.pi / 4),
                                d.conf['botRadius'] * (30.0 / 24.0) * math.cos(-bot['currentDirection']
                                                                                         - (3 * math.pi) / 4) + centerX,
                                d.conf['botRadius'] * (30.0 / 24.0) * math.sin(-bot['currentDirection']
                                                                                         - (3 * math.pi) / 4) + centerY)
                d.canvas.coords(d.botTrackRight[src],
                                centerX + d.conf['botRadius'] * (30.0 / 24.0)
                                * math.cos(-bot['currentDirection'] - (5 * math.pi) / 4),
                                centerY + d.conf['botRadius'] * (30.0 / 24.0)
                                * math.sin(-bot['currentDirection'] - (5 * math.pi) / 4),
                                d.conf['botRadius'] * (30.0 / 24.0)
                                * math.cos(-bot['currentDirection'] - (7 * math.pi) / 4) + centerX,
                                d.conf['botRadius'] * (30.0 / 24.0)
                                * math.sin(-bot['currentDirection'] - (7 * math.pi) / 4) + centerY)

                x2, y2 = nbmath.project(centerX, 0, bot['last']['fireCanonRequest']['direction'], 
                    d.conf['botRadius'] * 1.35)
                y2 = centerY - y2
                d.canvas.coords(d.botCanon[src], centerX, centerY, x2, y2)

                d.canvas.coords(d.botScan[src],
                                centerX - d.conf['botRadius'] * 1.5,
                                centerY - d.conf['botRadius'] * 1.5,
                                centerX + d.conf['botRadius'] * 1.5,
                                centerY + d.conf['botRadius'] * 1.5)
                d.canvas.itemconfigure(d.botScan[src], start=math.degrees(bot['last']['scanRequest']['startRadians']))
                extent = bot['last']['scanRequest']['endRadians'] - bot['last']['scanRequest']['startRadians']
                if extent < 0:
                    extent += math.pi*2
                d.canvas.itemconfigure(d.botScan[src], extent=math.degrees(extent))

                d.canvas.itemconfigure(d.botRequestedDirection[src], state='normal')
                d.canvas.itemconfigure(d.botCurrentDirection[src], state='normal')
                d.canvas.itemconfigure(d.botWidgets[src], state='normal')
                d.canvas.itemconfigure(d.botTrackLeft[src], state='normal')
                d.canvas.itemconfigure(d.botTrackRight[src], state='normal')
                d.canvas.itemconfigure(d.botScan[src], state='normal')
                d.canvas.itemconfigure(d.botCanon[src], state='normal')

        # remove shell widgets veiwer has but are not on server.
        for src in list(d.shellWidgets.keys()):
            if not src in msg['shells']:
                d.canvas.delete(d.shellWidgets[src][1])
                d.canvas.delete(d.shellWidgets[src][0])
                del d.shellWidgets[src]

        # add shell widgets server has that viewer doesn't
        for src in msg['shells']:
            if not src in d.shellWidgets:
                c = d.canvas.itemcget(d.botWidgets[src], 'fill')
                d.shellWidgets[src] = [
                    d.canvas.create_line(0, 0, 0, 0, width=2, arrow=t.LAST, fill=c),
                    d.canvas.create_line(0, 0, 0, 0, width=2, fill=c)
                    ]

        # update location of shell widgets
        for src in d.shellWidgets:
            centerX = msg['shells'][src]['x'] * d.scale + d.borderSize
            centerY = d.conf['arenaSize'] - msg['shells'][src]['y'] * d.scale + d.borderSize
            shellDir = msg['shells'][src]['direction']
            shell_item_1 = d.shellWidgets[src][0]
            d.canvas.coords(shell_item_1, centerX, centerY,
                            d.scale * 1 * math.cos(-shellDir) + centerX,
                            d.scale * 1 * math.sin(-shellDir) + centerY)
            shell_item_2 = d.shellWidgets[src][1]
            d.canvas.coords(shell_item_2, centerX, centerY,
                            d.scale * 10 * math.cos(-shellDir) + centerX,
                            d.scale * 10 * math.sin(-shellDir) + centerY)

        # remove explosion widgets viewer has but are not on server.
        for k in list(d.explWidgets.keys()):
            if not k in msg['explosions']:
                d.canvas.delete(d.explWidgets[k])
                del d.explWidgets[k]

        # reduce existing explosion size by 30% and turn off fill
        for k in d.explWidgets:
            bbox = d.canvas.bbox(d.explWidgets[k])
            d.canvas.coords(d.explWidgets[k],
                            bbox[0] + (bbox[2] - bbox[0]) * 0.85,
                            bbox[1] + (bbox[3] - bbox[1]) * 0.85,
                            bbox[2] - (bbox[2] - bbox[0]) * 0.85,
                            bbox[3] - (bbox[3] - bbox[1]) * 0.85)
            d.canvas.itemconfig(d.explWidgets[k], fill='')

        # add explosion widgets server has that viewer doesn't
        for k, expl in msg['explosions'].items():
            if not k in d.explWidgets:
                c = d.canvas.itemcget(d.botWidgets[expl['src']], 'fill')
                centerX = expl['x'] * d.scale + d.borderSize
                centerY = d.conf['arenaSize'] - expl['y'] * d.scale + d.borderSize
                explRadius = SrvData.getClassValue(d, 'explRadius', msg['bots'][expl['src']]['class'])
                d.explWidgets[k] = d.canvas.create_oval(centerX - explRadius,
                                                        centerY - explRadius,
                                                        centerX + explRadius,
                                                        centerY + explRadius,
                                                        fill=c, width=3, outline=c)

        # update game status widget
        d.statusWidget.config(text=d.conf['serverName'] +
                              "\n\nGame: " + str(msg['state']['gameNumber']) + " / " + str(d.conf['gamesToPlay']) +
                              "\nStep: " + str(msg['state']['gameStep']) + " / " + str(d.conf['stepMax']))

        # record the last time we got good view data from server.
        d.lastViewData = time.time()

    # server needs 1 every 10 seconds to keep us alive. Send every 2 secs to be sure.
    if time.time() > d.nextKeepAlive:
        d.viewerSocket.sendMessage({'type': 'viewKeepAlive'}, d.srvIP, d.srvPort)
        d.nextKeepAlive += 1

    # wait during instant replay
    if d.isReplaying:
        # Wait two steps before updating screen.
        wakeat = int(d.replayStepSec * d.replaySaveEveryNth * 1000)

    # normal wait
    else:
        # Wait two steps before updating screen.
        wakeat = int(d.conf['stepSec'] * 1000)

    d.window.after(wakeat, checkForUpdates, d)