def __post_frame(self, gamestate, event_bytes): gamestate.stage = self._current_stage gamestate.frame = np.ndarray((1,), ">i", event_bytes, 0x1)[0] controller_port = np.ndarray((1,), ">B", event_bytes, 0x5)[0] + 1 if controller_port not in gamestate.players: gamestate.players[controller_port] = PlayerState() playerstate = gamestate.players[controller_port] # Is this Nana? if np.ndarray((1,), ">B", event_bytes, 0x6)[0] == 1: playerstate.nana = PlayerState() playerstate = playerstate.nana playerstate.position.x = np.ndarray((1,), ">f", event_bytes, 0xa)[0] playerstate.position.y = np.ndarray((1,), ">f", event_bytes, 0xe)[0] playerstate.x = playerstate.position.x playerstate.y = playerstate.position.y playerstate.character = enums.Character(np.ndarray((1,), ">B", event_bytes, 0x7)[0]) try: playerstate.action = enums.Action(np.ndarray((1,), ">H", event_bytes, 0x8)[0]) except ValueError: playerstate.action = enums.Action.UNKNOWN_ANIMATION # Melee stores this in a float for no good reason. So we have to convert playerstate.facing = np.ndarray((1,), ">f", event_bytes, 0x12)[0] > 0 playerstate.percent = int(np.ndarray((1,), ">f", event_bytes, 0x16)[0]) playerstate.shield_strength = np.ndarray((1,), ">f", event_bytes, 0x1A)[0] playerstate.stock = np.ndarray((1,), ">B", event_bytes, 0x21)[0] playerstate.action_frame = int(np.ndarray((1,), ">f", event_bytes, 0x22)[0]) try: playerstate.hitstun_frames_left = int(np.ndarray((1,), ">f", event_bytes, 0x2B)[0]) except TypeError: playerstate.hitstun_frames_left = 0 except ValueError: playerstate.hitstun_frames_left = 0 try: playerstate.on_ground = not bool(np.ndarray((1,), ">B", event_bytes, 0x2F)[0]) except TypeError: playerstate.on_ground = True try: playerstate.jumps_left = np.ndarray((1,), ">B", event_bytes, 0x32)[0] except TypeError: playerstate.jumps_left = 1 try: playerstate.invulnerable = int(np.ndarray((1,), ">B", event_bytes, 0x34)[0]) != 0 except TypeError: playerstate.invulnerable = False try: playerstate.speed_air_x_self = np.ndarray((1,), ">f", event_bytes, 0x35)[0] except TypeError: playerstate.speed_air_x_self = 0 try: playerstate.speed_y_self = np.ndarray((1,), ">f", event_bytes, 0x39)[0] except TypeError: playerstate.speed_y_self = 0 try: playerstate.speed_x_attack = np.ndarray((1,), ">f", event_bytes, 0x3D)[0] except TypeError: playerstate.speed_x_attack = 0 try: playerstate.speed_y_attack = np.ndarray((1,), ">f", event_bytes, 0x41)[0] except TypeError: playerstate.speed_y_attack = 0 try: playerstate.speed_ground_x_self = np.ndarray((1,), ">f", event_bytes, 0x45)[0] except TypeError: playerstate.speed_ground_x_self = 0 try: playerstate.hitlag_left = int(np.ndarray((1,), ">f", event_bytes, 0x49)[0]) except TypeError: playerstate.hitlag_left = 0 # Keep track of a player's invulnerability due to respawn or ledge grab if controller_port in self._prev_gamestate.players: playerstate.invulnerability_left = max(0, self._invuln_start[controller_port][1] - (gamestate.frame - self._invuln_start[controller_port][0])) if playerstate.action == Action.ON_HALO_WAIT: playerstate.invulnerability_left = 120 self._invuln_start[controller_port] = (gamestate.frame, 120) # Don't give invulnerability to the first descent if playerstate.action == Action.ON_HALO_DESCENT and gamestate.frame > 150: playerstate.invulnerability_left = 120 self._invuln_start[controller_port] = (gamestate.frame, 120) if playerstate.action == Action.EDGE_CATCHING and playerstate.action_frame == 1: playerstate.invulnerability_left = 36 self._invuln_start[controller_port] = (gamestate.frame, 36) # First frame of the game if gamestate.frame == -123: playerstate.invulnerability_left = 0 self._invuln_start[controller_port] = (gamestate.frame, 0) # The pre-warning occurs when we first start a dash dance. if controller_port in self._prev_gamestate.players: if playerstate.action == Action.DASHING and \ self._prev_gamestate.players[controller_port].action not in [Action.DASHING, Action.TURNING]: playerstate.moonwalkwarning = True # Take off the warning if the player does an action other than dashing if playerstate.action != Action.DASHING: playerstate.moonwalkwarning = False # "off_stage" helper try: if (abs(playerstate.position.x) > stages.EDGE_GROUND_POSITION[gamestate.stage] or \ playerstate.y < -6) and not playerstate.on_ground: playerstate.off_stage = True else: playerstate.off_stage = False except KeyError: playerstate.off_stage = False # ECB top edge, x ecb_top_x = 0 ecb_top_y = 0 try: ecb_top_x = np.ndarray((1,), ">f", event_bytes, 0x4D)[0] except TypeError: ecb_top_x = 0 # ECB Top edge, y try: ecb_top_y = np.ndarray((1,), ">f", event_bytes, 0x51)[0] except TypeError: ecb_top_y = 0 playerstate.ecb.top = collections.namedtuple("Position", ['x', 'y']) playerstate.ecb.top.x = ecb_top_x playerstate.ecb.top.y = ecb_top_y playerstate.ecb_top = (ecb_top_x, ecb_top_y) # ECB bottom edge, x coord ecb_bot_x = 0 ecb_bot_y = 0 try: ecb_bot_x = np.ndarray((1,), ">f", event_bytes, 0x55)[0] except TypeError: ecb_bot_x = 0 # ECB Bottom edge, y coord try: ecb_bot_y = np.ndarray((1,), ">f", event_bytes, 0x59)[0] except TypeError: ecb_bot_y = 0 playerstate.ecb.bottom = collections.namedtuple("Position", ['x', 'y']) playerstate.ecb.bottom.x = ecb_bot_x playerstate.ecb.bottom.y = ecb_bot_y playerstate.ecb_bottom = (ecb_bot_x, ecb_bot_y) # ECB left edge, x coord ecb_left_x = 0 ecb_left_y = 0 try: ecb_left_x = np.ndarray((1,), ">f", event_bytes, 0x5D)[0] except TypeError: ecb_left_x = 0 # ECB left edge, y coord try: ecb_left_y = np.ndarray((1,), ">f", event_bytes, 0x61)[0] except TypeError: ecb_left_y = 0 playerstate.ecb.left = collections.namedtuple("Position", ['x', 'y']) playerstate.ecb.left.x = ecb_left_x playerstate.ecb.left.y = ecb_left_y playerstate.ecb_left = (ecb_left_x, ecb_left_y) # ECB right edge, x coord ecb_right_x = 0 ecb_right_y = 0 try: ecb_right_x = np.ndarray((1,), ">f", event_bytes, 0x65)[0] except TypeError: ecb_right_x = 0 # ECB right edge, y coord try: ecb_right_y = np.ndarray((1,), ">f", event_bytes, 0x69)[0] except TypeError: ecb_right_y = 0 playerstate.ecb.right = collections.namedtuple("Position", ['x', 'y']) playerstate.ecb.right.x = ecb_right_x playerstate.ecb.right.y = ecb_right_y playerstate.ecb_right = (ecb_right_x, ecb_right_y) if self._use_manual_bookends: self._frame = gamestate.frame
def __init__(self, path=None, is_dolphin=True, dolphin_home_path=None, tmp_home_directory=True, slippi_address="127.0.0.1", slippi_port=51441, online_delay=2, blocking_input=False, polling_mode=False, allow_old_version=False, logger=None): """Create a Console object Args: path (str): Path to the directory where your dolphin executable is located. If None, will assume the dolphin is remote and won't try to configure it. dolphin_home_path (str): Path to dolphin user directory. Optional. is_dolphin (bool): Is this console a dophin instance, or SLP file? tmp_home_directory (bool): Use a temporary directory for the dolphin User path This is useful so instances don't interfere with each other. slippi_address (str): IP address of the Dolphin / Wii to connect to. slippi_port (int): UDP port that slippi will listen on online_delay (int): How many frames of delay to apply in online matches blocking_input (bool): Should dolphin block waiting for bot input This is only really useful if you're doing ML training. polling_mode (bool): Polls input to console rather than blocking for it When set, step() will always return immediately, but may be None if no gamestate is available yet. allow_old_version (bool): Allow SLP versions older than 3.0.0 (rollback era) Only enable if you know what you're doing. You probably don't want this. Gamestates will be missing key information, come in really late, or possibly not work at all logger (logger.Logger): Logger instance to use. None for no logger. """ self.logger = logger self.is_dolphin = is_dolphin self.path = path self.dolphin_home_path = dolphin_home_path if tmp_home_directory and self.is_dolphin: temp_dir = tempfile.mkdtemp(prefix='libmelee_') temp_dir += "/User/" _copytree_safe(self._get_dolphin_home_path(), temp_dir) self.dolphin_home_path = temp_dir self.processingtime = 0 self._frametimestamp = time.time() self.slippi_address = slippi_address """(str): IP address of the Dolphin / Wii to connect to.""" self.slippi_port = slippi_port """(int): UDP port of slippi server. Default 51441""" self.eventsize = [0] * 0x100 self.connected = False self.nick = "" """(str): The nickname the console has given itself.""" self.version = "" """(str): The Slippi version of the console""" self.cursor = 0 self.controllers = [] self._current_stage = enums.Stage.NO_STAGE self._frame = 0 self._polling_mode = polling_mode self.slp_version = "unknown" """(str): The SLP version this stream/file currently is.""" self._allow_old_version = allow_old_version self._use_manual_bookends = False self._costumes = {0:0, 1:0, 2:0, 3:0} self._cpu_level = {0:0, 1:0, 2:0, 3:0} self._invuln_start = {1:(0,0), 2:(0,0), 3:(0,0), 4:(0,0)} # Keep a running copy of the last gamestate produced self._prev_gamestate = GameState() # Half-completed gamestate not yet ready to add to the list self._temp_gamestate = None self._process = None if self.is_dolphin: self._slippstream = SlippstreamClient(self.slippi_address, self.slippi_port) if self.path: # Setup some dolphin config options dolphin_ini_path = self._get_dolphin_config_path() + "Dolphin.ini" if not os.path.isfile(dolphin_ini_path): raise InvalidDolphinPath(self._get_dolphin_config_path()) config = configparser.ConfigParser() config.read(dolphin_ini_path) config.set("Core", 'slippienablespectator', "True") config.set("Core", 'slippispectatorlocalport', str(self.slippi_port)) # Set online delay config.set("Core", 'slippionlinedelay', str(online_delay)) # Turn on background input so we don't need to have window focus on dolphin config.set("Input", 'backgroundinput', "True") config.set("Core", 'BlockingPipes', str(blocking_input)) with open(dolphin_ini_path, 'w') as dolphinfile: config.write(dolphinfile) else: self._slippstream = SLPFileStreamer(self.path) # Prepare some structures for fixing melee data path = os.path.dirname(os.path.realpath(__file__)) with open(path + "/actiondata.csv") as csvfile: #A list of dicts containing the frame data actiondata = list(csv.DictReader(csvfile)) #Dict of sets self.zero_indices = defaultdict(set) for line in actiondata: if line["zeroindex"] == "True": self.zero_indices[int(line["character"])].add(int(line["action"])) # Read the character data csv self.characterdata = dict() with open(path + "/characterdata.csv") as csvfile: reader = csv.DictReader(csvfile) for line in reader: del line["Character"] #Convert all fields to numbers for key, value in line.items(): line[key] = float(value) self.characterdata[enums.Character(line["CharacterIndex"])] = line
def __handle_slippstream_events(self, event_bytes, gamestate): """ Handle a series of events, provided sequentially in a byte array """ gamestate.menu_state = enums.Menu.IN_GAME while len(event_bytes) > 0: event_size = self.eventsize[event_bytes[0]] if len(event_bytes) < event_size: print("WARNING: Something went wrong unpacking events. Data is probably missing") print("\tDidn't have enough data for event") return False if EventType(event_bytes[0]) == EventType.PAYLOADS: cursor = 0x2 payload_size = event_bytes[1] num_commands = (payload_size - 1) // 3 for i in range(0, num_commands): command, command_len = unpack(">bH", event_bytes[cursor:cursor+3]) self.eventsize[command] = command_len+1 cursor += 3 event_bytes = event_bytes[payload_size + 1:] elif EventType(event_bytes[0]) == EventType.FRAME_START: event_bytes = event_bytes[event_size:] elif EventType(event_bytes[0]) == EventType.GAME_START: event_bytes = event_bytes[event_size:] elif EventType(event_bytes[0]) == EventType.GAME_END: event_bytes = event_bytes[event_size:] elif EventType(event_bytes[0]) == EventType.PRE_FRAME: event_bytes = event_bytes[event_size:] elif EventType(event_bytes[0]) == EventType.POST_FRAME: gamestate.frame = unpack(">i", event_bytes[0x1:0x1+4])[0] controller_port = unpack(">B", event_bytes[0x5:0x5+1])[0] + 1 gamestate.player[controller_port].x = unpack(">f", event_bytes[0xa:0xa+4])[0] gamestate.player[controller_port].y = unpack(">f", event_bytes[0xe:0xe+4])[0] gamestate.player[controller_port].character = enums.Character(unpack(">B", event_bytes[0x7:0x7+1])[0]) try: gamestate.player[controller_port].action = enums.Action(unpack(">H", event_bytes[0x8:0x8+2])[0]) except ValueError: gamestate.player[controller_port].action = enums.Action.UNKNOWN_ANIMATION # Melee stores this in a float for no good reason. So we have to convert facing_float = unpack(">f", event_bytes[0x12:0x12+4])[0] gamestate.player[controller_port].facing = facing_float > 0 gamestate.player[controller_port].percent = int(unpack(">f", event_bytes[0x16:0x16+4])[0]) gamestate.player[controller_port].stock = unpack(">B", event_bytes[0x21:0x21+1])[0] gamestate.player[controller_port].action_frame = int(unpack(">f", event_bytes[0x22:0x22+4])[0]) # Extract the bit at mask 0x20 bitflags2 = unpack(">B", event_bytes[0x27:0x27+1])[0] gamestate.player[controller_port].hitlag = bool(bitflags2 & 0x20) try: gamestate.player[controller_port].hitstun_frames_left = int(unpack(">f", event_bytes[0x2b:0x2b+4])[0]) except ValueError: gamestate.player[controller_port].hitstun_frames_left = 0 gamestate.player[controller_port].on_ground = not bool(unpack(">B", event_bytes[0x2f:0x2f+1])[0]) gamestate.player[controller_port].jumps_left = unpack(">B", event_bytes[0x32:0x32+1])[0] gamestate.player[controller_port].invulnerable = int(unpack(">B", event_bytes[0x34:0x34+1])[0]) != 0 try: gamestate.player[controller_port].speed_air_x_self = unpack(">f", event_bytes[0x35:0x35+4])[0] except error: gamestate.player[controller_port].speed_air_x_self = 0 try: gamestate.player[controller_port].speed_y_self = unpack(">f", event_bytes[0x39:0x39+4])[0] except error: gamestate.player[controller_port].speed_y_self = 0 try: gamestate.player[controller_port].speed_x_attack = unpack(">f", event_bytes[0x3d:0x3d+4])[0] except error: gamestate.player[controller_port].speed_x_attack = 0 try: gamestate.player[controller_port].speed_y_attack = unpack(">f", event_bytes[0x41:0x41+4])[0] except error: gamestate.player[controller_port].speed_y_attack = 0 try: gamestate.player[controller_port].speed_ground_x_self = unpack(">f", event_bytes[0x45:0x45+4])[0] except error: gamestate.player[controller_port].speed_ground_x_self = 0 # Keep track of a player's invulnerability due to respawn or ledge grab gamestate.player[controller_port].invulnerability_left = max(0, self._prev_gamestate.player[controller_port].invulnerability_left - 1) if gamestate.player[controller_port].action == Action.ON_HALO_WAIT: gamestate.player[controller_port].invulnerability_left = 120 # Don't give invulnerability to the first descent if gamestate.player[controller_port].action == Action.ON_HALO_DESCENT and gamestate.frame > 150: gamestate.player[controller_port].invulnerability_left = 120 if gamestate.player[controller_port].action == Action.EDGE_CATCHING and gamestate.player[controller_port].action_frame == 1: gamestate.player[controller_port].invulnerability_left = 36 # The pre-warning occurs when we first start a dash dance. if gamestate.player[controller_port].action == Action.DASHING and \ self._prev_gamestate.player[controller_port].action not in [Action.DASHING, Action.TURNING]: gamestate.player[controller_port].moonwalkwarning = True # Take off the warning if the player does an action other than dashing if gamestate.player[controller_port].action != Action.DASHING: gamestate.player[controller_port].moonwalkwarning = False # "off_stage" helper if (abs(gamestate.player[controller_port].x) > stages.EDGE_GROUND_POSITION[gamestate.stage] or \ gamestate.player[controller_port].y < -6) and not gamestate.player[controller_port].on_ground: gamestate.player[controller_port].off_stage = True else: gamestate.player[controller_port].off_stage = False event_bytes = event_bytes[event_size:] elif EventType(event_bytes[0]) == EventType.GECKO_CODES: event_bytes = event_bytes[event_size:] elif EventType(event_bytes[0]) == EventType.FRAME_BOOKEND: self._prev_gamestate = gamestate # Calculate helper distance variable # This is a bit kludgey.... :/ i = 0 player_one_x, player_one_y, player_two_x, player_two_y = 0, 0, 0, 0 for _, player_state in gamestate.player.items(): if i == 0: player_one_x, player_one_y = player_state.x, player_state.y if i == 1: player_two_x, player_two_y = player_state.x, player_state.y i += 1 xdist = player_one_x - player_two_x ydist = player_one_y - player_two_y gamestate.distance = math.sqrt((xdist**2) + (ydist**2)) event_bytes = event_bytes[event_size:] return True elif EventType(event_bytes[0]) == EventType.ITEM_UPDATE: projectile = Projectile() projectile.x = unpack(">f", event_bytes[0x14:0x14+4])[0] projectile.y = unpack(">f", event_bytes[0x18:0x18+4])[0] projectile.x_speed = unpack(">f", event_bytes[0x0c:0x0c+4])[0] projectile.y_speed = unpack(">f", event_bytes[0x10:0x10+4])[0] try: projectile.owner = unpack(">B", event_bytes[0x2A:0x2A+1])[0] + 1 if projectile.owner > 4: projectile.owner = -1 except error: projectile.owner = -1 try: projectile.subtype = enums.ProjectileSubtype(unpack(">H", event_bytes[0x05:0x05+2])[0]) except ValueError: projectile.subtype = enums.ProjectileSubtype.UNKNOWN_PROJECTILE # Add the projectile to the gamestate list gamestate.projectiles.append(projectile) event_bytes = event_bytes[event_size:] else: print("WARNING: Something went wrong unpacking events. " + \ "Data is probably missing") print("\tGot invalid event type: ", event_bytes[0]) return False return False
def __init__(self, is_dolphin, ai_port, opponent_port, opponent_type, dolphin_executable_path=None, slippi_address="", logger=None): """Create a Console object Args: is_dolphin (boolean): Is this console a dolphin? (Or a Wii) ai_port (int): 1-4 for the controller port your bot will take opponent_port (int): 1-4 for the controller port your opponent will take opponent_type (:obj:`enums.ControllerType`): Enum of your opponent's controller type dolphin_executable_path (str): Path to the directory where your dolphin executable is located. (if applicable) None tells console to use the installed copy of the emulator slippi_address (str): IP address of the Dolphin / Wii to connect to. Empty string will try to autodiscover a nearby SlippiComm server """ self.logger = logger self.ai_port = ai_port self.opponent_port = opponent_port self.is_dolphin = is_dolphin self.dolphin_executable_path = dolphin_executable_path self.processingtime = 0 self._frametimestamp = time.time() self.slippi_address = slippi_address self.slippi_port = 51441 self.eventsize = [0] * 0x100 self.render = True # Keep a running copy of the last gamestate produced self._prev_gamestate = GameState(ai_port, opponent_port) self._process = None if self.is_dolphin: pipes_path = self._get_dolphin_home_path() + "Pipes/" path = os.path.dirname(os.path.realpath(__file__)) if platform.system() != "Windows": #Create the Pipes directory if it doesn't already exist if not os.path.exists(pipes_path): os.makedirs(pipes_path) print("WARNING: Had to create a Pipes directory in Dolphin just now. " \ "You may need to restart Dolphin and this program in order for this to work. " \ "(You should only see this warning once)") pipes_path += "slippibot" + str(ai_port) if not os.path.exists(pipes_path): os.mkfifo(pipes_path) #setup the controllers specified self.setup_dolphin_controller(ai_port) self.setup_dolphin_controller(opponent_port, opponent_type) self._slippstream = SlippstreamClient(self.slippi_address, self.slippi_port) # Prepare some structures for fixing melee data path = os.path.dirname(os.path.realpath(__file__)) with open(path + "/actiondata.csv") as csvfile: #A list of dicts containing the frame data actiondata = list(csv.DictReader(csvfile)) #Dict of sets self.zero_indices = defaultdict(set) for line in actiondata: if line["zeroindex"] == "True": self.zero_indices[int(line["character"])].add(int(line["action"])) # Read the character data csv self.characterdata = dict() with open(path + "/characterdata.csv") as csvfile: reader = csv.DictReader(csvfile) for line in reader: del line["Character"] #Convert all fields to numbers for key, value in line.items(): line[key] = float(value) self.characterdata[enums.Character(line["CharacterIndex"])] = line
def __handle_slippstream_events(self, event_bytes, gamestate): """ Handle a series of events, provided sequentially in a byte array """ lastmessage = EventType.GAME_START while len(event_bytes) > 0: lastmessage = EventType(event_bytes[0]) event_size = self.eventsize[event_bytes[0]] if len(event_bytes) < event_size: print( "WARNING: Something went wrong unpacking events. Data is probably missing" ) print("\tDidn't have enough data for event") return False if (EventType(event_bytes[0]) == EventType.PAYLOADS): cursor = 0x2 payload_size = event_bytes[1] num_commands = (payload_size - 1) // 3 for i in range(0, num_commands): command, command_len = unpack( ">bH", event_bytes[cursor:cursor + 3]) self.eventsize[command] = command_len + 1 cursor += 3 event_bytes = event_bytes[payload_size + 1:] continue elif (EventType(event_bytes[0]) == EventType.FRAME_START): self.frame_num = unpack(">i", event_bytes[1:5])[0] event_bytes = event_bytes[event_size:] continue elif (EventType(event_bytes[0]) == EventType.GAME_START): event_bytes = event_bytes[event_size:] continue elif (EventType(event_bytes[0]) == EventType.GAME_END): event_bytes = event_bytes[event_size:] continue elif (EventType(event_bytes[0]) == EventType.PRE_FRAME): event_bytes = event_bytes[event_size:] continue elif (EventType(event_bytes[0]) == EventType.POST_FRAME): gamestate.frame = unpack(">i", event_bytes[0x1:0x1 + 4])[0] controller_port = unpack(">B", event_bytes[0x5:0x5 + 1])[0] + 1 gamestate.player[controller_port].x = unpack( ">f", event_bytes[0xa:0xa + 4])[0] gamestate.player[controller_port].y = unpack( ">f", event_bytes[0xe:0xe + 4])[0] gamestate.player[controller_port].character = enums.Character( unpack(">B", event_bytes[0x7:0x7 + 1])[0]) try: gamestate.player[controller_port].action = enums.Action( unpack(">H", event_bytes[0x8:0x8 + 2])[0]) except ValueError: gamestate.player[ controller_port].action = enums.Action.UNKNOWN_ANIMATION # Melee stores this in a float for no good reason. So we have to convert facing_float = unpack(">f", event_bytes[0x12:0x12 + 4])[0] gamestate.player[controller_port].facing = facing_float > 0 gamestate.player[controller_port].percent = int( unpack(">f", event_bytes[0x16:0x16 + 4])[0]) gamestate.player[controller_port].stock = unpack( ">B", event_bytes[0x21:0x21 + 1])[0] gamestate.player[controller_port].action_frame = int( unpack(">f", event_bytes[0x22:0x22 + 4])[0]) # Extract the bit at mask 0x20 bitflags2 = unpack(">B", event_bytes[0x27:0x27 + 1])[0] gamestate.player[controller_port].hitlag = bool(bitflags2 & 0x20) gamestate.player[controller_port].hitstun_frames_left = int( unpack(">f", event_bytes[0x2b:0x2b + 4])[0]) gamestate.player[controller_port].on_ground = not bool( unpack(">B", event_bytes[0x2f:0x2f + 1])[0]) gamestate.player[controller_port].jumps_left = unpack( ">B", event_bytes[0x32:0x32 + 1])[0] event_bytes = event_bytes[event_size:] continue elif (EventType(event_bytes[0]) == EventType.GECKO_CODES): event_bytes = event_bytes[event_size:] continue elif (EventType(event_bytes[0]) == EventType.FRAME_BOOKEND): event_bytes = event_bytes[event_size:] return True elif (EventType(event_bytes[0]) == EventType.ITEM_UPDATE): # TODO projectiles projectile = Projectile() projectile.x = unpack(">f", event_bytes[0x14:0x14 + 4])[0] projectile.y = unpack(">f", event_bytes[0x18:0x18 + 4])[0] projectile.x_speed = unpack(">f", event_bytes[0x0c:0x0c + 4])[0] projectile.y_speed = unpack(">f", event_bytes[0x10:0x10 + 4])[0] try: projectile.subtype = enums.ProjectileSubtype( unpack(">H", event_bytes[0x05:0x05 + 2])[0]) except ValueError: projectile.subtype = enums.UNKNOWN_PROJECTILE # Add the projectile to the gamestate list gamestate.projectiles.append(projectile) event_bytes = event_bytes[event_size:] continue else: print("WARNING: Something went wrong unpacking events. " + \ "Data is probably missing") print("\tGot invalid event type: ", event_bytes[0]) return False return False
def __init__(self, path=None, is_dolphin=True, slippi_address="127.0.0.1", logger=None): """Create a Console object Args: path (str): Path to the directory where your dolphin executable is located. (if applicable) None tells console to use the installed copy of the emulator slippi_address (str): IP address of the Dolphin / Wii to connect to. Empty string will try to autodiscover a nearby SlippiComm server logger (logger.Logger): Logger instance to use. None for no logger. """ self.logger = logger self.is_dolphin = is_dolphin self.path = path self.processingtime = 0 self._frametimestamp = time.time() self.slippi_address = slippi_address """(str): IP address of the Dolphin / Wii to connect to.""" self.slippi_port = 51441 """(int): TCP port of slippi server. Default 51441""" self.eventsize = [0] * 0x100 self.render = True self.connected = False self.nick = "" """(str): The nickname the console has given itself.""" self.version = "" """(str): The Slippi version of the console""" self.cursor = 0 self.controllers = [] self._current_stage = enums.Stage.NO_STAGE self._frame = 0 self.slp_version = "unknown" """(str): The SLP version this stream/file currently is.""" # Keep a running copy of the last gamestate produced self._prev_gamestate = GameState() self._process = None if self.is_dolphin: self._slippstream = SlippstreamClient(self.slippi_address, self.slippi_port) else: self._slippstream = SLPFileStreamer(self.path) # Prepare some structures for fixing melee data path = os.path.dirname(os.path.realpath(__file__)) with open(path + "/actiondata.csv") as csvfile: #A list of dicts containing the frame data actiondata = list(csv.DictReader(csvfile)) #Dict of sets self.zero_indices = defaultdict(set) for line in actiondata: if line["zeroindex"] == "True": self.zero_indices[int(line["character"])].add( int(line["action"])) # Read the character data csv self.characterdata = dict() with open(path + "/characterdata.csv") as csvfile: reader = csv.DictReader(csvfile) for line in reader: del line["Character"] #Convert all fields to numbers for key, value in line.items(): line[key] = float(value) self.characterdata[enums.Character( line["CharacterIndex"])] = line
def __init__(self, is_dolphin, ai_port, opponent_port, opponent_type, config_path="", home_path="", logger=None): self.logger = logger self.ai_port = ai_port self.opponent_port = opponent_port self.is_dolphin = is_dolphin self.config_path = config_path self.home_path = home_path self.processingtime = 0 self._frametimestamp = time.time() self.slippi_address = "" self.slippi_port = 51441 self.eventsize = [0] * 0x100 # Keep a running copy of the last gamestate produced # game info is only produced as diffs, not whole snapshots # so if nothing changes, we need to know what the last value was self.render = True self._prev_gamestate = GameState(ai_port, opponent_port) self.process = None if self.is_dolphin: config_path = self.get_dolphin_home_path() pipes_path = config_path + "Pipes/" path = os.path.dirname(os.path.realpath(__file__)) #Create the Pipes directory if it doesn't already exist if not os.path.exists(pipes_path): os.makedirs(pipes_path) print("WARNING: Had to create a Pipes directory in Dolphin just now. " \ "You may need to restart Dolphin and this program in order for this to work. " \ "(You should only see this warning once)") pipes_path += "Bot" + str(ai_port) if not os.path.exists(pipes_path): os.mkfifo(pipes_path) #setup the controllers specified self.setup_dolphin_controller(ai_port) self.setup_dolphin_controller(opponent_port, opponent_type) # Prepare some structures for fixing melee data path = os.path.dirname(os.path.realpath(__file__)) with open(path + "/actiondata.csv") as csvfile: #A list of dicts containing the frame data actiondata = list(csv.DictReader(csvfile)) #Dict of sets self.zero_indices = defaultdict(set) for line in actiondata: if line["zeroindex"] == "True": self.zero_indices[int(line["character"])].add( int(line["action"])) # Read the character data csv self.characterdata = dict() with open(path + "/characterdata.csv") as csvfile: reader = csv.DictReader(csvfile) for line in reader: del line["Character"] #Convert all fields to numbers for key, value in line.items(): line[key] = float(value) self.characterdata[enums.Character( line["CharacterIndex"])] = line
def update(self, mem_update): label = self.locations[mem_update[0]][0] player_int = int(self.locations[mem_update[0]][1]) if label == "frame": self.frame = unpack('<I', mem_update[1])[0] self.newframe = True #Now that the frame is ready, let's calculate some derived information # These are not stored inside Melee anywhere, but are nonetheless # important pieces of information that we don't want to make the # user have to re-calculate on their own for i in self.player: # Move current x,y over to prev self.player[i].prev_x = self.player[i].x self.player[i].prev_y = self.player[i].y # Move future x,y over to current self.player[i].x = self.player[i].next_x self.player[i].y = self.player[i].next_y if (abs(self.player[i].x) > stages.edgegroundposition(self.stage) or \ self.player[i].y < -6) and not self.player[i].on_ground: self.player[i].off_stage = True else: self.player[i].off_stage = False # Keep track of a player's invulnerability due to respawn or ledge grab self.player[i].invulnerability_left = max( 0, self.player[i].invulnerability_left - 1) if self.player[i].action == Action.ON_HALO_WAIT: self.player[i].invulnerability_left = 120 # Don't give invulnerability to the first descent if self.player[ i].action == Action.ON_HALO_DESCENT and self.frame > 150: self.player[i].invulnerability_left = 120 if self.player[ i].action == Action.EDGE_CATCHING and self.player[ i].action_frame == 1: self.player[i].invulnerability_left = 36 # Which character are we right now? if self.player[i].character in [ Character.SHEIK, Character.ZELDA ]: if self.player[i].transformed == self.player[i].iszelda: self.player[i].character = Character.SHEIK else: self.player[i].character = Character.ZELDA # If the player is transformed, then copy over the sub-character attributes if self.player[i].transformed: self.player[i].action = self.player[i + 4].action self.player[i].action_counter = self.player[ i + 4].action_counter self.player[i].action_frame = self.player[i + 4].action_frame self.player[i].invulnerable = self.player[i + 4].invulnerable self.player[i].hitlag_frames_left = self.player[ i + 4].hitlag_frames_left self.player[i].hitstun_frames_left = self.player[ i + 4].hitstun_frames_left self.player[i].charging_smash = self.player[ i + 4].charging_smash self.player[i].jumps_left = self.player[i + 4].jumps_left self.player[i].on_ground = self.player[i + 4].on_ground self.player[i].speed_air_x_self = self.player[ i + 4].speed_air_x_self self.player[i].speed_y_self = self.player[i + 4].speed_y_self self.player[i].speed_x_attack = self.player[ i + 4].speed_x_attack self.player[i].speed_y_attack = self.player[ i + 4].speed_y_attack self.player[i].speed_ground_x_self = self.player[ i + 4].speed_ground_x_self self.player[i].x = self.player[i + 4].x self.player[i].y = self.player[i + 4].y self.player[i].percent = self.player[i + 4].percent self.player[i].facing = self.player[i + 4].facing # The pre-warning occurs when we first start a dash dance. if self.player[i].action == Action.DASHING and self.player[ i].prev_action not in [Action.DASHING, Action.TURNING]: self.player[i].moonwalkwarning = True # Take off the warning if the player does an action other than dashing if self.player[i].action != Action.DASHING: self.player[i].moonwalkwarning = False #TODO: This needs updating in order to support >2 players xdist = self.ai_state.x - self.opponent_state.x ydist = self.ai_state.y - self.opponent_state.y self.distance = math.sqrt((xdist**2) + (ydist**2)) self.fixiasa() self.fixframeindexing() return True if label == "stage": self.stage = unpack('<I', mem_update[1])[0] self.stage = self.stage >> 16 self.stage &= 0x000000ff try: self.stage = enums.Stage(self.stage) except ValueError: self.stage = enums.Stage.NO_STAGE return False if label == "menu_state": self.menu_state = unpack('<I', mem_update[1])[0] self.menu_state &= 0x000000ff self.menu_state = enums.Menu(self.menu_state) return False #Player variables if label == "percent": if player_int > 4: try: self.player[player_int].percent = int( unpack('<f', mem_update[1])[0]) except ValueError: self.player[player_int].percent = 0 return False self.player[player_int].percent = unpack('<I', mem_update[1])[0] self.player[ player_int].percent = self.player[player_int].percent >> 16 return False if label == "stock": self.player[player_int].stock = unpack('<I', mem_update[1])[0] self.player[player_int].stock = self.player[player_int].stock >> 24 return False if label == "facing": self.player[player_int].facing = unpack('<I', mem_update[1])[0] self.player[player_int].facing = not bool( self.player[player_int].facing >> 31) return False if label == "x": self.player[player_int].next_x = unpack('<f', mem_update[1])[0] return False if label == "y": self.player[player_int].next_y = unpack('<f', mem_update[1])[0] return False if label == "character": temp = unpack('<I', mem_update[1])[0] >> 24 try: self.player[player_int].character = enums.Character(temp) except ValueError: self.player[ player_int].character = enums.Character.UNKNOWN_CHARACTER return False if label == "cursor_x": self.player[player_int].cursor_x = unpack('<f', mem_update[1])[0] return False if label == "cursor_y": self.player[player_int].cursor_y = unpack('<f', mem_update[1])[0] return False if label == "action": temp = unpack('<I', mem_update[1])[0] try: # Keep track of old action self.player[player_int].prev_action = self.player[ player_int].action self.player[player_int].action = enums.Action(temp) except ValueError: self.player[player_int].action = enums.Action.UNKNOWN_ANIMATION return False if label == "action_counter": #TODO look if this is backwards temp = unpack('I', mem_update[1])[0] temp = temp >> 8 self.player[player_int].action_counter = temp return False if label == "action_frame": temp = unpack('<f', mem_update[1])[0] try: self.player[player_int].action_frame = int(temp) except ValueError: pass return False if label == "invulnerable": self.player[player_int].invulnerable = unpack('<I', mem_update[1])[0] self.player[player_int].invulnerable = self.player[ player_int].invulnerable >> 31 return False if label == "hitlag_frames_left": temp = unpack('<f', mem_update[1])[0] try: self.player[player_int].hitlag_frames_left = int(temp) except ValueError: pass return False if label == "hitstun_frames_left": temp = unpack('<f', mem_update[1])[0] try: self.player[player_int].hitstun_frames_left = int(temp) except ValueError: pass return False if label == "charging_smash": temp = unpack('<I', mem_update[1])[0] if temp == 2: self.player[player_int].charging_smash = True else: self.player[player_int].charging_smash = False return False if label == "jumps_left": temp = unpack('<I', mem_update[1])[0] temp = temp >> 24 #This value is actually the number of jumps USED # so we have to do some quick math to turn this into what we want try: totaljumps = int(self.characterdata[ self.player[player_int].character]["Jumps"]) self.player[player_int].jumps_left = totaljumps - temp + 1 # Key error will be expected when we first start except KeyError: self.player[player_int].jumps_left = 1 return False if label == "on_ground": temp = unpack('<I', mem_update[1])[0] if temp == 0: self.player[player_int].on_ground = True else: self.player[player_int].on_ground = False return False if label == "speed_air_x_self": self.player[player_int].speed_air_x_self = unpack( '<f', mem_update[1])[0] return False if label == "speed_y_self": self.player[player_int].speed_y_self = unpack('<f', mem_update[1])[0] return False if label == "speed_x_attack": self.player[player_int].speed_x_attack = unpack( '<f', mem_update[1])[0] return False if label == "speed_y_attack": self.player[player_int].speed_y_attack = unpack( '<f', mem_update[1])[0] return False if label == "speed_ground_x_self": self.player[player_int].speed_ground_x_self = unpack( '<f', mem_update[1])[0] return False if label == "coin_down": temp = unpack('<I', mem_update[1])[0] temp = temp & 0x000000ff self.player[player_int].coin_down = (temp == 2) return False if label == "stage_select_cursor_x": self.stage_select_cursor_x = unpack('<f', mem_update[1])[0] return False if label == "stage_select_cursor_y": self.stage_select_cursor_y = unpack('<f', mem_update[1])[0] return False if label == "ready_to_start": temp = unpack('>I', mem_update[1])[0] temp = temp & 0x000000ff self.ready_to_start = not bool(temp) return False if label == "controller_status": temp = unpack('>I', mem_update[1])[0] temp = temp & 0x000000ff self.player[player_int].controller_status = enums.ControllerStatus( temp) return False if label == "hitbox_1_size": self.player[player_int].hitbox_1_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_2_size": self.player[player_int].hitbox_2_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_3_size": self.player[player_int].hitbox_3_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_4_size": self.player[player_int].hitbox_4_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_1_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_1_status = status return False if label == "hitbox_2_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_2_status = status return False if label == "hitbox_3_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_3_status = status return False if label == "hitbox_4_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_4_status = status return False if label == "hitbox_1_x": self.player[player_int].hitbox_1_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_1_y": self.player[player_int].hitbox_1_y = unpack('<f', mem_update[1])[0] return False if label == "hitbox_2_x": self.player[player_int].hitbox_2_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_2_y": self.player[player_int].hitbox_2_y = unpack('<f', mem_update[1])[0] return False if label == "hitbox_3_x": self.player[player_int].hitbox_3_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_3_y": self.player[player_int].hitbox_3_y = unpack('<f', mem_update[1])[0] return False if label == "hitbox_4_x": self.player[player_int].hitbox_4_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_4_y": self.player[player_int].hitbox_4_y = unpack('<f', mem_update[1])[0] return False if label == "iasa": self.player[player_int].iasa = bool( unpack('<I', mem_update[1])[0] >> 31) return False if label == "transformed": temp = unpack('<I', mem_update[1])[0] status = False if temp == 16777216: status = True self.player[player_int].transformed = status return False if label == "iszelda": temp = unpack('<I', mem_update[1])[0] status = False if temp == 18: status = True self.player[player_int].iszelda = status return False if label == "projectiles": #Only once per new frame that we get a projectile, clear the list out if self.newframe: self.projectiles.clear() self.i = 0 self.i += 1 self.newframe = False if len(mem_update[1]) < 10: self.projectiles.clear() return False proj = Projectile() proj.x = unpack('>f', mem_update[1][0x4c:0x50])[0] proj.y = unpack('>f', mem_update[1][0x50:0x54])[0] proj.x_speed = unpack('>f', mem_update[1][0x40:0x44])[0] proj.y_speed = unpack('>f', mem_update[1][0x44:0x48])[0] try: proj.subtype = enums.ProjectileSubtype( unpack('>I', mem_update[1][0x10:0x14])[0]) except ValueError: return False self.projectiles.append(proj) return False
def update(self, mem_update): label = self.locations[mem_update[0]][0] player_int = int(self.locations[mem_update[0]][1]) if label == "frame": self.frame = unpack('<I', mem_update[1])[0] self.newframe = True #Now that the frame is ready, let's calculate some derived information # These are not stored inside Melee anywhere, but are nonetheless # important pieces of information that we don't want to make the # user have to re-calculate on their own for i in self.player: if abs(self.player[i].x) > stages.edgegroundposition( self.stage): self.player[i].off_stage = True else: self.player[i].off_stage = False #TODO: This needs updating in order to support >2 players xdist = self.ai_state.x - self.opponent_state.x ydist = self.ai_state.y - self.opponent_state.y self.distance = math.sqrt((xdist**2) + (ydist**2)) self.fixframeindexing() return True if label == "stage": self.stage = unpack('<I', mem_update[1])[0] self.stage = self.stage >> 16 self.stage &= 0x000000ff try: self.stage = enums.Stage(self.stage) except ValueError: self.stage = enums.Stage.NO_STAGE return False if label == "menu_state": self.menu_state = unpack('<I', mem_update[1])[0] self.menu_state &= 0x000000ff self.menu_state = enums.Menu(self.menu_state) return False #Player variables if label == "percent": self.player[player_int].percent = unpack('<I', mem_update[1])[0] self.player[ player_int].percent = self.player[player_int].percent >> 16 return False if label == "stock": self.player[player_int].stock = unpack('<I', mem_update[1])[0] self.player[player_int].stock = self.player[player_int].stock >> 24 return False if label == "facing": self.player[player_int].facing = unpack('<I', mem_update[1])[0] self.player[player_int].facing = not bool( self.player[player_int].facing >> 31) return False if label == "x": self.player[player_int].x = unpack('<f', mem_update[1])[0] return False if label == "y": self.player[player_int].y = unpack('<f', mem_update[1])[0] return False if label == "character": temp = unpack('<I', mem_update[1])[0] >> 24 try: self.player[player_int].character = enums.Character(temp) except ValueError: self.player[ player_int].character = enums.Character.UNKNOWN_CHARACTER return False if label == "cursor_x": self.player[player_int].cursor_x = unpack('<f', mem_update[1])[0] return False if label == "cursor_y": self.player[player_int].cursor_y = unpack('<f', mem_update[1])[0] return False if label == "action": temp = unpack('<I', mem_update[1])[0] try: self.player[player_int].action = enums.Action(temp) except ValueError: self.player[player_int].action = enums.Action.UNKNOWN_ANIMATION return False if label == "action_counter": #TODO look if this is backwards temp = unpack('I', mem_update[1])[0] temp = temp >> 8 self.player[player_int].action_counter = temp return False if label == "action_frame": temp = unpack('<f', mem_update[1])[0] try: self.player[player_int].action_frame = int(temp) except ValueError: pass return False if label == "invulnerable": self.player[player_int].invulnerable = unpack('<I', mem_update[1])[0] self.player[player_int].invulnerable = self.player[ player_int].invulnerable >> 31 return False if label == "hitlag_frames_left": temp = unpack('<f', mem_update[1])[0] try: self.player[player_int].hitlag_frames_left = int(temp) except ValueError: pass return False if label == "hitstun_frames_left": temp = unpack('<f', mem_update[1])[0] try: self.player[player_int].hitstun_frames_left = int(temp) except ValueError: pass return False if label == "charging_smash": temp = unpack('<I', mem_update[1])[0] if temp == 2: self.player[player_int].charging_smash = True else: self.player[player_int].charging_smash = False return False if label == "jumps_left": temp = unpack('<I', mem_update[1])[0] temp = temp >> 24 #This value is actually the number of jumps USED # so we have to do some quick math to turn this into what we want #TODO = characterstats.maxjumps(self.player[player_int].character) - temp + 1 self.player[player_int].jumps_left = temp return False if label == "on_ground": temp = unpack('<I', mem_update[1])[0] if temp == 0: self.player[player_int].on_ground = True else: self.player[player_int].on_ground = False return False if label == "speed_air_x_self": self.player[player_int].speed_air_x_self = unpack( '<f', mem_update[1])[0] return False if label == "speed_y_self": self.player[player_int].speed_y_self = unpack('<f', mem_update[1])[0] return False if label == "speed_x_attack": self.player[player_int].speed_x_attack = unpack( '<f', mem_update[1])[0] return False if label == "speed_y_attack": self.player[player_int].speed_y_attack = unpack( '<f', mem_update[1])[0] return False if label == "speed_ground_x_self": self.player[player_int].speed_ground_x_self = unpack( '<f', mem_update[1])[0] return False if label == "coin_down": temp = unpack('<I', mem_update[1])[0] temp = temp & 0x000000ff self.player[player_int].coin_down = (temp == 2) return False if label == "stage_select_cursor_x": self.stage_select_cursor_x = unpack('<f', mem_update[1])[0] return False if label == "stage_select_cursor_y": self.stage_select_cursor_y = unpack('<f', mem_update[1])[0] return False if label == "ready_to_start": temp = unpack('>I', mem_update[1])[0] temp = temp & 0x000000ff self.ready_to_start = not bool(temp) return False if label == "controller_status": temp = unpack('>I', mem_update[1])[0] temp = temp & 0x000000ff self.player[player_int].controller_status = enums.ControllerStatus( temp) return False if label == "hitbox_1_size": self.player[player_int].hitbox_1_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_2_size": self.player[player_int].hitbox_2_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_3_size": self.player[player_int].hitbox_3_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_4_size": self.player[player_int].hitbox_4_size = unpack( '<f', mem_update[1])[0] return False if label == "hitbox_1_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_1_status = status return False if label == "hitbox_2_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_2_status = status return False if label == "hitbox_3_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_3_status = status return False if label == "hitbox_4_status": temp = unpack('<I', mem_update[1])[0] status = True if temp == 0: status = False self.player[player_int].hitbox_4_status = status return False if label == "hitbox_1_x": self.player[player_int].hitbox_1_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_1_y": self.player[player_int].hitbox_1_y = unpack('<f', mem_update[1])[0] return False if label == "hitbox_2_x": self.player[player_int].hitbox_2_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_2_y": self.player[player_int].hitbox_2_y = unpack('<f', mem_update[1])[0] return False if label == "hitbox_3_x": self.player[player_int].hitbox_3_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_3_y": self.player[player_int].hitbox_3_y = unpack('<f', mem_update[1])[0] return False if label == "hitbox_4_x": self.player[player_int].hitbox_4_x = unpack('<f', mem_update[1])[0] return False if label == "hitbox_4_y": self.player[player_int].hitbox_4_y = unpack('<f', mem_update[1])[0] return False if label == "projectiles": #Only once per new frame that we get a projectile, clear the list out if self.newframe: self.projectiles.clear() self.i = 0 self.i += 1 self.newframe = False if len(mem_update[1]) < 10: self.projectiles.clear() return False proj = Projectile() proj.x = unpack('>f', mem_update[1][0x4c:0x50])[0] proj.y = unpack('>f', mem_update[1][0x50:0x54])[0] proj.x_speed = unpack('>f', mem_update[1][0x40:0x44])[0] proj.y_speed = unpack('>f', mem_update[1][0x44:0x48])[0] try: proj.subtype = enums.ProjectileSubtype( unpack('>I', mem_update[1][0x10:0x14])[0]) except ValueError: proj.subtype = enums.ProjectileSubtype.UNKNOWN_PROJECTILE self.projectiles.append(proj) return False
def __init__(self, write): if write: self.csvfile = open('framedata.csv', 'a') fieldnames = [ 'character', 'action', 'frame', 'hitbox_1_status', 'hitbox_1_size', 'hitbox_1_x', 'hitbox_1_y', 'hitbox_2_status', 'hitbox_2_size', 'hitbox_2_x', 'hitbox_2_y', 'hitbox_3_status', 'hitbox_3_size', 'hitbox_3_x', 'hitbox_3_y', 'hitbox_4_status', 'hitbox_4_size', 'hitbox_4_x', 'hitbox_4_y' ] self.writer = csv.DictWriter(self.csvfile, fieldnames=fieldnames) self.writer.writeheader() self.rows = [] #Read the existing framedata path = os.path.dirname(os.path.realpath(__file__)) self.framedata = defaultdict( lambda: defaultdict(lambda: defaultdict(dict))) with open(path + "/framedata.csv") as csvfile: # A list of dicts containing the frame data csvreader = list(csv.DictReader(csvfile)) # Build a series of nested dicts for faster read access for frame in csvreader: # Pull out the character, action, and frame character = enums.Character(int(frame["character"])) action = enums.Action(int(frame["action"])) action_frame = int(frame["frame"]) self.framedata[character][action][action_frame] = \ {"hitbox_1_status": frame["hitbox_1_status"] == "True", \ "hitbox_1_size": float(frame["hitbox_1_size"]), \ "hitbox_1_x": float(frame["hitbox_1_x"]), \ "hitbox_1_y": float(frame["hitbox_1_y"]), \ "hitbox_2_status": frame["hitbox_2_status"] == "True", \ "hitbox_2_size": float(frame["hitbox_2_size"]), \ "hitbox_2_x": float(frame["hitbox_2_x"]), \ "hitbox_2_y": float(frame["hitbox_2_y"]), \ "hitbox_3_status": frame["hitbox_3_status"] == "True", \ "hitbox_3_size": float(frame["hitbox_3_size"]), \ "hitbox_3_x": float(frame["hitbox_3_x"]), \ "hitbox_3_y": float(frame["hitbox_3_y"]), \ "hitbox_4_status": frame["hitbox_4_status"] == "True", \ "hitbox_4_size": float(frame["hitbox_4_size"]), \ "hitbox_4_x": float(frame["hitbox_4_x"]), \ "hitbox_4_y": float(frame["hitbox_4_y"])} #Read the action state data csv path = os.path.dirname(os.path.realpath(__file__)) with open(path + "/actiondata.csv") as csvfile: # A list of dicts containing the frame data self.actiondata = list(csv.DictReader(csvfile)) #read the character data csv self.characterdata = dict() path = os.path.dirname(os.path.realpath(__file__)) with open(path + "/characterdata.csv") as csvfile: reader = csv.DictReader(csvfile) for line in reader: del line["Character"] #Convert all fields to numbers for key, value in line.items(): line[key] = float(value) self.characterdata[enums.Character( line["CharacterIndex"])] = line
def __post_frame(self, gamestate, event_bytes): gamestate.stage = self._current_stage gamestate.frame = np.ndarray((1,), ">i", event_bytes, 0x1)[0] controller_port = np.ndarray((1,), ">B", event_bytes, 0x5)[0] + 1 if controller_port not in gamestate.player: gamestate.player[controller_port] = PlayerState() playerstate = gamestate.player[controller_port] playerstate.x = np.ndarray((1,), ">f", event_bytes, 0xa)[0] playerstate.y = np.ndarray((1,), ">f", event_bytes, 0xe)[0] playerstate.character = enums.Character(np.ndarray((1,), ">B", event_bytes, 0x7)[0]) try: playerstate.action = enums.Action(np.ndarray((1,), ">H", event_bytes, 0x8)[0]) except ValueError: playerstate.action = enums.Action.UNKNOWN_ANIMATION # Melee stores this in a float for no good reason. So we have to convert playerstate.facing = np.ndarray((1,), ">f", event_bytes, 0x12)[0] > 0 playerstate.percent = int(np.ndarray((1,), ">f", event_bytes, 0x16)[0]) playerstate.shield_strength = np.ndarray((1,), ">f", event_bytes, 0x1A)[0] playerstate.stock = np.ndarray((1,), ">B", event_bytes, 0x21)[0] playerstate.action_frame = int(np.ndarray((1,), ">f", event_bytes, 0x22)[0]) # Extract the bit at mask 0x20 try: bitflags2 = np.ndarray((1,), ">B", event_bytes, 0x27)[0] playerstate.hitlag = bool(bitflags2 & 0x20) except TypeError: playerstate.hitlag = False try: playerstate.hitstun_frames_left = int(np.ndarray((1,), ">f", event_bytes, 0x2B)[0]) except TypeError: playerstate.hitstun_frames_left = 0 except ValueError: playerstate.hitstun_frames_left = 0 try: playerstate.on_ground = not bool(np.ndarray((1,), ">B", event_bytes, 0x2F)[0]) except TypeError: playerstate.on_ground = True try: playerstate.jumps_left = np.ndarray((1,), ">B", event_bytes, 0x32)[0] except TypeError: playerstate.jumps_left = 1 try: playerstate.invulnerable = int(np.ndarray((1,), ">B", event_bytes, 0x34)[0]) != 0 except TypeError: playerstate.invulnerable = False try: playerstate.speed_air_x_self = np.ndarray((1,), ">f", event_bytes, 0x35)[0] except TypeError: playerstate.speed_air_x_self = 0 try: playerstate.speed_y_self = np.ndarray((1,), ">f", event_bytes, 0x39)[0] except TypeError: playerstate.speed_y_self = 0 try: playerstate.speed_x_attack = np.ndarray((1,), ">f", event_bytes, 0x3D)[0] except TypeError: playerstate.speed_x_attack = 0 try: playerstate.speed_y_attack = np.ndarray((1,), ">f", event_bytes, 0x41)[0] except TypeError: playerstate.speed_y_attack = 0 try: playerstate.speed_ground_x_self = np.ndarray((1,), ">f", event_bytes, 0x45)[0] except TypeError: playerstate.speed_ground_x_self = 0 # Keep track of a player's invulnerability due to respawn or ledge grab if controller_port in self._prev_gamestate.player: playerstate.invulnerability_left = max(0, self._prev_gamestate.player[controller_port].invulnerability_left - 1) if playerstate.action == Action.ON_HALO_WAIT: playerstate.invulnerability_left = 120 # Don't give invulnerability to the first descent if playerstate.action == Action.ON_HALO_DESCENT and gamestate.frame > 150: playerstate.invulnerability_left = 120 if playerstate.action == Action.EDGE_CATCHING and playerstate.action_frame == 1: playerstate.invulnerability_left = 36 # The pre-warning occurs when we first start a dash dance. if controller_port in self._prev_gamestate.player: if playerstate.action == Action.DASHING and \ self._prev_gamestate.player[controller_port].action not in [Action.DASHING, Action.TURNING]: playerstate.moonwalkwarning = True # Take off the warning if the player does an action other than dashing if playerstate.action != Action.DASHING: playerstate.moonwalkwarning = False # "off_stage" helper if (abs(playerstate.x) > stages.EDGE_GROUND_POSITION[gamestate.stage] or \ playerstate.y < -6) and not playerstate.on_ground: playerstate.off_stage = True else: playerstate.off_stage = False # ECB top edge, x ecb_top_x = 0 ecb_top_y = 0 try: ecb_top_x = np.ndarray((1,), ">f", event_bytes, 0x49)[0] except TypeError: ecb_top_x = 0 # ECB Top edge, y try: ecb_top_y = np.ndarray((1,), ">f", event_bytes, 0x4D)[0] except TypeError: ecb_top_y = 0 playerstate.ecb_top = (ecb_top_x, ecb_top_y) # ECB bottom edge, x coord ecb_bot_x = 0 ecb_bot_y = 0 try: ecb_bot_x = np.ndarray((1,), ">f", event_bytes, 0x51)[0] except TypeError: ecb_bot_x = 0 # ECB Bottom edge, y coord try: ecb_bot_y = np.ndarray((1,), ">f", event_bytes, 0x55)[0] except TypeError: ecb_bot_y = 0 playerstate.ecb_bottom = (ecb_bot_x, ecb_bot_y) # ECB left edge, x coord ecb_left_x = 0 ecb_left_y = 0 try: ecb_left_x = np.ndarray((1,), ">f", event_bytes, 0x59)[0] except TypeError: ecb_left_x = 0 # ECB left edge, y coord try: ecb_left_y = np.ndarray((1,), ">f", event_bytes, 0x5D)[0] except TypeError: ecb_left_y = 0 playerstate.ecb_left = (ecb_left_x, ecb_left_y) # ECB right edge, x coord ecb_right_x = 0 ecb_right_y = 0 try: ecb_right_x = np.ndarray((1,), ">f", event_bytes, 0x61)[0] except TypeError: ecb_right_x = 0 # ECB right edge, y coord try: ecb_right_y = np.ndarray((1,), ">f", event_bytes, 0x65)[0] except TypeError: ecb_right_y = 0 playerstate.ecb_right = (ecb_right_x, ecb_right_y) if self._allow_old_version: self._frame = gamestate.frame