예제 #1
0
class Optimizer:
    def __init__(self):
        configs = load_configs("Configs.json")
        value_configs = load_configs("Configs_ValueNN.json")
        policy_configs = load_configs("Configs_PolicyNN.json")

        #logging.basicConfig(format='%(asctime)s %(message)s', filename=configs["log_name"], level=logging.WARNING,
        #datefmt="%Y-%m-%d %H:%M:%S")
        #logging.info("Starting Optimizer.")

        self.width = configs["width"]
        self.height = configs["height"]
        self.buffer_size = configs["buffer_size"]

        self.filename = configs["data_filename"]
        self.experiment_name = "Deviations/" + configs["val_filename"]

        self.buffer = Buffer(self.buffer_size)
        self.env = BlockRelocation(self.height, self.width)
        #self.model = ValueNetwork(configs=value_configs)
        #self.policy_network = PolicyNetwork(configs=policy_configs)
        #self.combined_model = CombinedModel(configs=configs)
        #self.value_wrapper = EstimatorWrapper(self.model)
        #self.policy_wrapper = EstimatorWrapper(self.policy_network)
        self.value_net = ValueNetworkKeras(value_configs)
        self.policy_net = PolicyNetworkKeras(policy_configs)

        self.tree_searcher = TreeSearch(
            self.value_net, BlockRelocation(self.height, self.width),
            self.policy_net)
        self.tree_searcher.std_vals = load_obj(self.experiment_name)

        self.baseline_params = {
            "search_depth": 5,
            "epsilon": 0.1,
            "threshold": 0.01,
            "drop_percent": 0.25,
            "factor": 0.01
        }

        self.current_search_params = {
            "search_depth": 5,
            "epsilon": 0.1,
            "threshold": 0.05,
            "drop_percent": 0.3,
            "factor": 0.05
        }

        self.dfs_params_hq = {"stop_param": 4, "k": 12}
        self.dfs_params_fast = {"stop_param": 1, "k": 12}

        #logging.info("Start up process complete.")

    def create_training_example(self, permutations=True, units=8):
        if units < self.height * self.width:
            matrix = self.env.create_instance_random(units)
        else:
            matrix = self.env.create_instance(self.height, self.width)

        if units < 6:
            path = self.tree_searcher.find_path_2(matrix.copy())
        else:
            path = self.tree_searcher.find_path_dfs(matrix.copy())

        # In case the solver can't solve it with the given depth, this function is called again
        if not path:
            return self.create_training_example(permutations=permutations,
                                                units=units)

        try:
            data = self.tree_searcher.move_along_path(matrix.copy(), path)
        except TypeError:
            return self.create_training_example(permutations=permutations,
                                                units=units)

        if permutations:
            data = self.create_permutations(data)
        else:
            data = self.prepare_data_for_model(data)

        return data, len(path)

    def train_on_new_instances(self, num=1, units=10, perm=False, train=False):
        data_list = []
        step_list = []
        for ii in range(num):
            data, steps = self.create_training_example(permutations=perm,
                                                       units=units)
            data_list.append(data)
            step_list.append(steps)

        data = pd.concat(data_list, ignore_index=True, sort=False)

        self.calculate_deviations(data)

        #with open(self.filename, 'a') as f:
        #    data.to_csv(f, header=False, index=False)
        if train:
            train_data = data.sample(int(data.shape[0] / 3))
            self.value_net.train_df(train_data, epochs=2, validation=False)
            self.policy_net.train_df(train_data, epochs=2, validation=False)

        self.buffer.append(data)

    def calculate_deviations(self, data):
        data = data.copy()
        data["pred"] = list(self.value_net.predict_df(data))
        data["pred"] = data["pred"].apply(lambda x: x[0])
        data["Value"] = data["Value"].astype('int64')
        data["deviation"] = abs(data["pred"] - data["Value"])

        deviations = data.groupby("Value")["deviation"].mean()

        new_vals = deviations.to_dict()
        old_vals = self.tree_searcher.std_vals

        for key in new_vals.keys():
            if key not in old_vals:
                old_vals[key] = new_vals[key]
            else:
                old_vals[key] = (old_vals[key] + new_vals[key]) / 2

        self.tree_searcher.std_vals = old_vals
        save_obj(old_vals, self.experiment_name)
        return old_vals

    def test_deviations(self):
        for i in range(20):
            data, steps = self.create_training_example(units=9)
            print(steps)
        print("DONE")
        print(self.calculate_deviations(data))

    def prepare_data_for_model(self, data):
        # TODO DONT WANT TO CHANGE THE COLUMN NAME HERE
        data["StateRepresentation"] = data["StateRepresentation"].apply(
            lambda x: x.transpose().flatten())
        data.columns = ["Moves", "StateRepresentation", "Value"]
        data["MovesEncoded"] = data["Moves"].copy()
        data["MovesEncoded"] = data["MovesEncoded"].apply(
            lambda x: self.tree_searcher.move_to_hot_one_encoding(x))
        return data

    def create_permutations(self, df):
        df_list = []
        for i, row in df.iterrows():
            # creating representations
            rep = self.env.all_permutations_state(row.StateRepresentation)
            rep = list(rep)

            # creating value column
            val = [np.array(row.Value) for _ in range(len(rep))]

            # creating move and move_encoded columns
            moves = self.env.all_permutations_move(*row.Move)
            encoded = [
                self.tree_searcher.move_to_hot_one_encoding(m) for m in moves
            ]

            # creating the DataFrame
            temp_df = pd.DataFrame({
                "StateRepresentation": rep,
                "Value": val,
                "Moves": moves,
                "MovesEncoded": encoded
            })

            # removing duplicates
            temp_df["hashable_state"] = temp_df.StateRepresentation.apply(
                lambda x: x.tostring())
            temp_df = temp_df.drop_duplicates(subset="hashable_state")
            temp_df = temp_df.drop(columns="hashable_state")

            df_list.append(temp_df)

        final_df = pd.concat(df_list, ignore_index=True)
        return final_df

    def reinforce(self, iterations=20, units=12, instances=200, train=False):
        print("Starting reinforce with {} iterations and {} units.".format(
            iterations, units))
        start = time.time()
        for x in range(iterations):
            print("Iteration " + str(x + 1))
            self.train_on_new_instances(instances, units=units, train=train)

        end = time.time()
        print(end - start)
        with open("duration.txt", 'a+') as f:
            f.write(
                str(units) + "  " + str(iterations * instances) + "  " +
                str(end - start) + "\n")

    def train_and_update_models(self, epochs=20):
        data = self.buffer.get_sample(size=self.buffer.max_size)
        print("Training Policy Network ...")
        self.policy_net.train_df(data, epochs=epochs, validation=False)
        print("Training Value Network ...")
        self.value_net.train_df(data, epochs=epochs, validation=False)

    def train_on_csv(self, filename):
        data = pd.read_csv(filename)
        print(data.shape)
        data["StateRepresentation"] = data["StateRepresentation"].apply(
            lambda x: np.fromstring(x[1:-1], sep=" "))
        data["MovesEncoded"] = data["MovesEncoded"].apply(
            lambda x: np.fromstring(x[1:-1], sep=" "))

        data["hashed"] = data["StateRepresentation"].apply(
            lambda s: s.tostring())
        data = data.drop_duplicates(subset="hashed")
        data = data.drop(columns=["hashed"])
        data = data.reset_index(drop=True)
        print(data.shape)
        train_data, test_data = train_test_split(data,
                                                 shuffle=True,
                                                 test_size=0.1)

        for i in range(5):
            print("Currently on run {} of training.".format(i + 1))
            self.policy_net.train_df(train_data)
            self.value_net.train_df(train_data)

            print("Policy Network Statistics:")
            print(self.value_net.eval(test_data))

            print("Value Network Statistics:")
            print(self.policy_net.eval(test_data))
        print("Training finished!")

    def full_experiment(self):
        total_container = self.width * self.height
        for ii in range(5):
            self.reinforce(iterations=5, units=5, instances=200, train=True)
            self.train_and_update_models()

        for ii in range(6, total_container - 5):
            print("Training: Currently training on {} units.".format(ii))
            self.reinforce(iterations=20, units=ii, instances=2000)
            self.buffer.remove_duplicates()
            if ii % 3 == 0:
                data = self.buffer.get_sample(self.buffer.max_size,
                                              remove=True)
                self.policy_net.retrain_model(data)
                self.value_net.retrain_model(data)
                self.buffer.increase_max_size(0.1)
                del data
            else:
                self.train_and_update_models()

        for ii in range(total_container - 5, total_container + 1):
            print("Training: Currently training on {} units.".format(ii))
            self.reinforce(iterations=20, units=ii, instances=2000)
            self.buffer.remove_duplicates()
            data = self.buffer.storage
            self.policy_net.retrain_model(data)
            self.value_net.retrain_model(data)
            self.buffer.increase_max_size(0.1)

        for ii in range(10):
            print("Training with all units. Currently on iteration ",
                  str(ii + 1))
            bm = Benchmark()
            bm.benchmark_caserta()
            self.reinforce(iterations=10,
                           units=self.height * self.width,
                           instances=2000)
            self.buffer.remove_duplicates()
            self.train_and_update_models()

        # find best parameters
        # run experiment on test instances

    def produce_testing_data(self, filename, examples=10000, perm=False):
        data_list = []
        start = time.time()
        for e in range(examples):
            if e % 500 == 0 and e > 0:
                h = not os.path.isfile(filename)
                final_df = pd.concat(data_list)
                end = time.time()
                with open(filename, 'a+') as f:
                    final_df.to_csv(f, header=h, index=False)
                print(end - start)

                start = time.time()
                data_list = []

            data, length = self.create_training_example(permutations=perm,
                                                        units=self.height *
                                                        self.width)
            data_list.append(data)

        # in case number is not divisible by 500
        final_df = pd.concat(data_list)
        with open(filename, 'a') as f:
            final_df.to_csv(f, header=False, index=False)
예제 #2
0
class MinerProtocol(Protocol):
    """ implementation of the Minecraft protocol """
    def __init__(self, name):
        global debug
        self.log = debug
        self.bot = MinerBot(name, self.send)
        self.buffer = Buffer()
        self.world = World()
        self.packets = {
            #     ID: (function, format
            0x00: (self.onKeepAlive, Standard("Bi")),  # Keep alive
            0x01: (self.onLoginRequest, String("BiSqibbBB")),  # Login request
            0x02: (self.onHandshake, String("BS")),  # Handshake
            0x03: (self.onChat, String("BS")),  # Chat message
            0x04: (None, Standard("Bq")),  # Time update 
            0x05: (None, Standard("Bihhh")),  # Entity Equipment
            0x06: (self.onSpawnPosition, Standard("Biii")),  # Spawn position
            0x07: (None, Standard("Bii?")),  # Use entity  (C --> S)
            0x08: (self.onUpdateHealth, Standard("Bhhf")),  # Update health
            0x09: (None, Standard("Bbbbhl")),  # Respawn  (C --> S)
            0x0A: (None, Standard("B?")),  # Player  (C --> S)
            0x0B: (None, Standard("Bdddd?")),  # Player position  (C --> S)
            0x0C: (None, Standard("Bff?")),  # Player look  (C --> S)
            0x0D: (self.onPLayerPosition,
                   Standard("Bddddff?")),  # Player position & look
            0x0E: (None, Standard("Bbibib")),  #  Player digging  (C --> S)
            0x0F:
            (None, BlockSlot("BibibW")),  # Player block placement  (C --> S)
            0x10: (None, Standard("Bh")),  # Holding change  (C --> S)
            0x11: (None, Standard("Bibibi")),  # Use bed
            0x12: (None, Standard("Bib")),  # Animation
            0x13: (None, Standard("Bib")),  # Entity action  (C --> S)
            0x14: (self.onNamedEntitySpawn,
                   String("BiSiiibbh")),  # Named entity spawn
            0x15: (None, Standard("Bihbhiiibbb")),  # Pickup spawn
            0x16: (None, Standard("Bii")),  # Collect item
            0x17:
            (self.onAddObject, Standard("Bibiiiihhh")),  # Add object/vehicle
            0x18: (None, MetaEntity("BibiiibbE")),  # Mob spawn
            0x19: (None, String("BiSiiii")),  # Entity: painting
            0x1A: (None, Standard("Biiiih")),  # Experience Orb
            0x1B: (None, Standard("Bffff??")),  # Stance update
            0x1C: (None, Standard("Bihhh")),  # Entity velocity
            0x1D: (None, Standard("Bi")),  # Destroy entity
            0x1E: (None, Standard("Bi")),  # Entity
            0x1F: (self.onEntityRelativeMove,
                   Standard("Bibbb")),  # Entity relative move
            0x20: (None, Standard("Bibb")),  # Entity look
            0x21: (None, Standard("Bibbbbb")),  # Entity look and relative move
            0x22: (None, Standard("Biiiibb")),  # Entity teleport
            0x26: (None, Standard("Bib")),  # Entity status
            0x27: (None, Standard("Bii")),  # Attach entity
            0x28: (None, MetaEntity("BiE")),  # Entity metadata
            0x29: (None, Standard("Bibbh")),  # Entity Effect
            0x2A: (None, Standard("Bib")),  # Remove Entity Effect
            0x2B: (None, Standard("Bfhh")),  # Experience
            0x32: (self.onPreChunk, Standard("Bii?")),  # Pre chunk
            0x33: (self.onChunk, MetaChunk()),  # Map chunk
            0x34: (None, BlockArray()),  # Multi-block change
            0x35: (None, Standard("Bibibb")),  # Block change
            0x36: (None, Standard("Bihibb")),  # Block action
            0x3C: (None, BlockExplosion()),  # Explosion
            0x3D: (None, Standard("Biibii")),  # Sound effect
            0x46: (None, Standard("Bbb")),  # New/invalid state
            0x47: (None, Standard("Bi?iii")),  # Thunderbolt
            0x64: (None, String("BbbSb")),  # Open window
            0x65: (None, Standard("Bb")),  # Close window
            0x66: (None, BlockSlot("Bbhbh?W")),  # Window click
            0x67: (None, BlockSlot("BbhW")),  # Set slot
            0x68: (self.onWindowItems, Window()),  # Window items
            0x69: (None, Standard("Bbhh")),  # Update window property
            0x6A: (self.onTransaction, Standard("Bbh?")),  # Transaction
            0x6B: (None, BlockSlot("BhW")),  # Creative inventory action 
            0x6C: (None, Standard("Bbb")),  # Enchant Item
            0x82: (None, String("BihiSSSS")),  # Update sign
            0x83: (None, ByteArray()),  # item data
            0xC8: (None, Standard("Bib")),  # Increment statistic
            0xC9: (self.onPlayerList, String("BS?h")),  # Player List Item
            0xFF: (self.onDisconnected, String("BS")),  # Disconnect
        }

    def connectionMade(self):
        """ called when a connection has been established """
        self.log.info("<%s> connected" % self.bot.getName())
        #        self.writer = PacketWriter(self.transport)
        self.log.debug("ATTEMPTING TO HANDSHAKE")
        self.sendHandshake(self.bot.getName())

    def connectionLost(self, reason):
        self.log.error("connection lost %s" % reason)

    def dataReceived(self, data):
        """ new data are received """
        self.buffer.append(data)

        while True:
            # get the packet Type
            try:
                data = self.buffer.peek()[0]
                packetType = ord(data)
            except IOError:  # if empty buffer stop the loop
                break
            self.log.received(packetType)

            # get the packet information
            if packetType not in self.packets:
                self.log.error("Unknown packet 0x%02X" % packetType)
                self.transport.loseConnection()

            fct, fmt = self.packets[packetType]
            try:
                packet = fmt.unpack(self.buffer)
            except IOError:
                break

            # do action on packet received
            if fct != None:
                fct(self.bot, packet)

    def onDisconnected(self, bot, info):
        self.log.info("Disconnected by server. Reason=%s" % info[1])

    def onKeepAlive(self, bot, info):
        self.send(0x00, 0)

    def onUpdateHealth(self, bot, info):
        bot.setHealth(health=info[1], food=info[2], foodSaturation=info[3])

    def onLoginRequest(self, bot, info):
        bot.setUID(info[1])
        #bot.setMaxSeed(info[3])
        pass

    def onHandshake(self, bot, info):
        #bot.setConnectionHash(info[1])
        self.sendLoginRequest(22, self.bot.getName())
        self.bot.nameToUpper()

    def onSpawnPosition(self, bot, info):
        bot.setSpawnPosition(info)

    def onPLayerPosition(self, bot, info):
        cmd, x, stance, y, z, yaw, pitch, onGround = info
        bot.setPlayerPosition(x, y, z, stance, yaw, pitch, onGround)
        self.send(0x0D, x, y, stance, z, yaw, pitch, onGround)

    def onEntityRelativeMove(self, bot, info):
        bot.setEntityRelativeMove(info)

    def onWindowItems(self, bot, info):
        pass
#        if info[1] == 0:
#            # inventory window
#            bot.setInventory(info[2], info[3])
#        else:
#            logging.error("Unknown window number=%d" % info[1])

#    def onSetSlot(self, bot, info):
#        if info[1] == 0:
#            bot.setSlotItem(info[1], info[2], info[3], info[4], info[5])
#            logging.debug("SetSlot %d, slot %d" %(info[1], info[2]))

    def onNamedEntitySpawn(self, bot, info):
        bot.setPlayerSpawn(info)

    def onAddObject(self):
        pass

    def onPreChunk(self, bot, info):
        pass

    def onChunk(self, bot, info):
        cmd, x, y, z, sx, sy, sz, compSize, chunkData = info
        # correct the real size
        sx += 1
        sy += 1
        sz += 1
        self.log.chunk(
            "Chunk (x,y,z)=(%d, %d, %d) (sx,sy,sz)=(%d, %d, %d), len=%d" %
            (x, y, z, sx, sy, sz, len(chunkData)))
        self.world.addChunk(x, y, z, sx, sy, sz, chunkData)

    def onTransaction(self, bot, info):
        pass

    def send(self, cmd, *data):
        if cmd not in self.packets:
            self.log.error("Unknown packet to send 0x%02X" % cmd)
            self.transport.loseConnection()
        fct, pack = self.packets[cmd]
        res = pack.pack(cmd, *data)
        deb = ""
        for x in res:
            deb += "%02X " % ord(x)
        self.transport.write(res)
        self.log.send(cmd)

    def sendDisconnect(self, reason):
        self.send(0xFF, "Disconnection by client")
        self.transport.loseConnection()

    def sendHandshake(self, userName):
        self.send(0x02, userName)

    def sendLoginRequest(self, protocol_version, username):
        self.send(0x01, protocol_version, username, 0, 0, 0, 0, 0, 0)

    def onChat(self, bot, info):
        '''
        Chat received. Analyse it to detect a message from the player 
        '''
        info = info[1].upper()
        if ord(info[0]) == 0xA7:  #skip color code
            info = info[2:]
        # if the message is from a player it starts with <PlayerName>
        result = re.match("<(.*)>(.*)", info)
        if result == None:
            name = ""
            msg = info
        else:
            name = result.group(1)
            msg = result.group(2)
            result = msg.split()
            if BOT_NAME_NEEDED:  # Command starts with the targeted Bot name
                if result[0] == bot.getName():
                    bot.parseCommand(name, result[1:])
            else:
                if name <> bot.getName():  # do not compute self message
                    bot.parseCommand(name, result)

    def onPlayerList(self, bot, info):
        bot.updatePlayerList(info[1], info[2])
예제 #3
0
class MinerProtocol(Protocol):
    """ implementation of the Minecraft protocol """
    def __init__(self, name):
        global debug
        self.log = debug
        self.bot = MinerBot(name, self.send)
        self.buffer = Buffer()
        self.world = World()
        self.packets = {
        #     ID: (function, format
            0x00: (self.onKeepAlive, Standard("Bi")),           # Keep alive
            0x01: (self.onLoginRequest, String("BiSqibbBB")),   # Login request
            0x02: (self.onHandshake, String("BS")),             # Handshake
            0x03: (self.onChat, String("BS")),                  # Chat message
            0x04: (None, Standard("Bq")),                       # Time update 
            0x05: (None, Standard("Bihhh")),                    # Entity Equipment
            0x06: (self.onSpawnPosition, Standard("Biii")),     # Spawn position
            0x07: (None, Standard("Bii?")),                     # Use entity  (C --> S)
            0x08: (self.onUpdateHealth, Standard("Bhhf")),      # Update health
            0x09: (None, Standard("Bbbbhl")),                   # Respawn  (C --> S)
            0x0A: (None, Standard("B?")),                       # Player  (C --> S)
            0x0B: (None, Standard("Bdddd?")),                   # Player position  (C --> S)
            0x0C: (None, Standard("Bff?")),                     # Player look  (C --> S)
            0x0D: (self.onPLayerPosition, Standard("Bddddff?")), # Player position & look
            0x0E: (None, Standard("Bbibib")),                   #  Player digging  (C --> S)
            0x0F: (None, BlockSlot("BibibW")),                  # Player block placement  (C --> S)
            0x10: (None, Standard("Bh")),                       # Holding change  (C --> S)
            0x11: (None, Standard("Bibibi")),                   # Use bed
            0x12: (None, Standard("Bib")),                      # Animation
            0x13: (None, Standard("Bib")),                      # Entity action  (C --> S)
            0x14: (self.onNamedEntitySpawn, String("BiSiiibbh")), # Named entity spawn
            0x15: (None, Standard("Bihbhiiibbb")),              # Pickup spawn
            0x16: (None, Standard("Bii")),                      # Collect item
            0x17: (self.onAddObject, Standard("Bibiiiihhh")),   # Add object/vehicle
            0x18: (None, MetaEntity("BibiiibbE")),              # Mob spawn
            0x19: (None, String("BiSiiii")),                    # Entity: painting
            0x1A: (None, Standard("Biiiih")),                   # Experience Orb
            0x1B: (None, Standard("Bffff??")),                  # Stance update
            0x1C: (None, Standard("Bihhh")),                    # Entity velocity
            0x1D: (None, Standard("Bi")),                       # Destroy entity
            0x1E: (None, Standard("Bi")),                       # Entity
            0x1F: (self.onEntityRelativeMove, Standard("Bibbb")), # Entity relative move
            0x20: (None, Standard("Bibb")),                     # Entity look
            0x21: (None, Standard("Bibbbbb")),                  # Entity look and relative move
            0x22: (None, Standard("Biiiibb")),                  # Entity teleport
            0x26: (None, Standard("Bib")),                      # Entity status
            0x27: (None, Standard("Bii")),                      # Attach entity
            0x28: (None, MetaEntity("BiE")),                    # Entity metadata
            0x29: (None, Standard("Bibbh")),                    # Entity Effect
            0x2A: (None, Standard("Bib")),                      # Remove Entity Effect
            0x2B: (None, Standard("Bfhh")),                     # Experience
            0x32: (self.onPreChunk, Standard("Bii?")),          # Pre chunk
            0x33: (self.onChunk, MetaChunk()),                  # Map chunk
            0x34: (None, BlockArray()),                         # Multi-block change
            0x35: (None, Standard("Bibibb")),                   # Block change
            0x36: (None, Standard("Bihibb")),                   # Block action
            0x3C: (None, BlockExplosion()),                     # Explosion
            0x3D: (None, Standard("Biibii")),                   # Sound effect
            0x46: (None, Standard("Bbb")),                      # New/invalid state
            0x47: (None, Standard("Bi?iii")),                   # Thunderbolt
            0x64: (None, String("BbbSb")),                      # Open window
            0x65: (None, Standard("Bb")),                       # Close window
            0x66: (None, BlockSlot("Bbhbh?W")),                 # Window click
            0x67: (None, BlockSlot("BbhW")),                    # Set slot
            0x68: (self.onWindowItems, Window()),               # Window items
            0x69: (None, Standard("Bbhh")),                     # Update window property
            0x6A: (self.onTransaction, Standard("Bbh?")),       # Transaction
            0x6B: (None, BlockSlot("BhW")),                     # Creative inventory action 
            0x6C: (None, Standard("Bbb")),                      # Enchant Item
            0x82: (None, String("BihiSSSS")),                   # Update sign
            0x83: (None, ByteArray()),                          # item data
            0xC8: (None, Standard("Bib")),                      # Increment statistic
            0xC9: (self.onPlayerList, String("BS?h")),          # Player List Item
            0xFF: (self.onDisconnected, String("BS")),          # Disconnect
        }


    def connectionMade(self):
        """ called when a connection has been established """
        self.log.info("<%s> connected" % self.bot.getName())
#        self.writer = PacketWriter(self.transport)
        self.log.debug("ATTEMPTING TO HANDSHAKE")
        self.sendHandshake(self.bot.getName())

    def connectionLost(self, reason):
        self.log.error("connection lost %s" % reason)

    def dataReceived(self, data):
        """ new data are received """ 
        self.buffer.append(data)

        while True:
            # get the packet Type
            try:
                data = self.buffer.peek()[0]
                packetType = ord(data)
            except IOError:   # if empty buffer stop the loop
                break
            self.log.received(packetType)

            # get the packet information
            if packetType not in self.packets:
                self.log.error("Unknown packet 0x%02X" % packetType)
                self.transport.loseConnection()
                
            fct, fmt = self.packets[packetType]
            try:
                packet = fmt.unpack(self.buffer)
            except IOError:
                break

            # do action on packet received
            if fct!= None:
                fct(self.bot, packet) 
    
  
    def onDisconnected(self, bot, info):
        self.log.info("Disconnected by server. Reason=%s" % info[1])

    def onKeepAlive(self, bot, info):
        self.send(0x00, 0)
    
    def onUpdateHealth(self, bot, info):
        bot.setHealth(health=info[1], food=info[2], foodSaturation=info[3])
        
    def onLoginRequest(self, bot, info):
        bot.setUID(info[1])
        #bot.setMaxSeed(info[3])
        pass
    
    def onHandshake(self, bot, info):
        #bot.setConnectionHash(info[1])
        self.sendLoginRequest(22, self.bot.getName())
        self.bot.nameToUpper()

    
    def onSpawnPosition(self, bot, info):
        bot.setSpawnPosition(info)
    
    def onPLayerPosition(self, bot, info):
        cmd, x, stance, y, z, yaw, pitch, onGround = info
        bot.setPlayerPosition(x, y, z, stance, yaw, pitch, onGround)
        self.send(0x0D, x, y, stance, z, yaw, pitch, onGround)

    def onEntityRelativeMove(self, bot, info):
        bot.setEntityRelativeMove(info)
    
    def onWindowItems(self, bot, info):
        pass
#        if info[1] == 0:
#            # inventory window
#            bot.setInventory(info[2], info[3])
#        else:
#            logging.error("Unknown window number=%d" % info[1])

#    def onSetSlot(self, bot, info):
#        if info[1] == 0:
#            bot.setSlotItem(info[1], info[2], info[3], info[4], info[5])
#            logging.debug("SetSlot %d, slot %d" %(info[1], info[2]))
    
    def onNamedEntitySpawn(self, bot, info):
        bot.setPlayerSpawn(info)
        
    def onAddObject(self):
        pass
    
    def onPreChunk(self, bot, info):
        pass
    
    def onChunk(self, bot, info):
        cmd, x, y, z, sx, sy, sz, compSize, chunkData = info
        # correct the real size
        sx += 1
        sy += 1
        sz += 1
        self.log.chunk("Chunk (x,y,z)=(%d, %d, %d) (sx,sy,sz)=(%d, %d, %d), len=%d" % (x, y, z, sx, sy, sz, len(chunkData)))
        self.world.addChunk(x, y, z, sx, sy, sz, chunkData)
    
    def onTransaction(self, bot, info):
        pass 
    
    def send(self, cmd, *data):
        if cmd not in self.packets:
            self.log.error("Unknown packet to send 0x%02X" % cmd)
            self.transport.loseConnection()
        fct, pack = self.packets[cmd]
        res = pack.pack(cmd, *data)
        deb = ""
        for x in res:
            deb += "%02X " % ord(x)
        self.transport.write(res)
        self.log.send(cmd)
    
    def sendDisconnect(self, reason):
        self.send(0xFF, "Disconnection by client")
        self.transport.loseConnection()

    def sendHandshake(self, userName):
        self.send(0x02, userName)

    def sendLoginRequest(self, protocol_version, username):
        self.send(0x01, protocol_version, username, 0, 0, 0, 0, 0, 0)
   
    def onChat(self, bot, info):
        '''
        Chat received. Analyse it to detect a message from the player 
        '''
        info = info[1].upper()
        if ord(info[0]) == 0xA7: #skip color code
            info = info[2:]
        # if the message is from a player it starts with <PlayerName>
        result = re.match("<(.*)>(.*)", info)
        if result == None:
            name = ""
            msg = info
        else:
            name = result.group(1)
            msg = result.group(2)
            result = msg.split()
            if BOT_NAME_NEEDED: # Command starts with the targeted Bot name
                if result[0] == bot.getName():
                    bot.parseCommand(name, result[1:])
            else:
                if name <> bot.getName():  # do not compute self message
                    bot.parseCommand(name, result)
                
    def onPlayerList(self, bot, info):
        bot.updatePlayerList(info[1], info[2])
예제 #4
0
class Host(object):
    def __init__(self, number: int, gel: "Global Event List"):
        self.name = f"Host {number}"
        self.status = "idle"
        self.buffer = Buffer()
        self.channel = gel.channel
        self.GEL = gel
        self.arrivalRate = 0.8           # lambda
        self.senseTime = 0.01            # 0.01 ms
        self.DIFS = 0.1                  # 0.10 ms
        self.SIFS = 0.05                 # 0.05 ms
        self.notACKedDict = {}
        self.ackId = 0

    def random_backoff(self, n: int)-> float:
        """
        To generate random backoff counter value
        :param df: the dataframe which we can get the number of collision n from it
        :return: Random backoof counter value
        """
        if n > 10:
            n = 10
        wait_time = random.randint(0, 2 ** n - 1)
        return wait_time

    def addToBuffer(self, df: DataFrame):
        self.buffer.append(df)

    def processArrivalDataFrame(self, event_time: float, receiver: "Host", _type: str, df = None, origin = None):
        """
        create one of the following arrival event and put it to the GEL event list

        internal DF: df created in this host
            success => schedule sense event to see if the channel is idle to process the df
            failure => put into the buffer

        external DF: df from external host to this host
            success => schedule receive event and then return an ACK latter
            failure => no failure

        ACK DF: ACK from external host
            success => take the next df from the buffer and process it
            failure => no failure
        """

        sender = self

        success = None
        failure = None
        arrival = None



        if _type == "internal DF":
            """
            Schedule next event
            To create a sense channel event, or put it into the buffer
            """
            if self.GEL.packet_counter < self.GEL.TOTAL_PACKET:
                new_arrival_event = ScheduleDataFrameEvent(_type, event_time, sender, receiver, self.GEL, sender)
                self.GEL.addEvent(new_arrival_event)

            if self.status == "idle":
                sense_event_time = event_time + self.senseTime
                self.createSenseChannelEvent(sense_event_time, df, "df, stage 0", df.origin)
            else:
                self.buffer.insert_dataframe(df)

        elif _type == "external DF":
            """
            create an ack packet, and then create a SenseChannel Event for this ack packet 
            """
            ack_time = event_time
            sense_event_time = event_time + self.senseTime

            # print("ack", ack_time, df.sender, df.receiver, df.id, df.origin)
            ack = DataFrame("ack", ack_time, df.sender, df.receiver, df.id, df.origin)
            ack.global_Id = df.global_Id
            ack.size = 64

            self.createSenseChannelEvent(sense_event_time, ack, "ack, stage 0", df.origin)

        elif _type == "ack":
            success_time = event_time + 0

            def success():
                "to get the unacked event from the notAckedDict and then acknowledge the packet"
                unacked = self.notACKedDict[df.id]
                unacked.ACKed = True

                if len(self.buffer.array) != 0:
                    next_df = self.origin.buffer.popleft()
                    self.createSenseChannelEvent(event_time, next_df, "df, stage 0", df.origin)

            success_event = SuccessTransferEvent(success_time, df, success, failure, df.origin)
            self.GEL.addEvent(success_event)


    def createSenseChannelEvent(self, event_time: float, df: DataFrame, type: str, origin, counter: float = None):
        """
        five types of sense event
            1) df, stage 0
                success => If the channel is idle, wait for 1 DIFS (0.1 ms)
                failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0.
            2) df, stage 1
                success => If the channel is idle again, transfer the dataframe to the channel immediately
                failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0.
            3) ack, stage 0
                success => If the channel is idle, wait for 1 SIFS (0.1 ms)
                failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0.
            4) ack, stage 1
                success => If the channel is idle again, transfer the ack to the channel immediately
                failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0.
            5) Countdown        (not finished)
        """
        success = None
        failure = None
        sense_event_time = event_time + self.senseTime

        if type == "df, stage 0":
            """
            1) df, stage 0
                success => If the channel is idle, wait for 1 DIFS (0.1 ms)
                failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0.
            """

            def success():
                self.createSenseChannelEvent(sense_event_time + self.DIFS, df, "df, stage 1", df.origin)

            def failure():
                self.createSenseChannelEvent(sense_event_time, df, "RBA", df.origin)

        elif type == "df, stage 1":
            """
            2) df, stage 1
                success => If the channel is idle again, transfer the dataframe to the channel immediately
                    push_event_time_1 = event_time + transfer delay
                failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0.
                    sense_event_time = event_time + self.senseTime
            """

            def success():
                push_event_time = sense_event_time
                self.createPushToChannelEvent(push_event_time, df)

            def failure():
                _counter = self.random_backoff(df.number_of_collision)
                self.createSenseChannelEvent(sense_event_time, df, "Countdown", _counter)

        elif type == "ack, stage 0":
            """
            3) ack, stage 0
                success => If the channel is idle, wait for 1 SIFS (0.1 ms)
                           sense_time = event_time + 0.1
                failure => If the channel is busy, do nothing
        
            """
            def success():
                self.createSenseChannelEvent(sense_event_time, df, "ack, stage 1", df.origin)

            def failure():
                pass

        elif type == "ack, stage 1":
            """
            4) ack, stage 1
                success => If the channel is idle again, transfer the ack to the channel immediately
                           push_event_time = event_time + transfer delay
                failure => If the channel is busy, create a countdown and then create another sense event to reduce the timer to 0.
            """

            def success():
                push_event_time = sense_event_time
                self.createPushToChannelEvent(push_event_time, df, "ack")

        elif type == "Countdown":
            """
                5) Countdown        (not finished)
            """
            pass

        senseChannelEvent = SenseChannelEvent(sense_event_time, type, df, success, failure, df.origin )
        self.GEL.addEvent(senseChannelEvent)

    def createPushToChannelEvent(self, event_time:float, df: DataFrame, type:str = "external DF"):
        """
        After we detect the channel is idle again after DF stage 1 or ACK stage 1, we immediately push the packet into the channel
        pushEventTime = event_time + 0

        There are two types of push event. One is external DF and the other is ack
        external DF:
            success => schedule a departure event and then expect an ACK packet return
            failure => no failure would occur

        ack:
            success => just schedule a departure event, no need to schedule an ACK expect event
            failure => no failure

        """

        def success():
            """
            If success, expect an ACK packet return
            departure_event_time = event_time + transmission_time_df + transmission_time_ACK
            expected_ACK_time = event_time + (df size + ack size) / self.channel.rate + self.senseTime * 2 + 1e-9
            1e-9 ensure that the expected_ACK_time must be behind the ACK received time
            :return:
            """
            departure_event_time = event_time + df.size / self.channel.rate * 1000

            self.channel.createDepartureEvent(departure_event_time, df, type)

            if type == "external DF":
                self.notACKedDict[df.id] = df
                self.createExpectAckEvent(event_time, df)
            elif type == "ack":
                """do not need to pass back ack if the packet is an ack"""
                pass

        def failure():
            pass

        push_event_time = event_time + 0
        pushEvent = PushToChannelEvent(push_event_time, f"push {df.type} to channel", df, success, failure, df.origin)
        self.GEL.addEvent(pushEvent)



    def createExpectAckEvent(self, event_time: float, df: DataFrame):
        """
        If df is pushed to the channel, the sender expects an ACK packet will come back. This method is to create the AckExpecetedEvent and put it into the GEL.
        If the sender receives the ACK packet before timeout, then it does nothing; otherwise, it retransmit the packet
        success => do nothing
        failure => retransmit the packet (not finished)
        expected_event_time = event_time + total_transmission_time (2 df and 2 ACK) + SIFS + 2 * senseTime

        """
        expected_event_time = event_time + (df.size + 64) / self.channel.rate * 1000 + self.SIFS + 0.0001
        self.notACKedDict[df.id] = df
        _ackExpectEvent = AckExpectedEvent(event_time, expected_event_time, df, origin = df.origin)

        def success():
            """to remove the object from the unACK Array"""
            result = AckResultEvent(expected_event_time, df, df.origin, "success")
            self.GEL.addEvent(result)
            # del self.notACKedDict[df.id]
            # del _ackExpectEvent

        def failure():
            """
            to remove the object from the unACK Array
            Then, use the RBA to create a counter and retransmit the object
            """
            result = AckResultEvent(expected_event_time, df, df.origin, "failure")
            self.GEL.addEvent(result)
            df.number_of_collision += 1

            counter = self.random_backoff(df.number_of_collision)
            # print(df.number_of_collision, counter)

            self.createSenseChannelEvent(event_time, df, "Countdown", counter)

            # del self.notACKedDict[df.id]

        _ackExpectEvent.success = success
        _ackExpectEvent.failure = failure
        _ackExpectEvent._id = df.id
        _ackExpectEvent.ACKed = False
        self.notACKedDict[df.id] = _ackExpectEvent
        self.GEL.addEvent(_ackExpectEvent)

    def __str__(self):
        return self.name

    def __repr__(self):
        return str(self.name)