Пример #1
0
    def __init__(self, name, ip, port, serverip, serverport, mydata, friendsData):

        threading.Thread.__init__(self)

        self.name = name
        self.mydata = mydata
        self.friendsData = friendsData

        log(name + ": Running!")

        try:
            botSocket = nbipc.NetBotSocket(ip, port, serverip, serverport)
            self.botSocket = botSocket

            joinReply = botSocket.sendRecvMessage(
                {'type': 'joinRequest', 'name': name}, retries=300, delay=1, delayMultiplier=1)
            self.srvConf = joinReply['conf']

        except nbipc.NetBotSocketException as e:
            log(name + ": Is netbot server running at" + args.serverIP + ":" + str(args.serverPort) + "?")
            log(str(e), name + ": FAILURE")
            quit()

        log(name + ": Join server was successful. We are ready to play!")
        log(name + ": " + str(self.srvConf), "VERBOSE")
Пример #2
0
def sendToViwers(d):
    if len(d.viewers) == 0:
        return

    startTime = time.perf_counter()

    now = time.time()
    bmsg = d.srvSocket.serialize({
        'type': 'viewData',
                'state': d.state,
                'bots': d.bots,
                'shells': d.shells,
                'explosions': d.explosions
        })
    for src in list(d.viewers.keys()):  # we need a list of keys so we can del from the viewers dict below
        v = d.viewers[src]
        if v['lastKeepAlive'] + 10 < now:
            del d.viewers[src]
            log("Viewer " + src + " didn't send keep alive in last 10 secs and was removed.")
        else:
            try:
                # sending with a prepacked message makes it faster to send to a lot of viewers.
                d.srvSocket.sendMessage(bmsg, v['ip'], v['port'], packedAndChecked=True)
            except Exception as e:
                log(str(e), "ERROR")
                
    d.state['viewerMsgTime'] += time.perf_counter() - startTime
Пример #3
0
def initGame(d):
    """
    reset game state
    """
    d.state['gameNumber'] += 1
    log("Starting Game " + str(d.state['gameNumber']))

    d.state['gameStep'] = 0
    
    """
    for each bot
        reset health 100
        reset speed and direction values to 0
    """
    for src, bot in d.bots.items():
        bot['health'] = 100
        bot['currentSpeed'] = 0
        bot['requestedSpeed'] = 0
        bot['currentDirection'] = 0
        bot['requestedDirection'] = 0

    # set starting location
    start = d.starts.pop()
    for i in range(d.conf['botsInGame']):
        src = d.startBots[i]
        d.bots[src]['x'] = d.startLocs[start[i]]['x']
        d.bots[src]['y'] = d.startLocs[start[i]]['y']

    # delete all shells and explosions.
    d.shells = {}
    d.explosions = {}
Пример #4
0
def mkObstacles(d, n):
    '''
    Randomly lay out obstacles with so they are at least 2 and a bit bot diameters away from any wall or other obstacle.
    '''
    obstacles = []
    rad = d.conf['arenaSize'] * d.conf['obstacleRadius'] / 100.0

    for i in range(n):
        overlaps = True
        attempts = 0
        while overlaps:
            attempts += 1
            new = {
                'x': random.random() * (d.conf['arenaSize'] - rad * 8.1) + rad * 4.1,
                'y': random.random() * (d.conf['arenaSize'] - rad * 8.1) + rad * 4.1,
                'radius': rad
                }
            overlaps = False
            for o in obstacles:
                if nbmath.distance(o['x'], o['y'], new['x'], new['y']) < o['radius'] + \
                        new['radius'] + d.conf['botRadius'] * 4.1:
                    overlaps = True
                    break
            if overlaps == False:
                obstacles.append(new)
            else:
                log("Obstacle overlapped during random layout. Trying again.", "VERBOSE")
                if attempts > 999:
                    log("Could not layout obstacles without overlapping.", "FAILURE")
                    quit()

    return obstacles
Пример #5
0
def rundivision(divisionDir, botkeys):
    global bots

    log("Running Division: " + divisionDir)
    os.mkdir(divisionDir)

    srvProc = startserver(divisionDir)

    botProcs = []
    for botkey in botkeys:
        botProcs.append(startbot(divisionDir, botkey))

    time.sleep(2)

    #ensure all botProcs still running (make sure we did not have a startup problem.)
    botDead = False
    for bot in botProcs:
        if bot.poll() != None:
            botDead = True

    if botDead == False:
        #wait for server to quit
        srvProc.wait()
    else:
        log("BOT DIED EARLY!!!", "ERROR")
        if srvProc.poll() == None:
            os.kill(srvProc.pid, signal.SIGINT)
        time.sleep(1)
        if srvProc.poll() == None:
            log("Needed to terminate server.", "WARNING")
            srvProc.terminate()

    #try to kill bots nicely
    for bot in botProcs:
        if bot.poll() == None:
            os.kill(bot.pid, signal.SIGINT)

    time.sleep(2)

    #kill all robots
    for bot in botProcs:
        if bot.poll() == None:
            log("Needed to terminate bot.", "WARNIGN")
            bot.terminate()

    time.sleep(2)

    closeFiles()

    # if results.json has been created then load results else log error.
    jsonFile = os.path.join(divisionDir,"results.json")
    if os.path.isfile(jsonFile):
        with open(jsonFile) as json_file:
            results = json.load(json_file)
        botSort = sorted(results['bots'], key=lambda b: results['bots'][b]['points'], reverse=True)
        for i in range(len(botSort)):
            botkeys[i] = botSort[i]
    else:
        log("Server did not produce json file: " + jsonFile, "FAILURE")
        quit()
Пример #6
0
def joinRequest(d, msg, src):
    if src in d.bots:
        if d.conf['allowRejoin']:
            d.bots[src]['name'] = msg['name']
            result = "OK"
        else:
            result = "Bot at " + src + " is already in game. Can't join twice."
            log("Bot at " + src + " tried to join twice.")
    elif src in d.viewers:
        result = "Viewers are not allowed to be bots."
        log("Viewer from " + src + " tried to join as bot.")
    elif len(d.bots) >= d.conf['botsInGame']:
        result = "Game is full. No more bots can join."
        log("Bot from " + src + " tried to join full game.")
    else:
        d.bots[src] = copy.deepcopy(d.botTemplate)
        d.bots[src]['name'] = msg['name']
        d.startBots.append(src)
        result = "OK"
        log("Bot joined game: " + d.bots[src]['name'] + " (" + src + ")")

    log("Bots in Game: " + str(d.bots), "VERBOSE")

    if result == "OK":
        return {'type': "joinReply", 'conf': d.conf}
    else:
        return {'type': 'Error', 'result': result}
Пример #7
0
    def recvMessage(self):
        """
        Check the socket receive buffer and returns message, ip, and port only
        if a valid message is immediately ready to receive. recvMessage is
        considered asynchronous because it will not wait for a message to arrive
        before raising an exception.

        Returns msg, ip, port.
              msg: valid message (see Messages below)
              ip: IP address of the sender
              port: port of the sender

        If the reply is an “Error” message then it will be returned just like
        any other message. No exception will be raised.

        If msg is not a valid message (see Messages below) then raises
        NetBotSocketException.

        Immediately raises NetBotSocketException if the receive buffer is empty.

        Note, the text above assumes the socket timeout is set to 0
        (non-blocking), which is the default in NetBotSocket.

        """
        try:
            bytesAddressPair = self.s.recvfrom(self.bufferSize)
            # Convert data from network binary format to python objects
            msg = self.deserialize(bytesAddressPair[0])
            ip = bytesAddressPair[1][0]
            port = bytesAddressPair[1][1]
            log(
                "Received msg from " + ip + ":" + str(port) + " len=" +
                str(len(bytesAddressPair[0])) + " bytes " + str(msg), "DEBUG")

            self.recv = self.recv + 1
            if msg['type'] in self.recvTypes:
                self.recvTypes[msg['type']] += 1
            else:
                self.recvTypes[msg['type']] = 1
        except (BlockingIOError, socket.timeout):
            # There was no data in the receive buffer.
            raise NetBotSocketException("Receive buffer empty.")
        except (ConnectionResetError):
            # Windows raises this when it gets back an ICMP destination unreachable packet
            log(
                "The destination ip:port returned ICMP destination unreachable. Is the destination running?",
                "WARNING")
            raise NetBotSocketException(
                "The destination ip:port returned ICMP destination unreachable. Is the destination running?"
            )

        if not isValidMsg(msg):
            raise NetBotSocketException("Received message invalid format.")

        # If we get a joinReply then use the server conf to tune our send delay in sendRecvMessage()
        if msg['type'] == 'joinReply':
            self.setDelay(msg['conf']['stepSec'] * 2)

        return msg, ip, port
Пример #8
0
def startserver(divisionDir):
    global fd
    f = open(os.path.join(divisionDir,"server.output.txt"), "w")
    fd.append(f)
    cmdline = pythoncmd + srvoptions + [os.path.join(divisionDir,"results.json")]
    log(cmdline, "DEBUG")
    p = subprocess.Popen(cmdline, stdout=f, stderr=subprocess.STDOUT)
    return p
Пример #9
0
    def sendMessage(self, msg, destinationIP=None, destinationPort=None, packedAndChecked=False):
        """
        Sends msg to destinationIP:destinationPort and then returns immediately.
        sendMessage is considered asynchronous because it does not wait for a
        reply message and returns no value. Therefore there is no indication if
        msg will be received by the destination.

        msg must be a valid message (see Messages below). Raises
        NetBotSocketException exception if the msg does not have a valid format.

        If destinationIP or destinationPort is not provided then the default will
        be used (see setDestinationAddress()).

        If packedAndChecked is True then msg is assumed to already be serialized
        and no other checks will be done.

        """

        if destinationIP is None:
            destinationIP = self.destinationIP

        if destinationPort is None:
            destinationPort = self.destinationPort

        if not packedAndChecked:
            if not isValidMsg(msg):
                raise NetBotSocketException("Could not send because msg is not valid format.")
            if not isValidIP(destinationIP):
                raise NetBotSocketException("Could not send because destinationIP is not valid format.")
            if not isValidPort(destinationPort):
                raise NetBotSocketException("Could not send because destinationPort is not valid format.")

            # Convert data from python objects to network binary format
            networkbytes = self.serialize(msg)
        else:
            networkbytes = msg

        log("Sending msg to " + destinationIP + ":" + str(destinationPort) +
            " len=" + str(len(networkbytes)) + " bytes " + str(msg), "DEBUG")
        self.s.sendto(networkbytes, (destinationIP, destinationPort))

        dest = formatIpPort(destinationIP, destinationPort)
        if dest in self.sent:
            self.sent[dest] += 1
        else:
            self.sent[dest] = 1

        if not packedAndChecked:
            msgtype = msg['type']
        else:
            msgtype = "Serialized"

        if dest not in self.sendTypes:
            self.sendTypes[dest] = {}
        if msgtype in self.sendTypes[dest]:
            self.sendTypes[dest][msgtype] += 1
        else:
            self.sendTypes[dest][msgtype] = 1
Пример #10
0
def isValidIP(ip):
    """ Returns True if ip is valid IP address, otherwise returns false. """
    if not isinstance(ip, str):
        log("IP is type " + str(type(ip)) + " but must be type str.", "ERROR")
        return False
    if not re.match(r'^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$', ip):
        log("IP address has bad format, expected something like 'int.int.int.int' but got " + ip, "ERROR")
        return False
    return True
Пример #11
0
def quit(signal=None, frame=None):
    global leader, follower

    # tell bots to return (stop thread)
    for bot in (leader, follower):
        if bot.isAlive():
            bot.stop = True

    log("Quiting", "INFO")
Пример #12
0
def startbot(divisionDir, botkey):
    global fd, robotsDir, bots
    bot = bots[botkey]
    f = open(os.path.join(divisionDir, bot['file'] + ".output.txt"), "w")
    fd.append(f)
    cmdline = pythoncmd + [os.path.join(robotsDir, bot['file']), '-p', str(bot['port']),'-sp','20000']
    log(cmdline, "DEBUG")
    p = subprocess.Popen(cmdline, stdout=f, stderr=subprocess.STDOUT)
    return p
Пример #13
0
def isValidPort(p):
    """ Returns True if p is valid port number, otherwise returns false. """

    if not isinstance(p, int):
        log("Port is type " + str(type(p)) + " but must be type int.", "ERROR")
        return False
    if p < 1 or p > 65000:
        log("Port is out of valid range 0-65000: " + str(p), "ERROR")
        return False
    return True
Пример #14
0
def createController(ip, port, sip, sp):
    d = ViewerData()

    # POSSIBLE BUG (if you get triggered at the smallest "issues"):
    # log level for viewer are the same as they are for the bot.
    # setLogLevel(args.debug, args.verbose)
    # d.srvIP = args.serverIP
    # d.srvPort = args.serverPort
    d.srvIP = sip
    d.srvPort = sp

    log("Registering with Server: " + d.srvIP + ":" + str(d.srvPort) +
        " (this could take a few seconds)")

    try:
        d.viewerSocket = nbipc.NetBotSocket(ip, port, sip, sp)

        # this step is reeeeeally slow for some reason... must investigate
        reply = d.viewerSocket.sendRecvMessage({'type': 'addViewerRequest'})
        d.conf = reply['conf']
        log("Server Configuration: " + str(d.conf), "VERBOSE")
    except Exception as e:
        log(str(e), "FAILURE")
        quit()

    log("Server registration successful. Opening Window.")

    openWindow(d)

    return d
Пример #15
0
def recvReplyMsgs(d):
    # process all messages in socket recv buffer
    startTime = time.perf_counter()
    msgQ = []
    more = True
    while more:
        try:
            msgQ.append(d.srvSocket.recvMessage())
        except nbipc.NetBotSocketException as e:
            more = False
        except Exception as e:
            log(str(type(e)) + " " + str(e), "ERROR")
            more = False

    botMsgCount = {}
    for msg, ip, port in msgQ:

        src = nbipc.formatIpPort(ip, port)

        # Track src counter and drop msg if we have already proccessed the max msgs for this src this step
        if src in botMsgCount:
            botMsgCount[src] += 1
        else:
            botMsgCount[src] = 1
        if botMsgCount[src] > d.conf['botMsgsPerStep']:
            continue
        
        if dropMessage(d):
            continue

        reply = processMsg(d, msg, src)
        if reply:
            if dropMessage(d):
                continue
            try:
                d.srvSocket.sendMessage(reply, ip, port)
            except Exception as e:
                log(str(e), "ERROR")

    if d.state['gameNumber'] > 0: # Don't count missed steps while waiting for bots to join.
        for src in d.bots:
            if src not in botMsgCount:
                d.bots[src]['missedSteps'] += 1

    d.state['msgTime'] += time.perf_counter() - startTime
Пример #16
0
def addViewerRequest(d, msg, src):
    if src in d.bots:
        return {
            'type': 'Error',
            'result': "Bots are not allowed to be viewers."
        }
        log("Bot from " + src + " tried to join as viewer.")
    elif src in d.viewers:
        d.viewers[src]['lastKeepAlive'] = time.time()
    else:
        ipPort = re.split('[-:]', src)  # create [str(ip),str(port)]
        d.viewers[src] = {
            'lastKeepAlive': time.time(),
            'ip': ipPort[0],
            'port': int(ipPort[1])
        }
        log("Viewer started watching game: " + src)

    return {'type': "addViewerReply", 'conf': d.conf}
Пример #17
0
def mkStartLocations(d):
    while len(d.starts) < d.conf['gamesToPlay']:
        botsOverlap = True  # loop must run at least once.
        botsObsOverlap = True
        attempts = 0
        while botsOverlap or botsObsOverlap:
            startLocs = []
            for i in range(d.conf['botsInGame']):
                loc = {}
                loc['x'] = random.random() * (d.conf['arenaSize'] * 0.8) + (d.conf['arenaSize'] * 0.1)
                loc['y'] = random.random() * (d.conf['arenaSize'] * 0.8) + (d.conf['arenaSize'] * 0.1)
                startLocs.append(loc)

            botsOverlap = findOverlapingBots(d, startLocs)
            botsObsOverlap = findOverlapingBotsAndObstacles(d, startLocs)
            if botsOverlap:
                log("Bots overlapped during random layout, trying again.", "VERBOSE")
            elif botsObsOverlap:
                log("Bots overlapped obstacles during random layout, trying again.", "VERBOSE")

            attempts += 1
            if attempts > 999:
                log("Could not layout bots without overlapping.", "FAILURE")
                quit()

        d.startLocs.extend(startLocs)
        locIndexes = range(len(d.startLocs) - d.conf['botsInGame'], len(d.startLocs))
        if d.conf['startPermutations']:
            startLocsPerms = itertools.permutations(locIndexes)
            d.starts.extend(startLocsPerms)
        else:
            d.starts.append(list(locIndexes))

    random.shuffle(d.starts)
Пример #18
0
    def run(self):
        name = self.name
        botSocket = self.botSocket
        srvConf = self.srvConf
        mydata = self.mydata
        friendsData = self.friendsData

        log(name + ": Running!")

        self.stop = False  # when this becomes True the run method must return.
        while not self.stop:
            try:
                # Store my location in mydata so friend can see it.
                getLocationReply = botSocket.sendRecvMessage({'type': 'getLocationRequest'})
                mydata.x = getLocationReply['x']
                mydata.y = getLocationReply['y']

                # Compute distance to friend and set speed based on distance (slower as we get closer).
                distanceToFriend = nbmath.distance(mydata.x, mydata.y, friendsData.x, friendsData.y)
                botSocket.sendRecvMessage({'type': 'setSpeedRequest',
                                           'requestedSpeed': min(100, distanceToFriend / 1000 * 100)})

                # Compute angle to friend and go in that direction.
                angleToFriend = nbmath.angle(mydata.x, mydata.y, friendsData.x, friendsData.y)
                botSocket.sendRecvMessage({'type': 'setDirectionRequest', 'requestedDirection': angleToFriend})

                log(f"{name}: Distance to friend == {distanceToFriend:>4.2f}, Angle to friend == {angleToFriend:>4.2f},", "INFO")

            except nbipc.NetBotSocketException as e:
                log(name + ": " + str(e), "WARNING")
                continue
Пример #19
0
    def run(self):
        name = self.name
        botSocket = self.botSocket
        srvConf = self.srvConf
        mydata = self.mydata
        friendsData = self.friendsData

        log(name + ": Running!")

        self.stop = False  # when this becomes True the run method must return.
        while not self.stop:
            try:
                # Store my location in mydata so friend can see it.
                getLocationReply = botSocket.sendRecvMessage({'type': 'getLocationRequest'})
                mydata.x = getLocationReply['x']
                mydata.y = getLocationReply['y']

                # Compute distance to friend.
                distanceToFriend = nbmath.distance(mydata.x, mydata.y, friendsData.x, friendsData.y)
                log(f"{name}: Distance to friend == {distanceToFriend:>4.2f}", "INFO")

                if distanceToFriend > 300:
                    # wait for friend to catch up.
                    botSocket.sendRecvMessage({'type': 'setSpeedRequest', 'requestedSpeed': 0})
                    log(name + ": Waiting for friend to get closer.", "INFO")
                else:
                    getSpeedReply = botSocket.sendRecvMessage({'type': 'getSpeedRequest'})
                    if getSpeedReply['requestedSpeed'] == 0:
                        radians = random.random() * 2 * math.pi
                        botSocket.sendRecvMessage({'type': 'setDirectionRequest', 'requestedDirection': radians})
                        botSocket.sendRecvMessage({'type': 'setSpeedRequest', 'requestedSpeed': 100})
                        log(f"{name}: Requested to go {radians:>4.2f} radians at max speed.", "INFO")

            except nbipc.NetBotSocketException as e:
                log(name + ": " + str(e), "WARNING")
                continue
Пример #20
0
def main():
    global d
    
    d = ViewerData()

    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter,
                                    epilog="Note: pressing the space bar activates a 7 second long instant replay at the default step speed. "
                                     + "Live action will resume after the replay completes.")
    parser.add_argument('-ip', metavar='My IP', dest='myIP', type=nbipc.argParseCheckIPFormat, nargs='?',
                        default='127.0.0.1', help='My IP Address')
    parser.add_argument('-p', metavar='My Port', dest='myPort', type=int, nargs='?',
                        default=20010, help='My port number')
    parser.add_argument('-sip', metavar='Server IP', dest='serverIP', type=nbipc.argParseCheckIPFormat, nargs='?',
                        default='127.0.0.1', help='Server IP Address')
    parser.add_argument('-sp', metavar='Server Port', dest='serverPort', type=int, nargs='?',
                        default=20000, help='Server port number')
    parser.add_argument('-randcolors', dest='randomColors', action='store_true',
                        default=False, help='Randomizes bot colors in viewer')
    parser.add_argument('-debug', dest='debug', action='store_true',
                        default=False, help='Print DEBUG level log messages.')
    parser.add_argument('-verbose', dest='verbose', action='store_true',
                        default=False, help='Print VERBOSE level log messages. Note, -debug includes -verbose.')
    args = parser.parse_args()
    setLogLevel(args.debug, args.verbose)
    d.srvIP = args.serverIP
    d.srvPort = args.serverPort

    log("Registering with Server: " + d.srvIP + ":" + str(d.srvPort))

    try:
        d.viewerSocket = nbipc.NetBotSocket(args.myIP, args.myPort, d.srvIP, d.srvPort)
        reply = d.viewerSocket.sendRecvMessage({'type': 'addViewerRequest'}, retries=60, delay=1, delayMultiplier=1)
        d.conf = reply['conf']
        log("Server Configuration: " + str(d.conf), "VERBOSE")
    except Exception as e:
        log(str(e), "FAILURE")
        quit()

    log("Server registration successful. Opening Window.")
    
    if args.randomColors:
        random.shuffle(d.colors)
        
    openWindow(d)
Пример #21
0
def main():
    global leader, follower  # This is global so quit() can access them.

    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('-ip', metavar='My IP', dest='myIP', type=nbipc.argParseCheckIPFormat, nargs='?',
                        default='127.0.0.1', help='My IP Address')
    parser.add_argument('-p', metavar='My Port', dest='myPort', type=int, nargs='?',
                        default=20010, help='My port number')
    parser.add_argument('-sip', metavar='Server IP', dest='serverIP', type=nbipc.argParseCheckIPFormat, nargs='?',
                        default='127.0.0.1', help='Server IP Address')
    parser.add_argument('-sp', metavar='Server Port', dest='serverPort', type=int, nargs='?',
                        default=20000, help='Server port number')
    parser.add_argument('-debug', dest='debug', action='store_true',
                        default=False, help='Print DEBUG level log messages.')
    parser.add_argument('-verbose', dest='verbose', action='store_true',
                        default=False, help='Print VERBOSE level log messages. Note, -debug includes -verbose.')
    args = parser.parse_args()
    setLogLevel(args.debug, args.verbose)

    # Create shared data.
    leaderData = sharedData()
    followerData = sharedData()

    # Create robot thread objects, each need to use a different port number since they both will open a socket.
    robotPort = args.myPort
    leader = Leader("Team Leader", args.myIP, robotPort, args.serverIP, args.serverPort, leaderData, followerData)
    robotPort += 1
    follower = Follower("Team Follower", args.myIP, robotPort, args.serverIP, args.serverPort, followerData, leaderData)

    # Start threads. This will call the run() method.
    leader.start()
    follower.start()

    # Wait for leader and follower to both end
    while leader.isAlive() or follower.isAlive():
        time.sleep(1)

    # Both threads have returned. Print stats and exit.
    for bot in (leader, follower):
        log("=====================================")
        log("=== " + bot.name)
        log("=====================================")
        log(bot.botSocket.getStats())
    exit()
Пример #22
0
    def __init__(self,
                 sourceIP,
                 sourcePort,
                 destinationIP='127.0.0.1',
                 destinationPort=20000):
        """
        Create and bind UDP socket and bind it to listen on sourceIP and sourcePort.

        sourceIP: IP the socket will listen on. This must be 127.0.0.1 (locahost), 0.0.0.0 (all interfaces), or a valid IP address on the computer.
        sourcePort: port to listen on. This is an integer number.
        destinationIP and destinationPort are stored with setDestinationAddress()


        Returns NetBotSocket object.

        Raises socket related exceptions.
        """

        self.sent = 0  # Number of messages sent to OS socket
        self.recv = 0  # Number of messages recv from OS socket
        self.sendRecvMessageCalls = 0  # Number of calls to sendRecvMessage
        self.sendRecvMessageResends = 0  # Number of resends made by sendRecvMessage
        self.sendRecvMessageTime = 0  # Total time in sendRecvMessage
        self.sendTypes = {}
        self.recvTypes = {}

        self.sendrecvDelay = 0.1

        self.sourceIP = sourceIP
        self.sourcePort = sourcePort
        log(
            "Creating socket with sourceIP=" + sourceIP + ", sourcePort=" +
            str(sourcePort), "VERBOSE")
        self.s = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
        try:
            self.s.bind((sourceIP, sourcePort))
            log("Source Socket Binding Successful. Listening on " +
                formatIpPort(sourceIP, sourcePort))
        except Exception as e:
            self.s.close()
            self.s = None
            log(
                "Source Socket Binding Failed. The source port may already be in use. Try another port.",
                "FAILURE")
            raise
        self.s.settimeout(0)
        self.destinationIP = destinationIP
        self.destinationPort = destinationPort
        self.bufferSize = 4096
        random.seed()
        self.msgID = random.randrange(0, 65000, 1)
Пример #23
0
def play(botSocket, srvConf):
    gameNumber = 0  # The last game number bot got from the server (0 == no game has been started)

    while True:
        try:
            # Get information to determine if bot is alive (health > 0) and if a new game has started.
            getInfoReply = botSocket.sendRecvMessage(
                {'type': 'getInfoRequest'})
        except nbipc.NetBotSocketException as e:
            # We are always allowed to make getInfoRequests, even if our health == 0. Something serious has gone wrong.
            log(str(e), "FAILURE")
            log("Is netbot server still running?")
            quit()

        if getInfoReply['health'] == 0:
            # we are dead, there is nothing we can do until we are alive again.
            continue

        if getInfoReply['gameNumber'] != gameNumber:
            # A new game has started. Record new gameNumber and reset any variables back to their initial state
            gameNumber = getInfoReply['gameNumber']
            log("Game " + str(gameNumber) + " has started. Points so far = " +
                str(getInfoReply['points']))

            ############################################################################
            # This is where code can go to reset variables for a new game.
            ############################################################################

        try:
            ############################################################################
            # This is where game playing code would go if we were not a "Sitting Duck".
            ############################################################################
            pass

        except nbipc.NetBotSocketException as e:
            # Consider this a warning here. It may simply be that a request returned
            # an Error reply because our health == 0 since we last checked. We can
            # continue until the next game starts.
            log(str(e), "WARNING")
            continue
Пример #24
0
def main():
    global botSocket  # This is global so quit() can print stats in botSocket
    global robotName

    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('-ip', metavar='My IP', dest='myIP', type=nbipc.argParseCheckIPFormat, nargs='?',
                        default='127.0.0.1', help='My IP Address')
    parser.add_argument('-p', metavar='My Port', dest='myPort', type=int, nargs='?',
                        default=20010, help='My port number')
    parser.add_argument('-sip', metavar='Server IP', dest='serverIP', type=nbipc.argParseCheckIPFormat, nargs='?',
                        default='127.0.0.1', help='Server IP Address')
    parser.add_argument('-sp', metavar='Server Port', dest='serverPort', type=int, nargs='?',
                        default=20000, help='Server port number')
    parser.add_argument('-debug', dest='debug', action='store_true',
                        default=False, help='Print DEBUG level log messages.')
    parser.add_argument('-verbose', dest='verbose', action='store_true',
                        default=False, help='Print VERBOSE level log messages. Note, -debug includes -verbose.')
    args = parser.parse_args()
    setLogLevel(args.debug, args.verbose)

    try:
        botSocket = nbipc.NetBotSocket(args.myIP, args.myPort, args.serverIP, args.serverPort)
        joinReply = botSocket.sendRecvMessage({'type': 'joinRequest', 'name': robotName}, retries=300, delay=1, delayMultiplier=1)
    except nbipc.NetBotSocketException as e:
        log("Is netbot server running at" + args.serverIP + ":" + str(args.serverPort) + "?")
        log(str(e), "FAILURE")
        quit()

    log("Join server was successful. We are ready to play!")

    # the server configuration tells us all about how big the arena is and other useful stuff.
    srvConf = joinReply['conf']
    log(str(srvConf), "VERBOSE")

    # Now we can play, but we may have to wait for a game to start.
    play(botSocket, srvConf)
Пример #25
0
def recvReplyMsgs(d):
    # process all messages in socket recv buffer
    startTime = time.perf_counter()
    msgQ = []
    more = True
    while more:
        try:
            msgQ.append(d.srvSocket.recvMessage())
        except nbipc.NetBotSocketException as e:
            more = False
        except Exception as e:
            log(str(type(e)) + " " + str(e), "ERROR")
            more = False

    botMsgCount = {}
    for msg, ip, port in msgQ:
        if dropMessage(d):
            continue

        src = nbipc.formatIpPort(ip, port)

        # Track src counter and drop msg if we have already proccessed the max msgs for this src this step
        if src in botMsgCount:
            botMsgCount[src] += 1
        else:
            botMsgCount[src] = 1
        if botMsgCount[src] > d.conf['botMsgsPerStep']:
            continue

        reply = processMsg(d, msg, src)
        if reply:
            if dropMessage(d):
                continue
            try:
                d.srvSocket.sendMessage(reply, ip, port)
            except Exception as e:
                log(str(e), "ERROR")

    d.state['msgTime'] += time.perf_counter() - startTime
    log("Msgs Processed per Bot this step: " + str(botMsgCount), "DEBUG")
Пример #26
0
def isValidMsg(msg):
    """ Returns True if msg is a valid message, otherwise returns false. """

    global MsgDef

    if not isinstance(msg, dict):
        log("Msg is type " + str(type(msg)) + " but must be dict type: " + str(msg), "ERROR")
        return False
    if not 'type' in msg:
        log("Msg does not contain 'type' key: " + str(msg), "ERROR")
        return False

    unvalidedFields = list(msg.keys())
    # type is validated below as part of loop so does not need specific validation.
    unvalidedFields.remove('type')
    # msgId and replyData are always optional and have no specific format. So they are always valid if present.
    if 'msgID' in unvalidedFields:
        unvalidedFields.remove('msgID')
    if 'replyData' in unvalidedFields:
        unvalidedFields.remove('replyData')

    for msgtype, msgspec in MsgDef.items():
        if msgtype == msg['type']:
            for fld, fldspec in msgspec.items():
                if fld.endswith('_o'):
                    # remove magic suffix marking field as optional
                    fld = fld.rstrip('_o')
                    if fld not in msg:
                        # optional field is not present, which is valid.
                        continue
                elif fld not in msg:
                    log("Msg does not contain required '" + fld + "' key: " + str(msg), "ERROR")
                    return False
                if isinstance(fldspec, list):
                    if not isinstance(msg[fld], eval(fldspec[0])):
                        log("Msg '" + fld + "' key has value of type " + str(type(msg[fld])) +
                            " but expected " + fldspec[0] + ": " + str(msg), "ERROR")
                        return False
                    if fldspec[0] is 'str':
                        if len(msg[fld]) < fldspec[1] or len(msg[fld]) > fldspec[2]:
                            log("Msg '" + fld + "' key has a string value " + str(msg[fld]) +
                                " with length out of range [" + str(fldspec[1]) + "," +
                                str(fldspec[2]) + "] : " + str(msg), "ERROR")
                            return False
                    elif msg[fld] < fldspec[1] or msg[fld] > fldspec[2]:
                        log("Msg '" + fld + "' key has a value " + str(msg[fld]) +
                            " which is out of range [" + str(fldspec[1]) + "," +
                            str(fldspec[2]) + "] : " + str(msg), "ERROR")
                        return False
                else:
                    if not isinstance(msg[fld], eval(fldspec)):
                        log("Msg '" + fld + "' key has value of type " + str(type(msg[fld])) +
                            " but expected " + fldspec + ": " + str(msg), "ERROR")
                        return False
                unvalidedFields.remove(fld)
            # All fields defined for message type have now been examined and are valid
            if len(unvalidedFields):
                # message has fields it should not have.
                log("Msg contains field(s) " + str(unvalidedFields) + " which is not defined for message type " + msg['type'] + ": " + str(msg), "ERROR")
                for fld in unvalidedFields:
                    if fld.endswith('_o'):
                        log("Optional message fields should not include '_o' suffix in field name.", "WARNING")
                        break
                return False
            else:
                # message is valid and has no extra fields.
                return True
    log("Msg 'type' key has value '" + str(msg['type']) + "' which is not known: " + str(msg), "ERROR")
    return False
Пример #27
0
    def sendRecvMessage(self, msg, destinationIP=None, destinationPort=None,
                        retries=10, delay=None, delayMultiplier=1.2):
        """
        Sends msg to destinationIP:destinationPort and then returns the reply.
        sendRecvMessage is considered synchronous because it will not return
        until and unless a reply is received. Programmers can this of this much
        like a normal function call.

        msg must be a valid message (see Messages below)

        If destinationIP or destinationPort is not provided then the default will
        be used (see setDestinationAddress()).

        If the reply is an “Error” message then a NetBotSocketException exception
        will be raised.

        If no reply is received then the message will be sent again (retried) in
        case it was dropped by the network. If the maximum number of retries is
        reached then a NetBotSocketException exception will be raised.

        Raises NetBotSocketException exception if the msg does not hae a valid format.
        """

        startTime = time.perf_counter()
        self.sendRecvMessageCalls += 1

        if destinationIP is None:
            destinationIP = self.destinationIP

        if destinationPort is None:
            destinationPort = self.destinationPort

        if delay:
            nextDelay = delay
        else:
            nextDelay = self.sendrecvDelay

        remaining = retries

        self.msgID = self.msgID + 1
        if self.msgID > 65000:
            self.msgID = 0

        msg['msgID'] = self.msgID

        gotReply = False
        sendMessage = 0
        while remaining != 0 and gotReply == False:
            if sendMessage <= time.perf_counter():
                self.sendMessage(msg, destinationIP, destinationPort)
                if sendMessage != 0:
                    self.sendRecvMessageResends += 1
                remaining = remaining - 1
                sendMessage = time.perf_counter() + nextDelay
                self.s.settimeout(nextDelay)
                nextDelay = nextDelay * delayMultiplier

            try:
                replyMsg, ip, port = self.recvMessage()
            except NetBotSocketException as e:
                # We didn't get anything from the buffer or it was an invalid message.
                ip = None

            if ip is not None:
                # if the message is the one we are looking for.
                if ip == destinationIP and port == destinationPort and \
                        isinstance(replyMsg, dict) and \
                        'msgID' in replyMsg and replyMsg['msgID'] == msg['msgID']:
                    gotReply = True

        self.s.settimeout(0)

        if not gotReply:
            log("Raising Exception NetBotSocketException because failed to get valid respose after " + str(retries) +
                " retries with delay = " + str(delay) + " and delayMultiplier = " + str(delayMultiplier), "VERBOSE")
            raise NetBotSocketException("Failed to get valid respose.")

        if replyMsg['type'] == "Error":
            log("Raising Exception NetBotSocketException because reply message, with correct msgID was of type Error.",
                "VERBOSE")
            raise NetBotSocketException("Received Error Message: " + replyMsg['result'])

        del replyMsg['msgID']

        self.sendRecvMessageTime += time.perf_counter() - startTime
        return replyMsg
Пример #28
0
def play(botSocket, srvConf):
    gameNumber = 0  # The last game number bot got from the server (0 == no game has been started)

    while True:
        try:
            # Get information to determine if bot is alive (health > 0) and if a new game has started.
            getInfoReply = botSocket.sendRecvMessage(
                {'type': 'getInfoRequest'})
        except nbipc.NetBotSocketException as e:
            # We are always allowed to make getInfoRequests, even if our health == 0. Something serious has gone wrong.
            log(str(e), "FAILURE")
            log("Is netbot server still running?")
            quit()

        if getInfoReply['health'] == 0:
            # we are dead, there is nothing we can do until we are alive again.
            continue

        if getInfoReply['gameNumber'] != gameNumber:
            # A new game has started. Record new gameNumber and reset any variables back to their initial state
            gameNumber = getInfoReply['gameNumber']
            log("Game " + str(gameNumber) + " has started. Points so far = " +
                str(getInfoReply['points']))

            # We only try and go to a corner at the beginning of the game and then hope for the best.
            try:
                # get location data from server
                getLocationReply = botSocket.sendRecvMessage(
                    {'type': 'getLocationRequest'})

                # find the closest corner:
                if getLocationReply['x'] < srvConf['arenaSize'] / 2:
                    cornerX = 0
                else:
                    cornerX = srvConf['arenaSize']

                if getLocationReply['y'] < srvConf['arenaSize'] / 2:
                    cornerY = 0
                else:
                    cornerY = srvConf['arenaSize']

                # find the angle from where we are to the closest corner
                radians = nbmath.angle(getLocationReply['x'],
                                       getLocationReply['y'], cornerX, cornerY)

                # Turn in a new direction
                botSocket.sendRecvMessage({
                    'type': 'setDirectionRequest',
                    'requestedDirection': radians
                })

                # Request we start accelerating to max speed
                botSocket.sendRecvMessage({
                    'type': 'setSpeedRequest',
                    'requestedSpeed': 100
                })

                # log some useful information.
                degrees = str(int(round(math.degrees(radians))))
                log("Requested to go " + degrees + " degress at max speed.",
                    "INFO")

            except nbipc.NetBotSocketException as e:
                # Consider this a warning here. It may simply be that a request returned
                # an Error reply because our health == 0 since we last checked. We can
                # continue until the next game starts.
                log(str(e), "WARNING")
            continue
Пример #29
0
import socket
import random
import time
import re
import math
import argparse

from netbots_log import log

try:
    import msgpack as umsgpack
    log("Using binary python msgpack.")
except:
    import umsgpack
    log("Using pure python msgpack. Install binary msgpack for better performance.", "WARNING")

"""
**About Messages**

Every message is a python dict type with at least a type attribute (i.e. field) and a 
string value.

For example: { 'type': 'getInfoRequest'}

MsgDef below defines valid types and additional fields that must be included and 
can optionally be included based on type.

Optional fields end in '_o' which marks the fields as optional. The '_o' should not 
appear in the actual message, it is just a marker that the field is optional.
For example, a joinRequest message with optional class field would be:
Пример #30
0
def quit(signal=None, frame=None):
    global botSocket
    log(botSocket.getStats())
    log("Quiting", "INFO")
    exit()