예제 #1
0
 def _load_local_player(self) -> int:
     """
     Returns the local player object out of uWorld.UGameInstance.
     Used to get the players coordinates before reading any actors
     :rtype: int
     :return: Memory address of the local player object
     """
     game_instance = self.rm.read_ptr(
         self.world_address + OFFSETS.get('UWorld.OwningGameInstance'))
     local_player = self.rm.read_ptr(
         game_instance + OFFSETS.get('UGameInstance.LocalPlayers'))
     return self.rm.read_ptr(local_player)
예제 #2
0
    def __init__(self):
        """
        Upon initialization of this object, we want to find the base address
        for the SoTGame.exe, then begin to load in the static addresses for the
        uWorld, gName, gObject, and uLevel objects.

        We also poll the local_player object to get a first round of coords.
        When running read_actors, we update the local players coordinates
        using the camera-manager object

        Also initialize a number of class variables which help us cache some
        basic information
        """
        self.rm = ReadMemory("SoTGame.exe")
        base_address = self.rm.base_address
        logging.info(f"Process ID: {self.rm.pid}")

        u_world_offset = self.rm.read_ulong(
            base_address + self.rm.u_world_base + 3
        )
        u_world = base_address + self.rm.u_world_base + u_world_offset + 7
        self.world_address = self.rm.read_ptr(u_world)

        g_name_offset = self.rm.read_ulong(
            base_address + self.rm.g_name_base + 3
        )
        g_name = base_address + self.rm.g_name_base + g_name_offset + 7
        logging.info(f"SoT gName Address: {hex(g_name)}")
        self.g_name = self.rm.read_ptr(g_name)

        g_objects_offset = self.rm.read_ulong(
            base_address + self.rm.g_object_base + 2
        )
        g_objects = base_address + self.rm.g_object_base + g_objects_offset + 22
        logging.info(f"SoT gObject Address: {hex(g_objects)}")
        self.g_objects = self.rm.read_ptr(g_objects)

        self.u_level = self.rm.read_ptr(self.world_address +
                                        OFFSETS.get('UWorld.PersistentLevel'))

        self.u_local_player = self._load_local_player()
        self.player_controller = self.rm.read_ptr(
            self.u_local_player + OFFSETS.get('ULocalPlayer.PlayerController')
        )

        self.my_coords = self._coord_builder(self.u_local_player)
        self.my_coords['fov'] = 90

        self.actor_name_map = {}
        self.server_players = []
        self.display_objects = []
예제 #3
0
 def update_my_coords(self):
     """
     Function to update the players coordinates and camera information
     storing that new info back into the my_coords field. Necessary as
     we dont always run a full scan and we need a way to update ourselves
     """
     manager = self.rm.read_ptr(
         self.player_controller + OFFSETS.get('APlayerController.CameraManager')
     )
     self.my_coords = self._coord_builder(
         manager,
         OFFSETS.get('APlayerCameraManager.CameraCache')
         + OFFSETS.get('FCameraCacheEntry.FMinimalViewInfo'),
         fov=True)
예제 #4
0
 def _get_root_comp_address(self, address: int) -> int:
     """
     Function to get an AActor's root component memory address
     :param address: the base address for a given AActor
     :rtype: int
     :return: the address of an AActors root component
     """
     return self.m_r.read_ptr(address + OFFSETS.get("AActor.rootComponent"))
 def _get_actor_id(self, address: int) -> int:
     """
     Function to get the AActor's ID, used to validate the ID hasn't changed
     while running a "quick" scan
     :param int address: the base address for a given AActor
     :rtype: int
     :return: The AActors ID
     """
     return self.rm.read_int(address + OFFSETS.get('AActor.actorId'))
예제 #6
0
    def read_actors(self):
        """
        Main game loop. Is responsible for determining how many actors to scan
        and calling assisting functions to interpret data about said actors.
        Stores all relevent information in class variables
        """
        self.run_info = []

        actor_raw = self.rm.read_bytes(self.u_level + 0xa0, 0xC)
        actor_data = struct.unpack("<Qi", actor_raw)

        self.server_players = []
        for x in range(0, actor_data[1]):
            # We start by getting the ActorID for a given actor, and comparing
            # that ID to a list of "known" id's we cache in self.actor_name_map
            raw_name = ""
            actor_address = self.rm.read_ptr(actor_data[0] + (x * 0x8))
            actor_id = self.rm.read_int(actor_address +
                                        OFFSETS.get('AActor.actorId'))
            if actor_id not in self.actor_name_map and actor_id != 0:
                try:
                    raw_name = self._read_name(actor_id)
                    self.actor_name_map[actor_id] = raw_name
                except Exception as e:
                    print(str(e))
            elif actor_id in self.actor_name_map:
                raw_name = self.actor_name_map.get(actor_id)

            # Ignore anything we cannot find a name for
            if not raw_name:
                continue

            # AthenaPlayerCameraManager contains information on the local
            # players camera object, namely coordinates and camera information.
            # Use that data and set self.my_coordinates to the camera's coords
            if raw_name == "BP_AthenaPlayerCameraManager_C":
                self.my_coords = self._coord_builder(actor_address + 0x450,
                                                     0x0,
                                                     fov=True)
                continue

            # If we have Ship ESP enabled in helpers.py, and the name of the
            # actor is in our mapping.py ship_keys object, interpret the actor
            # as a ship
            if CONFIG.get('SHIPS_ENABLED') and raw_name in ship_keys:
                self.read_ships(actor_address, raw_name)

            # If we have the world players enabled in helpers.py, and the name
            # of the actor is AthenaPlayerState, we interpret the actor as a
            # player on the server.
            # NOTE: This will NOT give us information on nearby players for the
            # sake of ESP
            elif CONFIG.get('WORLD_PLAYERS_ENABLED'
                            ) and "AthenaPlayerState" in raw_name:
                self.read_world_players(actor_address)
예제 #7
0
    def read_world_players(self, actor_address):
        """
        Reads information about an AthenaPlayerState actor (a server-level
        player object), to obtain into on who is on the server. Append the user
        to the list of players on the server for a given run
        :param actor_address: The memory address which the actor begins at
        """
        player_name_location = self.rm.read_ptr(
            actor_address + OFFSETS.get('APlayerState.PlayerName'))
        player_name = self.rm.read_name_string(player_name_location)

        if player_name and player_name not in self.server_players:
            self.server_players.append(player_name)
예제 #8
0
    def read_actors(self):
        """
        Represents a full scan of every actor within our render distance.
        Will create an object for each type of object we are interested in,
        and store it in a class variable (display_objects).
        Then our main game loop updates those objects
        """
        # On a full run, start by cleaning up all the existing text renders
        for display_ob in self.display_objects:
            try:
                display_ob.text_render.delete()
            except:
                continue
            try:
                display_ob.icon.delete()
            except:
                continue
        self.display_objects = []
        self.update_my_coords()

        actor_raw = self.rm.read_bytes(self.u_level + 0xa0, 0xC)
        actor_data = struct.unpack("<Qi", actor_raw)

        self.server_players = []
        for x in range(0, actor_data[1]):
            # We start by getting the ActorID for a given actor, and comparing
            # that ID to a list of "known" id's we cache in self.actor_name_map
            raw_name = ""
            actor_address = self.rm.read_ptr(actor_data[0] + (x * 0x8))
            actor_id = self.rm.read_int(
                actor_address + OFFSETS.get('AActor.actorId')
            )

            # We save a mapping of actor id to actor name for the sake of
            # saving memory calls
            if actor_id not in self.actor_name_map and actor_id != 0:
                try:
                    raw_name = self.rm.read_gname(actor_id)
                    self.actor_name_map[actor_id] = raw_name
                except Exception as e:
                    logger.error(f"Unable to find actor name: {e}")
            elif actor_id in self.actor_name_map:
                raw_name = self.actor_name_map.get(actor_id)

            # Ignore anything we cannot find a name for
            if not raw_name:
                continue

            # If we have Ship ESP enabled in helpers.py, and the name of the
            # actor is in our mapping.py ship_keys object, interpret the actor
            # as a ship
            if CONFIG.get('SHIPS_ENABLED') and raw_name in ship_keys:
                ship = Ship(self.rm, actor_id, actor_address, self.my_coords,
                            raw_name)
                # if "Near" not in ship.name and ship.distance < 1720:
                #     continue
                # else:
                self.display_objects.append(ship)

            # If we have the world players enabled in helpers.py, and the name
            # of the actor is AthenaPlayerState, we interpret the actor as a
            # player on the server.
            # NOTE: This will NOT give us information on nearby players for the
            # sake of ESP
            elif CONFIG.get('WORLD_PLAYERS_ENABLED') and "AthenaPlayerState" in raw_name:
                self.read_world_players(actor_address)