Skip to content

Commit a0bf374

Browse files
committed
Migrate to Crews over AthenaPlayerState
1 parent e4f5634 commit a0bf374

File tree

7 files changed

+150
-20
lines changed

7 files changed

+150
-20
lines changed

Modules/crews.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
"""
2+
@Author https://github.com/DougTheDruid
3+
@Source https://github.com/DougTheDruid/SoT-ESP-Framework
4+
"""
5+
6+
from helpers import *
7+
import struct
8+
from Modules.display_object import DisplayObject
9+
from helpers import crew_tracker
10+
11+
12+
class Crews(DisplayObject):
13+
"""
14+
Class to generate information about the crews current on our server
15+
"""
16+
17+
def __init__(self, memory_reader, actor_id, address):
18+
"""
19+
be located at the screen coordinated + our text_offsets from helpers.py
20+
The function of this class is to collect all of the data about the crews
21+
currently on our server. `CrewService` is effectively just a list of
22+
`Crew` structures in memory, which we will iterating over to collect the
23+
requisite data.
24+
25+
Previously, you were able to collect player names from this data but we
26+
cannot any longer. Instead we will simply use it to get a count of how
27+
many players are on the server and on each crew.
28+
29+
:param memory_reader: The SoT MemoryHelper Object we use to read memory
30+
:param actor_id: The actor ID of our CrewService. Used to validate if
31+
there is an unexpected change
32+
:param address: The address in which our CrewService lives
33+
"""
34+
# Initialize our super-class
35+
super().__init__(memory_reader)
36+
37+
self.rm = memory_reader
38+
self.actor_id = actor_id
39+
self.address = address
40+
41+
# Collect and store information about the crews on the server
42+
self.crew_info = self._get_crews_info()
43+
44+
# Sum all of the crew sizes into our total_players variable
45+
self.total_players = sum(crew['size'] for crew in self.crew_info)
46+
47+
# All of our actual display information & rendering
48+
self.crew_str = self._built_text_string()
49+
50+
# Used to track if the display object needs to be removed
51+
self.to_delete = False
52+
53+
def _built_text_string(self):
54+
"""
55+
Generates a string used for rendering. Separate function in the event
56+
you need to add more data or want to change formatting
57+
"""
58+
output = ""
59+
for x in range(len(self.crew_info)):
60+
# We store all of the crews in a tracker dictionary. This allows us
61+
# to assign each crew a "Short"-ID based on count on the server.
62+
short_id = crew_tracker.get(self.crew_info[x]['guid'], None)
63+
output += f" Crew #{short_id} - {self.crew_info[x]['size']} Pirates\n"
64+
65+
return output
66+
67+
def _get_crews_info(self):
68+
# Find the starting address for our Crews TArray
69+
crew_raw = self.rm.read_bytes(self.address + OFFSETS.get('CrewService.Crews'), 16)
70+
71+
# (Crews_Data<Array>, Crews length, Crews max)
72+
crews = struct.unpack("<Qii", crew_raw)
73+
74+
# Will contain all of our condensed Crew Data
75+
crews_data = []
76+
77+
# For each crew on the server
78+
for x in range(0, crews[1]):
79+
# Each crew has a unique ID composed of four ints, maybe be useful if you
80+
# add functionality around Crews on your own
81+
crew_guid_raw = self.rm.read_bytes(crews[0] + (OFFSETS.get('Crew.Size') * x), 16)
82+
crew_guid = struct.unpack("<iiii", crew_guid_raw)
83+
84+
# Read the TArray of Players on the the specific Crew, used to determine
85+
# Crew size
86+
crew_raw = self.rm.read_bytes(
87+
crews[0] + OFFSETS.get('Crew.Players') + (OFFSETS.get('Crew.Size') * x), 16
88+
)
89+
90+
# Players<Array>, current length, max length
91+
crew = struct.unpack("<Qii", crew_raw)
92+
93+
# If our crew has more than 0 people on it, we care about it, so we add it to our tracker
94+
if crew[1] > 0:
95+
crew_data = {
96+
"guid": crew_guid,
97+
"size": crew[1]
98+
}
99+
crews_data.append(crew_data)
100+
if crew_guid not in crew_tracker:
101+
crew_tracker[crew_guid] = len(crew_tracker)+1
102+
return crews_data
103+
104+
def update(self, my_coords):
105+
"""
106+
A generic method to update all the interesting data about the
107+
crews on our server. To be called when seeking to perform an update on
108+
the CrewService actor without reinitializing our class.
109+
110+
1. Determine if our actor is what we expect it to be
111+
2. Pull the latest crew information
112+
3. Update our strings accordingly
113+
"""
114+
if self._get_actor_id(self.address) != self.actor_id:
115+
self.to_delete = True
116+
return
117+
self.crew_info = self._get_crews_info()
118+
self.crew_str = self._built_text_string()

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ of guidance as to where to look to find those bugs.
5656
This hack has the following implemented:
5757
- Get the location of ships in render distance and display them on screen
5858
- Display a count of the players on the server
59-
- Display a list of all players on the server, and update the list correctly
59+
- Display a list of all the crews on the server, how many players are on that crew, and update this list regularly
6060

6161
## Your First Additions
6262
Once you have accomplished getting the framework working as intended, you may find yourself asking, what do I do next?

helpers.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313

1414
# True=Enabled & False=Disabled for each of the config items
1515
CONFIG = {
16-
"WORLD_PLAYERS_ENABLED": True,
16+
"CREWS_ENABLED": True,
1717
"SHIPS_ENABLED": False
1818
}
1919

20+
#
21+
crew_tracker = {}
22+
2023
version = "1.2.0"
2124

2225
# Config specification for logging file

main.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,9 @@ def on_draw():
9292
"""
9393
window.clear()
9494

95-
# Update our player count Label & player list
96-
player_count.text = f"Player Count: {len(smr.server_players)}"
97-
# player_list.text = "\n".join(smr.server_players)
95+
# Update our player count Label & crew list
96+
player_count.text = f"Player Count: {smr.crew_data.total_players}"
97+
# crew_list.text = smr.crew_data.crew_str
9898

9999
# Draw our main batch & FPS counter at the bottom left
100100
main_batch.draw()
@@ -122,10 +122,11 @@ def on_draw():
122122
y=SOT_WINDOW_H * 0.9, batch=main_batch)
123123

124124
# The label for showing all players on the server under the count
125+
# This purely INITIALIZES it does not inherently update automatically
125126
if False: # pylint: disable=using-constant-test
126-
player_list = Label("\n".join(smr.server_players), x=SOT_WINDOW_W * 0.85,
127-
y=(SOT_WINDOW_H-25) * 0.9, batch=main_batch, width=300,
128-
multiline=True)
127+
crew_list = Label(f"{smr.crew_data.crew_str}", x=SOT_WINDOW_W * 0.85,
128+
y=(SOT_WINDOW_H-25) * 0.9, batch=main_batch, width=300,
129+
multiline=True)
129130
# Note: The width of 300 is the max pixel width of a single line
130131
# before auto-wrapping the text to the next line. Updated in on_draw()
131132

mapping.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,16 @@
2828
},
2929

3030
"BP_AISmallShipTemplate_C": {
31-
"Name": "Ghost Sloop (Near)",
31+
"Name": "Skeleton Sloop (Near)",
3232
},
3333
"BP_AISmallShipNetProxy_C": {
34-
"Name": "Ghost Sloop",
34+
"Name": "Skeleton Sloop",
3535
},
3636
"BP_AILargeShipTemplate_C": {
37-
"Name": "Ghost Galleon (Near)",
37+
"Name": "Skeleton Galleon (Near)",
3838
},
3939
"BP_AILargeShipNetProxy_C": {
40-
"Name": "Ghost Galleon",
40+
"Name": "Skeleton Galleon",
4141
},
4242
# "BP_AggressiveGhostShip_C": {
4343
# "Name": "Flameheart Galleon",

offsets.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
{
22
"Actor.actorId": 24,
3-
"Actor.rootComponent": 368,
3+
"CrewService.Crews": 1184,
4+
"Crew.Players": 32,
5+
"Crew.Size": 152,
6+
"Actor.rootComponent": 360,
47
"CameraCacheEntry.MinimalViewInfo": 16,
58
"GameInstance.LocalPlayers": 56,
69
"LocalPlayer.PlayerController": 48,
710
"PlayerCameraManager.CameraCache": 1088,
8-
"PlayerController.CameraManager": 1120,
9-
"PlayerState.PlayerName": 984,
11+
"PlayerController.CameraManager": 1112,
12+
"PlayerState.PlayerName": 976,
1013
"SceneComponent.ActorCoordinates": 300,
1114
"World.OwningGameInstance": 448,
1215
"World.PersistentLevel": 48

sot_hack.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from mapping import ship_keys
1111
from helpers import OFFSETS, CONFIG, logger
1212
from Modules.ship import Ship
13+
from Modules.crews import Crews
1314

1415

1516
class SoTMemoryReader:
@@ -69,6 +70,8 @@ def __init__(self):
6970
self.actor_name_map = {}
7071
self.server_players = []
7172
self.display_objects = []
73+
self.crew_data = None
74+
7275

7376
def _load_local_player(self) -> int:
7477
"""
@@ -190,13 +193,15 @@ def read_actors(self):
190193
# else:
191194
self.display_objects.append(ship)
192195

193-
# If we have the world players enabled in helpers.py, and the name
194-
# of the actor is AthenaPlayerState, we interpret the actor as a
195-
# player on the server.
196+
# If we have the crews data enabled in helpers.py and the name
197+
# of the actor is CrewService, we create a class based on that Crew
198+
# data to generate information about people on the server
196199
# NOTE: This will NOT give us information on nearby players for the
197200
# sake of ESP
198-
elif CONFIG.get('WORLD_PLAYERS_ENABLED') and "AthenaPlayerState" in raw_name:
199-
self.read_world_players(actor_address)
201+
elif CONFIG.get('CREWS_ENABLED') and raw_name == "CrewService":
202+
self.crew_data = Crews(self.rm, actor_id, actor_address)
203+
# elif CONFIG.get('WORLD_PLAYERS_ENABLED') and "AthenaPlayerState" in raw_name:
204+
# self.read_world_players(actor_address)
200205

201206
def read_world_players(self, actor_address):
202207
"""

0 commit comments

Comments
 (0)