robloxmemoryapi 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,87 @@
1
+ import platform
2
+ import math
3
+
4
+ __all__ = ["RobloxRandom", "RobloxGameClient", "__version__"]
5
+ __version__ = "0.1.0"
6
+
7
+
8
+ class RobloxRandom:
9
+ MULT = 6364136223846793005
10
+ INC = 105
11
+ MASK64 = (1 << 64) - 1
12
+
13
+ def __init__(self, seed):
14
+ s = math.floor(seed)
15
+
16
+ self._state = 0
17
+ self._inc = RobloxRandom.INC
18
+ self._next_internal() # warm-up #1
19
+ self._state = (self._state + s) & RobloxRandom.MASK64
20
+ self._next_internal() # warm-up #2
21
+
22
+ def _next_internal(self):
23
+ old = self._state
24
+ self._state = (old * RobloxRandom.MULT + self._inc) & RobloxRandom.MASK64
25
+ x = ((old >> 18) ^ old) >> 27
26
+ r = old >> 59
27
+ return ((x >> r) | (x << ((32 - r) & 31))) & 0xFFFFFFFF
28
+
29
+ def _next_fraction64(self):
30
+ lo = self._next_internal()
31
+ hi = self._next_internal()
32
+ bits = (hi << 32) | lo
33
+ return bits / 2**64
34
+
35
+ def NextNumber(self, minimum=0.0, maximum=1.0):
36
+ frac = self._next_fraction64()
37
+ return minimum + frac * (maximum - minimum)
38
+
39
+ def NextInteger(self, a, b=None):
40
+ if b is None:
41
+ u = a
42
+ r = self._next_internal()
43
+ return ((u * r) >> 32) + 1
44
+ else:
45
+ lo, hi = (a, b) if a <= b else (b, a)
46
+ u = hi - lo + 1
47
+ r = self._next_internal()
48
+ return ((u * r) >> 32) + lo
49
+
50
+
51
+ class RobloxGameClient:
52
+ def __init__(self, pid: int = None, process_name: str = "RobloxPlayerBeta.exe"):
53
+ if platform.system() != "Windows":
54
+ self.failed = True
55
+ return
56
+
57
+ from .utils.memory import (
58
+ EvasiveProcess,
59
+ PROCESS_QUERY_INFORMATION,
60
+ PROCESS_VM_READ,
61
+ get_pid_by_name,
62
+ )
63
+
64
+ if pid is None:
65
+ self.pid = get_pid_by_name(process_name)
66
+ else:
67
+ self.pid = pid
68
+
69
+ if self.pid is None or self.pid == 0:
70
+ raise ValueError("Failed to get PID.")
71
+
72
+ self.memory_module = EvasiveProcess(self.pid, PROCESS_VM_READ | PROCESS_QUERY_INFORMATION)
73
+ self.failed = False
74
+
75
+ def close(self):
76
+ self.memory_module.close()
77
+
78
+ @property
79
+ def DataModel(self):
80
+ if platform.system() != "Windows":
81
+ raise RuntimeError("This module is only compatible with Windows.")
82
+ elif self.failed:
83
+ raise RuntimeError("There was an error while getting access to memory. Please try again later.")
84
+
85
+ from .utils.rbx.instance import DataModel
86
+ return DataModel(self.memory_module)
87
+
@@ -0,0 +1,6 @@
1
+ {
2
+ "Text": "0xC10",
3
+ "Character": "0x328",
4
+ "PrimaryPart": "0x268"
5
+ }
6
+
@@ -0,0 +1,2 @@
1
+ # Utility subpackage for memory access and offsets
2
+
@@ -0,0 +1,275 @@
1
+ import ctypes
2
+ import struct
3
+ from ctypes import wintypes
4
+
5
+ ntdll = ctypes.WinDLL('ntdll.dll')
6
+ psapi = ctypes.WinDLL('psapi.dll')
7
+ kernel32 = ctypes.WinDLL('kernel32.dll')
8
+
9
+ kernel32.VirtualAlloc.restype = wintypes.LPVOID
10
+ kernel32.VirtualAlloc.argtypes = [wintypes.LPVOID, ctypes.c_size_t, wintypes.DWORD, wintypes.DWORD]
11
+ kernel32.GetProcAddress.restype = wintypes.LPVOID
12
+ kernel32.GetProcAddress.argtypes = [wintypes.HMODULE, wintypes.LPCSTR]
13
+
14
+ NTSTATUS = wintypes.LONG
15
+ HANDLE = wintypes.HANDLE
16
+ DWORD = wintypes.DWORD
17
+ LPVOID = wintypes.LPVOID
18
+ HMODULE = wintypes.HMODULE
19
+ BOOL = wintypes.BOOL
20
+
21
+ PROCESS_QUERY_INFORMATION = 0x0400
22
+ PROCESS_VM_READ = 0x0010
23
+ LIST_MODULES_ALL = 0x03
24
+ STATUS_SUCCESS = 0
25
+ MEM_COMMIT = 0x1000
26
+ MEM_RESERVE = 0x2000
27
+ PAGE_EXECUTE_READWRITE = 0x40
28
+ NTDLL_HANDLE = ntdll._handle
29
+
30
+ class CLIENT_ID(ctypes.Structure):
31
+ _fields_ = [
32
+ ("UniqueProcess", HANDLE),
33
+ ("UniqueThread", HANDLE),
34
+ ]
35
+
36
+ class OBJECT_ATTRIBUTES(ctypes.Structure):
37
+ _fields_ = [
38
+ ("Length", wintypes.ULONG),
39
+ ("RootDirectory", HANDLE),
40
+ ("ObjectName", LPVOID),
41
+ ("Attributes", wintypes.ULONG),
42
+ ("SecurityDescriptor", LPVOID),
43
+ ("SecurityQualityOfService", LPVOID),
44
+ ]
45
+
46
+ def get_syscall_number(function_name: str) -> int | None:
47
+ func_address = kernel32.GetProcAddress(NTDLL_HANDLE, function_name.encode('ascii'))
48
+ if not func_address:
49
+ return None
50
+ buffer = (ctypes.c_ubyte * 8).from_address(func_address)
51
+ if tuple(buffer[0:4]) == (0x4c, 0x8b, 0xd1, 0xb8):
52
+ return int.from_bytes(bytes(buffer[4:8]), 'little')
53
+ return None
54
+
55
+ def create_syscall_function(syscall_number, func_prototype):
56
+ assembly_stub = b'\x4C\x8B\xD1' + \
57
+ b'\xB8' + syscall_number.to_bytes(4, 'little') + \
58
+ b'\x0F\x05' + \
59
+ b'\xC3'
60
+
61
+ exec_mem = kernel32.VirtualAlloc(None, len(assembly_stub), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)
62
+ if not exec_mem:
63
+ raise ctypes.WinError(ctypes.get_last_error())
64
+ ctypes.memmove(exec_mem, assembly_stub, len(assembly_stub))
65
+ return func_prototype(exec_mem)
66
+
67
+ NtOpenProcessProto = ctypes.WINFUNCTYPE(
68
+ NTSTATUS,
69
+ ctypes.POINTER(HANDLE),
70
+ DWORD,
71
+ ctypes.POINTER(OBJECT_ATTRIBUTES),
72
+ ctypes.POINTER(CLIENT_ID)
73
+ )
74
+ NtReadVirtualMemoryProto = ctypes.WINFUNCTYPE(
75
+ NTSTATUS,
76
+ HANDLE,
77
+ LPVOID,
78
+ LPVOID,
79
+ ctypes.c_ulong,
80
+ ctypes.POINTER(ctypes.c_ulong)
81
+ )
82
+ NtCloseProto = ctypes.WINFUNCTYPE(NTSTATUS, HANDLE)
83
+
84
+ syscall_id_open = get_syscall_number("NtOpenProcess")
85
+ syscall_id_read = get_syscall_number("NtReadVirtualMemory")
86
+ syscall_id_close = get_syscall_number("NtClose")
87
+
88
+ if not all([syscall_id_open, syscall_id_read, syscall_id_close]):
89
+ raise RuntimeError("Could not find required syscall numbers.")
90
+
91
+ nt_open_process_syscall = create_syscall_function(syscall_id_open, NtOpenProcessProto)
92
+ nt_read_virtual_memory_syscall = create_syscall_function(syscall_id_read, NtReadVirtualMemoryProto)
93
+ nt_close_syscall = create_syscall_function(syscall_id_close, NtCloseProto)
94
+
95
+ psapi.EnumProcessModulesEx.argtypes = [
96
+ HANDLE,
97
+ ctypes.POINTER(HMODULE),
98
+ DWORD,
99
+ ctypes.POINTER(DWORD),
100
+ DWORD
101
+ ]
102
+ psapi.EnumProcessModulesEx.restype = BOOL
103
+
104
+ def _get_module_base(process_handle: HANDLE) -> int:
105
+ try:
106
+ modules_arr_size = 256
107
+ modules_arr = (HMODULE * modules_arr_size)()
108
+ needed = DWORD(0)
109
+ psapi.EnumProcessModulesEx(
110
+ process_handle,
111
+ modules_arr,
112
+ ctypes.sizeof(modules_arr),
113
+ ctypes.byref(needed),
114
+ LIST_MODULES_ALL
115
+ )
116
+ if needed.value > ctypes.sizeof(modules_arr):
117
+ new_size = needed.value // ctypes.sizeof(HMODULE)
118
+ modules_arr = (HMODULE * new_size)()
119
+ success = psapi.EnumProcessModulesEx(
120
+ process_handle,
121
+ modules_arr,
122
+ ctypes.sizeof(modules_arr),
123
+ ctypes.byref(needed),
124
+ LIST_MODULES_ALL
125
+ )
126
+ if not success:
127
+ return 0
128
+ if needed.value > 0:
129
+ return modules_arr[0] if modules_arr[0] else 0
130
+ return 0
131
+ except Exception as e:
132
+ print(f"An exception occurred in _get_module_base: {e}")
133
+ return 0
134
+
135
+ def get_pid_by_name(process_name: str) -> int:
136
+ class PROCESSENTRY32(ctypes.Structure):
137
+ _fields_ = [
138
+ ("dwSize", DWORD),
139
+ ("cntUsage", DWORD),
140
+ ("th32ProcessID", DWORD),
141
+ ("th32DefaultHeapID", ctypes.POINTER(wintypes.ULONG)),
142
+ ("th32ModuleID", DWORD),
143
+ ("cntThreads", DWORD),
144
+ ("th32ParentProcessID", DWORD),
145
+ ("pcPriClassBase", wintypes.LONG),
146
+ ("dwFlags", DWORD),
147
+ ("szExeFile", wintypes.CHAR * 260)
148
+ ]
149
+ snapshot = kernel32.CreateToolhelp32Snapshot(2, 0)
150
+ if not snapshot or snapshot == wintypes.HANDLE(-1).value:
151
+ raise ctypes.WinError()
152
+ entry = PROCESSENTRY32()
153
+ entry.dwSize = ctypes.sizeof(PROCESSENTRY32)
154
+ try:
155
+ if not kernel32.Process32First(snapshot, ctypes.byref(entry)):
156
+ return 0
157
+ while True:
158
+ if entry.szExeFile.decode('utf-8', errors='ignore') == process_name:
159
+ return entry.th32ProcessID
160
+ if not kernel32.Process32Next(snapshot, ctypes.byref(entry)):
161
+ break
162
+ finally:
163
+ kernel32.CloseHandle(snapshot)
164
+ return 0
165
+
166
+ class EvasiveProcess:
167
+ def __init__(self, pid: int, access: DWORD):
168
+ self.pid = pid
169
+ self.access = access
170
+ self.handle = HANDLE(0)
171
+ self.base = 0
172
+
173
+ object_attributes = OBJECT_ATTRIBUTES()
174
+ client_id = CLIENT_ID()
175
+ client_id.UniqueProcess = HANDLE(pid)
176
+ object_attributes.Length = ctypes.sizeof(OBJECT_ATTRIBUTES)
177
+
178
+ status = nt_open_process_syscall(
179
+ ctypes.byref(self.handle),
180
+ access,
181
+ ctypes.byref(object_attributes),
182
+ ctypes.byref(client_id)
183
+ )
184
+ if status != STATUS_SUCCESS:
185
+ raise ctypes.WinError(f"NtOpenProcess failed with NTSTATUS: 0x{status:X}")
186
+ self.base = _get_module_base(self.handle)
187
+ if self.base == 0:
188
+ self.close()
189
+ raise ConnectionError("Failed to get module base address.")
190
+
191
+ def read(self, address: int, size: int) -> bytes:
192
+ if not self.handle or self.handle.value == 0:
193
+ raise ValueError("Process handle is not valid.")
194
+ buffer = ctypes.create_string_buffer(size)
195
+ bytes_read = ctypes.c_ulong(0)
196
+ status = nt_read_virtual_memory_syscall(
197
+ self.handle,
198
+ LPVOID(address),
199
+ buffer,
200
+ size,
201
+ ctypes.byref(bytes_read)
202
+ )
203
+ if status != STATUS_SUCCESS:
204
+ raise OSError(f"NtReadVirtualMemory failed with NTSTATUS: 0x{status:X}")
205
+ return buffer.raw[:bytes_read.value]
206
+
207
+ # numbers #
208
+ def read_int(self, address: int) -> int:
209
+ buffer = self.read(address, 4)
210
+ return int.from_bytes(buffer, 'little') if len(buffer) == 4 else 0
211
+
212
+ def read_long(self, address: int) -> int:
213
+ buffer = self.read(address, 8)
214
+ return int.from_bytes(buffer, 'little') if len(buffer) == 8 else 0
215
+
216
+ def read_double(self, address: int) -> float:
217
+ try:
218
+ double_bytes = self.read(address, 8)
219
+ return struct.unpack('<d', double_bytes)[0] if len(double_bytes) == 8 else 0.0
220
+ except (OSError, struct.error):
221
+ return 0.0
222
+
223
+ def read_float(self, address: int) -> float:
224
+ try:
225
+ float_bytes = self.read(address, 4)
226
+ return struct.unpack('f', float_bytes)[0] if len(float_bytes) == 4 else 0.0
227
+ except (OSError, struct.error):
228
+ return 0.0
229
+
230
+ def read_floats(self, address: int, amount: int):
231
+ try:
232
+ bulk_float_bytes = self.read(address, 4 * amount)
233
+ floats = []
234
+ for i in range(amount):
235
+ start_range = i * 4
236
+ float_bytes = bulk_float_bytes[start_range:start_range + 4]
237
+
238
+ if len(float_bytes) == 4:
239
+ floats.append(struct.unpack('f', float_bytes)[0])
240
+ else:
241
+ floats.append(0.0)
242
+
243
+ return floats
244
+ except (OSError, struct.error) as e:
245
+ return [0.0]
246
+
247
+ # bool #
248
+ def read_bool(self, address: int) -> bool:
249
+ try:
250
+ bool_byte = self.read(address, 1)
251
+ if not bool_byte: return False
252
+ return bool(int.from_bytes(bool_byte, 'little'))
253
+ except OSError:
254
+ return False
255
+
256
+ # string #
257
+ def read_raw_string(self, address: int, max_length: int = 256) -> str:
258
+ buffer = self.read(address, max_length)
259
+ null_pos = buffer.find(b'\x00')
260
+ valid_bytes = buffer[:null_pos] if null_pos != -1 else buffer
261
+ return valid_bytes.decode('utf-8', errors='ignore')
262
+
263
+ def read_string(self, address: int) -> str:
264
+ string_length = self.read_int(address + 0x10)
265
+ if string_length <= 15:
266
+ return self.read_raw_string(address, string_length)
267
+ else:
268
+ string_data_pointer = self.read_long(address)
269
+ return self.read_raw_string(string_data_pointer, string_length) if string_data_pointer else ""
270
+
271
+ #########
272
+ def close(self):
273
+ if self.handle and self.handle.value != 0:
274
+ nt_close_syscall(self.handle)
275
+ self.handle = HANDLE(0)
@@ -0,0 +1,46 @@
1
+ import json
2
+ import requests
3
+ from importlib import resources
4
+
5
+ Offsets = {}
6
+ def ParseOffsets(*DataSources):
7
+ for _, Data in enumerate(DataSources, start=1):
8
+ for OffsetName in Data:
9
+ try:
10
+ FormattedOffsetName = OffsetName.replace(" ", "")
11
+ OffsetHexadecimalValue = Data[OffsetName]
12
+
13
+ Offsets[FormattedOffsetName] = int(OffsetHexadecimalValue, 16)
14
+ except (ValueError, TypeError):
15
+ pass
16
+
17
+
18
+ OffsetsRequest = requests.get("https://offsets.ntgetwritewatch.workers.dev/offsets.json")
19
+
20
+ try:
21
+ LoadedOffsetsRequest = requests.get(
22
+ "https://raw.githubusercontent.com/notpoiu/RobloxMemoryAPI/refs/heads/main/src/robloxmemoryapi/data/offsets.json"
23
+ )
24
+ LoadedOffsetsRequest.raise_for_status()
25
+
26
+ LoadedOffsets = LoadedOffsetsRequest.json()
27
+ except Exception:
28
+ try:
29
+ with resources.files("robloxmemoryapi.data").joinpath("offsets.json").open("r", encoding="utf-8") as f:
30
+ LoadedOffsets = json.load(f)
31
+ except Exception:
32
+ # Fallback defaults
33
+ LoadedOffsets = {
34
+ "Text": "0xC10",
35
+ "Character": "0x328",
36
+ "PrimaryPart": "0x268",
37
+ }
38
+
39
+ ParseOffsets(LoadedOffsets, OffsetsRequest.json())
40
+
41
+ # CFrame Offsets
42
+ RotationMatriciesLengthBytes = 3 * 3 * 4
43
+
44
+ Offsets["CameraCFrame"] = Offsets["CameraPos"] - RotationMatriciesLengthBytes
45
+ Offsets["CFrame"] = Offsets["Position"] - RotationMatriciesLengthBytes
46
+
File without changes
@@ -0,0 +1,266 @@
1
+ import math
2
+
3
+ def get_flat_matrix_column(matrix, column, invert_values=False):
4
+ stride = 3
5
+
6
+ if invert_values:
7
+ return [matrix[column + i * stride] * -1 for i in range(3)]
8
+
9
+ return [matrix[column + i * stride] for i in range(3)]
10
+
11
+ class UDim:
12
+ def __init__(self, scale=0, offset=0):
13
+ self.Scale = float(scale)
14
+ self.Offset = int(offset)
15
+
16
+ def __repr__(self):
17
+ return f"{{{self.Scale}, {self.Offset}}}"
18
+
19
+ class UDim2:
20
+ def __init__(self, scaleX=0, offsetX=0, scaleY=0, offsetY=0):
21
+ self.X = UDim(scaleX, offsetX)
22
+ self.Y = UDim(scaleY, offsetY)
23
+
24
+ def __repr__(self):
25
+ return f"{{{self.X}, {self.Y}}}"
26
+
27
+ @classmethod
28
+ def fromScale(cls, scaleX, scaleY):
29
+ return cls(scaleX, 0, scaleY, 0)
30
+
31
+ @classmethod
32
+ def fromOffset(cls, offsetX, offsetY):
33
+ return cls(0, offsetX, 0, offsetY)
34
+
35
+ class Vector2:
36
+ def __init__(self, x=0, y=0):
37
+ self.X = float(x)
38
+ self.Y = float(y)
39
+
40
+ def __repr__(self):
41
+ return f"{self.X}, {self.Y}"
42
+
43
+ def __eq__(self, other):
44
+ return (
45
+ isinstance(other, Vector2)
46
+ and self.X == other.X
47
+ and self.Y == other.Y
48
+ )
49
+
50
+ class Vector3:
51
+ def __init__(self, x=0, y=0, z=0):
52
+ self.X = float(x)
53
+ self.Y = float(y)
54
+ self.Z = float(z)
55
+
56
+ def __repr__(self):
57
+ return f"{self.X}, {self.Y}, {self.Z}"
58
+
59
+ def __eq__(self, other):
60
+ eps = 1e-9
61
+
62
+ return (
63
+ isinstance(other, Vector3)
64
+ and abs(self.X - other.X) < eps
65
+ and abs(self.Y - other.Y) < eps
66
+ and abs(self.Z - other.Z) < eps
67
+ )
68
+
69
+ # --- Arithmetic ---
70
+ def __add__(self, other):
71
+ if isinstance(other, Vector3):
72
+ return Vector3(self.X + other.X, self.Y + other.Y, self.Z + other.Z)
73
+ raise TypeError("Vector3 can only be added to Vector3")
74
+
75
+ def __sub__(self, other):
76
+ if isinstance(other, Vector3):
77
+ return Vector3(self.X - other.X, self.Y - other.Y, self.Z - other.Z)
78
+ raise TypeError("Vector3 can only be subtracted by Vector3")
79
+
80
+ def __mul__(self, other):
81
+ if isinstance(other, (int, float)):
82
+ return Vector3(self.X * other, self.Y * other, self.Z * other)
83
+ if isinstance(other, Vector3):
84
+ return Vector3(self.X * other.X, self.Y * other.Y, self.Z * other.Z)
85
+ raise TypeError("Vector3 can only be multiplied by Vector3 or a number")
86
+
87
+ __rmul__ = __mul__
88
+
89
+ def __truediv__(self, other):
90
+ if isinstance(other, (int, float)):
91
+ return Vector3(self.X / other, self.Y / other, self.Z / other)
92
+ if isinstance(other, Vector3):
93
+ return Vector3(self.X / other.X, self.Y / other.Y, self.Z / other.Z)
94
+ raise TypeError("Vector3 can only be divided by Vector3 or a number")
95
+
96
+ def __neg__(self):
97
+ return Vector3(-self.X, -self.Y, -self.Z)
98
+
99
+ # --- Vector math ---
100
+ def Dot(self, other):
101
+ return self.X * other.X + self.Y * other.Y + self.Z * other.Z
102
+
103
+ def Cross(self, other):
104
+ return Vector3(
105
+ self.Y * other.Z - self.Z * other.Y,
106
+ self.Z * other.X - self.X * other.Z,
107
+ self.X * other.Y - self.Y * other.X,
108
+ )
109
+
110
+ def Magnitude(self):
111
+ return math.sqrt(self.X**2 + self.Y**2 + self.Z**2)
112
+
113
+ def Unit(self):
114
+ m = self.Magnitude()
115
+ return Vector3(self.X / m, self.Y / m, self.Z / m) if m != 0 else Vector3()
116
+
117
+ def Lerp(self, other, alpha: float):
118
+ return self + (other - self) * alpha
119
+
120
+ class CFrame:
121
+ def __init__(self, position=None, right=None, up=None, look=None):
122
+ self.Position = position or Vector3(0, 0, 0)
123
+
124
+ if right is None and up is None and look is None:
125
+ self.RightVector = Vector3(1, 0, 0)
126
+ self.UpVector = Vector3(0, 1, 0)
127
+ self.LookVector = Vector3(0, 0, -1)
128
+ else:
129
+ r, u, l = self._orthonormal_basis(right, up, look)
130
+ self.RightVector, self.UpVector, self.LookVector = r, u, l
131
+
132
+ def __repr__(self):
133
+ return (
134
+ f"{self.Position}, {self.RightVector}, {self.UpVector}, {self.LookVector}"
135
+ )
136
+
137
+ @classmethod
138
+ def new(cls, x=0, y=0, z=0):
139
+ return cls(position=Vector3(x, y, z))
140
+
141
+ # --- Rotation helpers ---
142
+ def _rotate_vector(self, v: Vector3) -> Vector3:
143
+ return (
144
+ self.RightVector * v.X +
145
+ self.UpVector * v.Y +
146
+ self.LookVector * v.Z
147
+ )
148
+
149
+ def _inverse_rotate_vector(self, v: Vector3) -> Vector3:
150
+ # Multiply by transpose of rotation matrix
151
+ return Vector3(
152
+ v.Dot(self.RightVector),
153
+ v.Dot(self.UpVector),
154
+ v.Dot(self.LookVector)
155
+ )
156
+
157
+ @staticmethod
158
+ def _orthonormal_basis(right=None, up=None, look=None):
159
+ def unit(v: Vector3 | None):
160
+ return v.Unit() if isinstance(v, Vector3) else None
161
+
162
+ def nearly_parallel(a: Vector3, b: Vector3) -> bool:
163
+ ma, mb = a.Magnitude(), b.Magnitude()
164
+ if ma == 0 or mb == 0:
165
+ return True
166
+ return abs(a.Dot(b) / (ma * mb)) > 0.9999
167
+
168
+ def orthogonal_to(v: Vector3) -> Vector3:
169
+ fallback = Vector3(0, 1, 0)
170
+ if nearly_parallel(v, fallback):
171
+ fallback = Vector3(1, 0, 0)
172
+ return (fallback - v * fallback.Dot(v)).Unit()
173
+
174
+ r = unit(right)
175
+ u = unit(up)
176
+ l = unit(look)
177
+
178
+ if r is not None and u is not None:
179
+ u = (u - r * u.Dot(r)).Unit()
180
+ l = -r.Cross(u).Unit()
181
+ return r, u, l
182
+
183
+ if r is not None and l is not None:
184
+ l = (l - r * l.Dot(r)).Unit()
185
+ u = l.Cross(r).Unit()
186
+ return r, u, l
187
+
188
+ if u is not None and l is not None:
189
+ l = (l - u * l.Dot(l)).Unit()
190
+ r = l.Cross(u).Unit()
191
+ return r, u, l
192
+
193
+ if r is not None:
194
+ u = orthogonal_to(r)
195
+ l = -r.Cross(u).Unit()
196
+ return r, u, l
197
+
198
+ if u is not None:
199
+ r = orthogonal_to(u)
200
+ l = -r.Cross(u).Unit()
201
+ return r, u, l
202
+
203
+ if l is not None:
204
+ u = orthogonal_to(l)
205
+ r = l.Cross(u).Unit()
206
+ return r, u, l
207
+
208
+ return Vector3(1, 0, 0), Vector3(0, 1, 0), Vector3(0, 0, -1)
209
+
210
+ # --- Operators ---
211
+ def __mul__(self, other):
212
+ if isinstance(other, CFrame):
213
+ rotated_pos = self._rotate_vector(other.Position)
214
+ new_pos = self.Position + rotated_pos
215
+
216
+ r = self._rotate_vector(other.RightVector)
217
+ u = self._rotate_vector(other.UpVector)
218
+ l = self._rotate_vector(other.LookVector)
219
+
220
+ return CFrame(new_pos, r, u, l)
221
+
222
+ if isinstance(other, Vector3):
223
+ return self.Position + self._rotate_vector(other)
224
+
225
+ raise TypeError("CFrame can only be multiplied by CFrame or Vector3")
226
+
227
+ def __add__(self, other):
228
+ if isinstance(other, Vector3):
229
+ return CFrame(self.Position + other,
230
+ self.RightVector, self.UpVector, self.LookVector)
231
+ raise TypeError("CFrame can only be added to Vector3")
232
+
233
+ def __sub__(self, other):
234
+ if isinstance(other, Vector3):
235
+ return CFrame(self.Position - other,
236
+ self.RightVector, self.UpVector, self.LookVector)
237
+ raise TypeError("CFrame can only be subtracted by Vector3")
238
+
239
+ # --- API Methods ---
240
+ def Inverse(self):
241
+ # Rotation inverse is transpose
242
+ r = Vector3(self.RightVector.X, self.UpVector.X, self.LookVector.X)
243
+ u = Vector3(self.RightVector.Y, self.UpVector.Y, self.LookVector.Y)
244
+ l = Vector3(self.RightVector.Z, self.UpVector.Z, self.LookVector.Z)
245
+
246
+ inv_pos = Vector3(
247
+ -self.Position.Dot(r),
248
+ -self.Position.Dot(u),
249
+ -self.Position.Dot(l),
250
+ )
251
+
252
+ return CFrame(inv_pos, r, u, l)
253
+
254
+ def ToWorldSpace(self, cf):
255
+ return self * cf
256
+
257
+ def ToObjectSpace(self, cf):
258
+ return self.Inverse() * cf
259
+
260
+ def GetComponents(self):
261
+ return tuple([
262
+ self.RightVector.X, self.UpVector.X, self.LookVector.X,
263
+ self.RightVector.Y, self.UpVector.Y, self.LookVector.Y,
264
+ self.RightVector.Z, self.UpVector.Z, self.LookVector.Z,
265
+ self.Position.X, self.Position.Y, self.Position.Z
266
+ ])
@@ -0,0 +1,457 @@
1
+ from ..offsets import *
2
+ import time, math
3
+ from .datastructures import *
4
+
5
+ # Normal Classes #
6
+ class RBXInstance:
7
+ def __init__(self, address, memory_module):
8
+ self.raw_address = address
9
+ self.memory_module = memory_module
10
+
11
+ def __eq__(self, value):
12
+ return value.raw_address == self.raw_address
13
+
14
+ def __getattr__(self, key):
15
+ return self.FindFirstChild(key)
16
+
17
+ # utilities #
18
+ @property
19
+ def primitive_address(self):
20
+ part_primitive_pointer = self.raw_address + Offsets["Primitive"]
21
+ part_primitive = int.from_bytes(self.memory_module.read(part_primitive_pointer, 8), 'little')
22
+ return part_primitive
23
+
24
+ # props #
25
+ @property
26
+ def Parent(self):
27
+ parent_pointer = int.from_bytes(self.memory_module.read(self.raw_address + Offsets["Parent"], 8), 'little')
28
+ if parent_pointer == 0:
29
+ return None
30
+
31
+ return RBXInstance(parent_pointer, self.memory_module)
32
+
33
+ @property
34
+ def Name(self):
35
+ name_address_pointer = self.raw_address + Offsets["Name"]
36
+ name_address = int.from_bytes(self.memory_module.read(name_address_pointer, 8), 'little')
37
+ return self.memory_module.read_string(name_address)
38
+
39
+ @property
40
+ def ClassName(self):
41
+ class_descriptor_address = int.from_bytes(
42
+ self.memory_module.read(self.raw_address + Offsets["ClassDescriptor"], 8),
43
+ 'little'
44
+ )
45
+ class_name_address = int.from_bytes(
46
+ self.memory_module.read(class_descriptor_address + Offsets["ClassDescriptorToClassName"], 8),
47
+ 'little'
48
+ )
49
+ return self.memory_module.read_string(class_name_address)
50
+
51
+ @property
52
+ def CFrame(self):
53
+ className = self.ClassName
54
+
55
+ CFrameDataMatriciesLength = 12 # 3x4 matrix
56
+
57
+ if "part" in className.lower():
58
+ CFrameData = self.memory_module.read_floats(self.primitive_address + Offsets["CFrame"], CFrameDataMatriciesLength)
59
+ elif className == "Camera":
60
+ CFrameData = self.memory_module.read_floats(self.raw_address + Offsets["CameraCFrame"], CFrameDataMatriciesLength)
61
+ else:
62
+ return None
63
+
64
+ RightVectorData = get_flat_matrix_column(CFrameData, 0)
65
+ UpVectorData = get_flat_matrix_column(CFrameData, 1)
66
+ LookVectorData = get_flat_matrix_column(CFrameData, 2, invert_values=True)
67
+ PositionData = CFrameData[9:12]
68
+
69
+ return CFrame(
70
+ Vector3(*PositionData),
71
+ Vector3(*RightVectorData),
72
+ Vector3(*UpVectorData),
73
+ Vector3(*LookVectorData)
74
+ )
75
+
76
+ @property
77
+ def Position(self):
78
+ className = self.ClassName
79
+ if "part" in className.lower():
80
+ position_vector3 = self.memory_module.read_floats(self.primitive_address + Offsets["Position"], 3)
81
+ return Vector3(*position_vector3)
82
+ elif className == "Camera":
83
+ position_vector3 = self.memory_module.read_floats(self.raw_address + Offsets["CameraPos"], 3)
84
+ return Vector3(*position_vector3)
85
+ else:
86
+ try:
87
+ x = self.memory_module.read_float(self.raw_address + Offsets["FramePositionX"])
88
+ x_offset = self.memory_module.read_int(self.raw_address + Offsets["FramePositionOffsetX"])
89
+
90
+ y = self.memory_module.read_float(self.raw_address + Offsets["FramePositionY"])
91
+ y_offset = self.memory_module.read_int(self.raw_address + Offsets["FramePositionOffsetY"])
92
+
93
+ return UDim2(x, x_offset, y, y_offset)
94
+ except (KeyError, OSError) as e:
95
+ print(f"Error reading position: {e}")
96
+ return (0.0, 0, 0.0, 0)
97
+
98
+ @property
99
+ def Size(self):
100
+ if "part" in self.ClassName.lower():
101
+ size_vector3 = self.memory_module.read_floats(self.primitive_address + Offsets["PartSize"], 3)
102
+ return Vector3(*size_vector3)
103
+ else:
104
+ try:
105
+ x = self.memory_module.read_float(self.raw_address + Offsets["FrameSizeX"])
106
+ y = self.memory_module.read_float(self.raw_address + Offsets["FrameSizeY"])
107
+ return (x, y)
108
+ except (KeyError, OSError) as e:
109
+ print(f"Error reading position: {e}")
110
+ return (0.0, 0.0)
111
+
112
+ # XXXXValue props #
113
+ @property
114
+ def Value(self):
115
+ classname = self.ClassName
116
+ if classname == "StringValue":
117
+ return self.memory_module.read_string(self.raw_address + Offsets["Value"])
118
+
119
+ elif classname == "IntValue":
120
+ return self.memory_module.read_int(self.raw_address + Offsets["Value"])
121
+
122
+ elif classname == "NumberValue":
123
+ return self.memory_module.read_double(self.raw_address + Offsets["Value"])
124
+
125
+ elif classname == "BoolValue":
126
+ return self.memory_module.read_bool(self.raw_address + Offsets["Value"])
127
+
128
+ elif classname == "ObjectValue":
129
+ object_pointer = self.raw_address + Offsets["Value"]
130
+ object_address = int.from_bytes(self.memory_module.read(object_pointer, 8), 'little')
131
+
132
+ return RBXInstance(object_address, self.memory_module)
133
+
134
+ return None
135
+
136
+ # text props #
137
+ @property
138
+ def Text(self):
139
+ if "text" in self.ClassName.lower():
140
+ return self.memory_module.read_string(self.raw_address + Offsets["Text"])
141
+
142
+ return None
143
+
144
+ # humanoid props #
145
+ @property
146
+ def WalkSpeed(self):
147
+ if self.ClassName != "Humanoid":
148
+ return None
149
+
150
+ return self.memory_module.read_float(self.raw_address + Offsets["WalkSpeed"])
151
+
152
+ @property
153
+ def JumpPower(self):
154
+ if self.ClassName != "Humanoid":
155
+ return None
156
+
157
+ return self.memory_module.read_float(self.raw_address + Offsets["JumpPower"])
158
+
159
+ @property
160
+ def Health(self):
161
+ if self.ClassName != "Humanoid":
162
+ return None
163
+
164
+ return self.memory_module.read_float(self.raw_address + Offsets["Health"])
165
+
166
+ @property
167
+ def MaxHealth(self):
168
+ if self.ClassName != "Humanoid":
169
+ return None
170
+
171
+ return self.memory_module.read_float(self.raw_address + Offsets["MaxHealth"])
172
+
173
+ # model props #
174
+ @property
175
+ def PrimaryPart(self):
176
+ if self.ClassName != "Model":
177
+ return None
178
+
179
+ parent_pointer = int.from_bytes(self.memory_module.read(self.raw_address + Offsets["PrimaryPart"], 8), 'little')
180
+ if parent_pointer == 0:
181
+ return None
182
+
183
+ return RBXInstance(parent_pointer, self.memory_module)
184
+
185
+ # functions #
186
+ def GetChildren(self):
187
+ children = []
188
+ children_pointer = int.from_bytes(self.memory_module.read(self.raw_address + Offsets["Children"], 8), 'little')
189
+
190
+ if children_pointer == 0:
191
+ return children
192
+
193
+ children_start = int.from_bytes(self.memory_module.read(children_pointer, 8), 'little')
194
+ children_end = int.from_bytes(self.memory_module.read(children_pointer + Offsets["ChildrenEnd"], 8), 'little')
195
+
196
+ for child_address in range(children_start, children_end, 0x10):
197
+ child_pointer_bytes = self.memory_module.read(child_address, 8)
198
+ child_pointer = int.from_bytes(child_pointer_bytes, 'little')
199
+
200
+ if child_pointer != 0:
201
+ children.append(RBXInstance(child_pointer, self.memory_module))
202
+
203
+ return children
204
+
205
+ def GetFullName(self):
206
+ if self.ClassName == "DataModel":
207
+ return self.Name
208
+
209
+ ObjectPointer = self
210
+ ObjectPath = self.Name
211
+
212
+ while True:
213
+ if ObjectPointer.Parent.ClassName == "DataModel":
214
+ break
215
+
216
+ ObjectPointer = ObjectPointer.Parent
217
+ ObjectPath = f"{ObjectPointer.Name}." + ObjectPath
218
+
219
+ return ObjectPath
220
+
221
+ def GetDescendants(self):
222
+ descendants = []
223
+ for child in self.GetChildren():
224
+ descendants.append(child)
225
+ descendants.extend(child.GetDescendants())
226
+ return descendants
227
+
228
+ def FindFirstChildOfClass(self, classname):
229
+ for child in self.GetChildren():
230
+ if child.ClassName == classname:
231
+ return child
232
+ return None
233
+
234
+ def FindFirstChild(self, name, recursive=False):
235
+ try:
236
+ children = self.GetChildren()
237
+ for child in children:
238
+ if child.Name == name:
239
+ return child
240
+
241
+ if recursive:
242
+ for child in children:
243
+ found_descendant = child.FindFirstChild(name, recursive=True)
244
+ if found_descendant:
245
+ return found_descendant
246
+ except: pass
247
+
248
+ return None
249
+
250
+ def WaitForChild(self, name, memoryhandler, timeout=5):
251
+ start = time.time()
252
+ child = None
253
+
254
+ while time.time() - start < timeout:
255
+ child = self.FindFirstChild(name)
256
+ if child is not None: break
257
+ if not (memoryhandler.game and not memoryhandler.game.failed): break
258
+ time.sleep(0.1)
259
+
260
+ return child
261
+
262
+ class PlayerClass(RBXInstance):
263
+ def __init__(self, memory_module, player: RBXInstance):
264
+ super().__init__(player.raw_address, memory_module)
265
+ self.memory_module = memory_module
266
+
267
+ try:
268
+ if player.ClassName != "Player":
269
+ self.failed = True
270
+ else:
271
+ self.instance = player
272
+ except (KeyError, OSError):
273
+ self.failed = True
274
+
275
+ # props #
276
+ @property
277
+ def Character(self) -> RBXInstance | None:
278
+ addr = int.from_bytes(self.memory_module.read(self.instance.raw_address + Offsets["Character"], 8), 'little')
279
+ if addr == 0:
280
+ return None
281
+
282
+ return RBXInstance(addr, self.memory_module)
283
+
284
+ @property
285
+ def DisplayName(self):
286
+ return self.memory_module.read_string(self.raw_address + Offsets["DisplayName"])
287
+
288
+ @property
289
+ def UserId(self):
290
+ return self.memory_module.read_long(self.raw_address + Offsets["UserId"])
291
+
292
+ @property
293
+ def Team(self):
294
+ addr = int.from_bytes(self.memory_module.read(self.instance.raw_address + Offsets["Team"], 8), 'little')
295
+ if addr == 0:
296
+ return None
297
+
298
+ return RBXInstance(addr, self.memory_module)
299
+
300
+ class CameraClass(RBXInstance):
301
+ def __init__(self, memory_module, camera: RBXInstance):
302
+ super().__init__(camera.raw_address, memory_module)
303
+ self.memory_module = memory_module
304
+
305
+ try:
306
+ if camera.ClassName != "Camera":
307
+ self.failed = True
308
+ else:
309
+ self.instance = camera
310
+ except (KeyError, OSError):
311
+ self.failed = True
312
+
313
+ # props #
314
+ @property
315
+ def FieldOfView(self):
316
+ return self.FieldOfViewRadians * (180/math.pi)
317
+
318
+ @property
319
+ def FieldOfViewRadians(self):
320
+ return self.memory_module.read_float(self.raw_address + Offsets["FOV"])
321
+
322
+ @property
323
+ def ViewportSize(self):
324
+ SizeData = self.memory_module.read_floats(self.raw_address + Offsets["ViewportSize"], 2)
325
+ return Vector2(*SizeData)
326
+
327
+ # Service #
328
+ class ServiceBase:
329
+ def __init__(self):
330
+ self.instance = None
331
+ self.failed = False
332
+
333
+ # expose instance functions #
334
+ def __getattr__(self, name):
335
+ # instance #
336
+ if self.instance is not None:
337
+ return getattr(self.instance, name)
338
+
339
+ return self.instance.FindFirstChild(name)
340
+
341
+ class DataModel(ServiceBase):
342
+ def __init__(self, memory_module):
343
+ super().__init__()
344
+ self.memory_module = memory_module
345
+
346
+ self.error = None
347
+ try:
348
+ if Offsets.get("DataModelPointer") is not None:
349
+ datamodel_addr = Offsets["DataModelPointer"]
350
+ else:
351
+ fake_dm_pointer_offset = Offsets["FakeDataModelPointer"]
352
+ fake_dm_pointer_addr = memory_module.base + fake_dm_pointer_offset
353
+ fake_dm_pointer_val = int.from_bytes(memory_module.read(fake_dm_pointer_addr, 8), 'little')
354
+
355
+ dm_to_datamodel_offset = Offsets["FakeDataModelToDataModel"]
356
+ datamodel_addr_ptr = fake_dm_pointer_val + dm_to_datamodel_offset
357
+ datamodel_addr = int.from_bytes(memory_module.read(datamodel_addr_ptr, 8), 'little')
358
+
359
+ datamodel_instance = RBXInstance(datamodel_addr, memory_module)
360
+
361
+ if datamodel_instance.Name != "Ugc":
362
+ self.failed = True
363
+ else:
364
+ self.instance = datamodel_instance
365
+ except (KeyError, OSError) as e:
366
+ self.error = e
367
+ self.failed = True
368
+
369
+ @property
370
+ def PlaceId(self):
371
+ return self.memory_module.read_long(self.raw_address + Offsets["PlaceId"])
372
+
373
+ @property
374
+ def GameId(self):
375
+ return self.memory_module.read_long(self.raw_address + Offsets["GameId"])
376
+
377
+ @property
378
+ def JobId(self):
379
+ return self.memory_module.read_string(self.raw_address + Offsets["JobId"])
380
+
381
+ @property
382
+ def Players(self):
383
+ return PlayersService(self.memory_module, self)
384
+
385
+ @property
386
+ def Workspace(self):
387
+ return WorkspaceService(self.memory_module, self)
388
+
389
+ # class functions #
390
+ def GetService(self, name):
391
+ if self.failed: return
392
+
393
+ for instance in self.instance.GetChildren():
394
+ if instance.ClassName == name:
395
+ return instance
396
+
397
+ return None
398
+
399
+ # Stuff
400
+ def IsLoaded(self):
401
+ return self.memory_module.read_bool(self.raw_address + Offsets["GameLoaded"])
402
+
403
+ class PlayersService(ServiceBase):
404
+ def __init__(self, memory_module, game: DataModel):
405
+ super().__init__()
406
+ self.memory_module = memory_module
407
+
408
+ try:
409
+ players_instance: RBXInstance = game.GetService("Players")
410
+ if players_instance.ClassName != "Players":
411
+ self.failed = True
412
+ else:
413
+ self.instance = players_instance
414
+ except (KeyError, OSError):
415
+ self.failed = True
416
+
417
+ # props #
418
+ @property
419
+ def LocalPlayer(self) -> RBXInstance | None:
420
+ if self.failed: return
421
+
422
+ addr = int.from_bytes(self.memory_module.read(self.instance.raw_address + Offsets["LocalPlayer"], 8), 'little')
423
+ return PlayerClass(self.memory_module, RBXInstance(addr, self.memory_module))
424
+
425
+ def GetPlayers(self):
426
+ players = []
427
+
428
+ for instance in self.instance.GetChildren():
429
+ if instance.ClassName == "Player":
430
+ players.append(PlayerClass(self.memory_module, instance))
431
+
432
+ return players
433
+
434
+ class WorkspaceService(ServiceBase):
435
+ def __init__(self, memory_module, game: DataModel):
436
+ super().__init__()
437
+ self.memory_module = memory_module
438
+
439
+ try:
440
+ workspace_instance: RBXInstance = game.GetService("Workspace")
441
+ if workspace_instance.ClassName != "Workspace":
442
+ self.failed = True
443
+ else:
444
+ self.instance = workspace_instance
445
+ except (KeyError, OSError):
446
+ self.failed = True
447
+
448
+ # props #
449
+ @property
450
+ def CurrentCamera(self) -> CameraClass | None:
451
+ if self.failed: return
452
+
453
+ addr = int.from_bytes(self.memory_module.read(self.instance.raw_address + Offsets["Camera"], 8), 'little')
454
+ if addr == 0:
455
+ return None
456
+
457
+ return CameraClass(self.memory_module, RBXInstance(addr, self.memory_module))
@@ -0,0 +1,75 @@
1
+ Metadata-Version: 2.4
2
+ Name: robloxmemoryapi
3
+ Version: 0.0.1
4
+ Summary: Python Library that abstracts reading data from the Roblox DataModel
5
+ Author-email: upio <notpoiu@users.noreply.github.com>, mstudio45 <mstudio45@users.noreply.github.com>, ActualMasterOogway <ActualMasterOogway@users.noreply.github.com>
6
+ License: Copyright 2025 upio, mstudio45, master oogway
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
11
+
12
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13
+
14
+ Project-URL: Homepage, https://github.com/notpoiu/RobloxMemoryAPI
15
+ Project-URL: Issues, https://github.com/notpoiu/RobloxMemoryAPI/issues
16
+ Keywords: roblox,memory,windows
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Operating System :: Microsoft :: Windows
20
+ Classifier: Development Status :: 3 - Alpha
21
+ Classifier: Intended Audience :: Developers
22
+ Classifier: Topic :: Software Development :: Libraries
23
+ Requires-Python: >=3.9
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE.md
26
+ Requires-Dist: requests>=2.0
27
+ Dynamic: license-file
28
+
29
+ # RobloxMemoryAPI
30
+
31
+ A Python library that is _hopefully stealthy_ and abstracts externally reading memory to get datamodel information from the roblox game client.
32
+
33
+ This was made by [upio](https://github.com/notpoiu), [mstudio45](https://github.com/mstudio45), and [Master Oogway](https://github.com/ActualMasterOogway) and created for the [Dig Macro](https://github.com/mstudio45/digmacro) project (external mode and not the computer vision mode).
34
+
35
+ ## Installation
36
+
37
+ PyPI:
38
+
39
+ ```bash
40
+ pip install robloxmemoryapi
41
+ ```
42
+
43
+ Development (editable install from source):
44
+
45
+ ```bash
46
+ pip install -e .
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ An example script can be found in [example.py](example.py). If running from the repo, use the editable install above so `import robloxmemoryapi` resolves the `src` package.
52
+
53
+ Import the library and create a client instance:
54
+
55
+ ```python
56
+ from robloxmemoryapi import RobloxGameClient
57
+
58
+ client = RobloxGameClient()
59
+ ```
60
+
61
+ Access the data model:
62
+
63
+ ```python
64
+ game = client.DataModel
65
+ ```
66
+
67
+ Get the local player's name:
68
+
69
+ ```python
70
+ print("Player Name:", game.Players.LocalPlayer.Name)
71
+ ```
72
+
73
+ ## License
74
+
75
+ This project is licensed under the MIT License.
@@ -0,0 +1,13 @@
1
+ robloxmemoryapi/__init__.py,sha256=HdrWxS_cjqt7V7P5sL2N8_vGGLKV6xOxAZGSQL4nzIM,2560
2
+ robloxmemoryapi/data/offsets.json,sha256=VfVvOpFA11CxJgDgqqpjeRm-mwehSDqZM-e3TXCkS0s,79
3
+ robloxmemoryapi/utils/__init__.py,sha256=PtpwEbCo79OW68ycpVRjLg1DN-lW_3Z6bdMTIDMF4ek,52
4
+ robloxmemoryapi/utils/memory.py,sha256=yjLvvmhxcpQe8u_TULKDSmMiGM4gmYtj214JyGKDEcc,9534
5
+ robloxmemoryapi/utils/offsets.py,sha256=jWfl0ERDxO4-vGXN7FRkv4-Nz1x257pigqhMM06fWI4,1463
6
+ robloxmemoryapi/utils/rbx/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ robloxmemoryapi/utils/rbx/datastructures.py,sha256=U_tBET2RqoCllqFIb1Ry_ct0jx7YOTWjaTJLvH4htcA,8462
8
+ robloxmemoryapi/utils/rbx/instance.py,sha256=cRghPNhsRys9g8Xi1fe1dq1wkpefmDJmIWnqndQ-Szc,15644
9
+ robloxmemoryapi-0.0.1.dist-info/licenses/LICENSE.md,sha256=zngVZqcrtwUO0Un82JVGU24AS0_ly3IjE0ussEvqktM,1078
10
+ robloxmemoryapi-0.0.1.dist-info/METADATA,sha256=TBSMJ3O4tC0tmWNYadY1QyZcztB0umU_1hH8gkB8TV8,3186
11
+ robloxmemoryapi-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
12
+ robloxmemoryapi-0.0.1.dist-info/top_level.txt,sha256=j28W-HyH5eTKJLbNUc5EGb3KYgNG4MJ6jcOgEKrO_Uc,16
13
+ robloxmemoryapi-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,7 @@
1
+ Copyright 2025 upio, mstudio45, master oogway
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1 @@
1
+ robloxmemoryapi