robloxmemoryapi 0.2.1__tar.gz → 0.2.2.1__tar.gz

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.
Files changed (23) hide show
  1. {robloxmemoryapi-0.2.1/src/robloxmemoryapi.egg-info → robloxmemoryapi-0.2.2.1}/PKG-INFO +1 -1
  2. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/pyproject.toml +1 -1
  3. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi/__init__.py +14 -1
  4. robloxmemoryapi-0.2.2.1/src/robloxmemoryapi/utils/offsets.py +37 -0
  5. robloxmemoryapi-0.2.2.1/src/robloxmemoryapi/utils/rbx/fflags.py +197 -0
  6. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1/src/robloxmemoryapi.egg-info}/PKG-INFO +1 -1
  7. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi.egg-info/SOURCES.txt +1 -0
  8. robloxmemoryapi-0.2.1/src/robloxmemoryapi/utils/offsets.py +0 -22
  9. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/LICENSE.md +0 -0
  10. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/README.md +0 -0
  11. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/setup.cfg +0 -0
  12. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi/utils/__init__.py +0 -0
  13. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi/utils/luau/__init__.py +0 -0
  14. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi/utils/luau/parser.py +0 -0
  15. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi/utils/memory.py +0 -0
  16. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi/utils/rbx/__init__.py +0 -0
  17. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi/utils/rbx/bytecode/decryptor.py +0 -0
  18. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi/utils/rbx/bytecode/encryptor.py +0 -0
  19. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi/utils/rbx/datastructures.py +0 -0
  20. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi/utils/rbx/instance.py +0 -0
  21. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi.egg-info/dependency_links.txt +0 -0
  22. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi.egg-info/requires.txt +0 -0
  23. {robloxmemoryapi-0.2.1 → robloxmemoryapi-0.2.2.1}/src/robloxmemoryapi.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robloxmemoryapi
3
- Version: 0.2.1
3
+ Version: 0.2.2.1
4
4
  Summary: Python Library that abstracts reading and writing data from the Roblox DataModel
5
5
  Author-email: upio <notpoiu@users.noreply.github.com>, mstudio45 <mstudio45@users.noreply.github.com>, ActualMasterOogway <ActualMasterOogway@users.noreply.github.com>
6
6
  License: Copyright 2025 upio, mstudio45, master oogway
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "robloxmemoryapi"
7
- version = "0.2.1"
7
+ version = "0.2.2.1"
8
8
  description = "Python Library that abstracts reading and writing data from the Roblox DataModel"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.9"
@@ -80,10 +80,23 @@ class RobloxGameClient:
80
80
 
81
81
  self.memory_module = EvasiveProcess(self.pid, desired_access)
82
82
  self.failed = False
83
+ self._fflags = None
83
84
 
84
85
  def close(self):
85
86
  self.memory_module.close()
86
87
 
88
+ @property
89
+ def FFlags(self):
90
+ if platform.system() != "Windows":
91
+ raise RuntimeError("This module is only compatible with Windows.")
92
+ elif self.failed:
93
+ raise RuntimeError("There was an error while getting access to memory. Please try again later.")
94
+
95
+ if self._fflags is None:
96
+ from .utils.rbx.fflags import FFlagManager
97
+ self._fflags = FFlagManager(self.memory_module)
98
+ return self._fflags
99
+
87
100
  @property
88
101
  def DataModel(self):
89
102
  if platform.system() != "Windows":
@@ -92,4 +105,4 @@ class RobloxGameClient:
92
105
  raise RuntimeError("There was an error while getting access to memory. Please try again later.")
93
106
 
94
107
  from .utils.rbx.instance import DataModel
95
- return DataModel(self.memory_module)
108
+ return DataModel(self.memory_module)
@@ -0,0 +1,37 @@
1
+ import requests
2
+
3
+ BASE_URL = "https://imtheo.lol/Offsets"
4
+
5
+ # Offsets
6
+ OffsetsRequest = requests.get(f"{BASE_URL}/Offsets.json")
7
+
8
+ # Legacy Offsets
9
+ OldOffsetsRequest = requests.get("https://offsets.ntgetwritewatch.workers.dev/offsets.json")
10
+ try:
11
+ OldOffsets = OldOffsetsRequest.json()
12
+ except:
13
+ OldOffsets = {}
14
+
15
+ try:
16
+ Offsets = OffsetsRequest.json()["Offsets"]
17
+ except:
18
+ Offsets = {}
19
+
20
+ # Handle non-existant offsets
21
+ try:
22
+ Offsets["Camera"]["ViewportSize"] = int(OldOffsets["ViewportSize"], 16)
23
+ except:
24
+ Offsets["Camera"]["ViewportSize"] = 0x6AD28F
25
+
26
+ # FFlag offsets (lazily loaded)
27
+ _fflag_data = None
28
+ _fflag_offsets = None
29
+
30
+ def get_fflag_offsets() -> dict:
31
+ global _fflag_data, _fflag_offsets
32
+ if _fflag_data is None:
33
+ resp = requests.get(f"{BASE_URL}/FFlags.json")
34
+ resp.raise_for_status()
35
+ _fflag_data = resp.json()
36
+ _fflag_offsets = _fflag_data["FFlagOffsets"]["FFlags"]
37
+ return _fflag_offsets
@@ -0,0 +1,197 @@
1
+ from ..offsets import get_fflag_offsets
2
+
3
+ _MODULE_SIZE_LIMIT = 0x20000000
4
+
5
+ # FLog level byte → display name.
6
+ _FLOG_LEVELS = {
7
+ 2: "Error",
8
+ 3: "Warning",
9
+ 4: "Info",
10
+ 5: "Debug",
11
+ 6: "Verbose",
12
+ 255: "None",
13
+ }
14
+
15
+ _VALID_FLOG_LEVELS = set(_FLOG_LEVELS.keys())
16
+
17
+ _FLOG_LEVELS_REV = {v: k for k, v in _FLOG_LEVELS.items()}
18
+
19
+ def _decode_flog(raw_int: int) -> str:
20
+ level_byte = (raw_int >> 8) & 0xFF
21
+ param_byte = raw_int & 0xFF
22
+ level_name = _FLOG_LEVELS.get(level_byte, str(level_byte))
23
+ return f"{level_name},{param_byte}"
24
+
25
+
26
+ def _encode_flog(value: str) -> int:
27
+ parts = value.split(",", 1)
28
+ level_name = parts[0].strip()
29
+ param = int(parts[1].strip()) if len(parts) > 1 else 0
30
+ level_byte = _FLOG_LEVELS_REV.get(level_name)
31
+ if level_byte is None:
32
+ level_byte = int(level_name)
33
+ return (level_byte << 8) | (param & 0xFF)
34
+
35
+
36
+ class FFlag:
37
+ __slots__ = ("name", "type", "value", "offset")
38
+
39
+ def __init__(self, name: str, flag_type: str, value, offset: int):
40
+ self.name = name
41
+ self.type = flag_type # "bool", "int", or "string"
42
+ self.value = value
43
+ self.offset = offset
44
+
45
+ def __repr__(self):
46
+ return f"FFlag(name={self.name!r}, type={self.type!r}, value={self.value!r})"
47
+
48
+ class FFlagManager:
49
+ def __init__(self, memory_module):
50
+ self._mem = memory_module
51
+ self._base = memory_module.base
52
+ self._offsets = None
53
+
54
+ def _ensure_offsets(self):
55
+ if self._offsets is None:
56
+ self._offsets = get_fflag_offsets()
57
+
58
+ def _is_module_ptr(self, ptr: int) -> bool:
59
+ return self._base <= ptr < self._base + _MODULE_SIZE_LIMIT
60
+
61
+ def _reflect(self, name: str, offset: int):
62
+ addr = self._base + offset
63
+ raw = self._mem.read(addr, 32)
64
+ if len(raw) < 32:
65
+ return "unknown", None
66
+
67
+ ptr_at_8 = int.from_bytes(raw[8:16], "little")
68
+
69
+ if self._is_module_ptr(ptr_at_8):
70
+ try:
71
+ name_in_mem = self._mem.read_raw_string(ptr_at_8, 256)
72
+ except OSError:
73
+ name_in_mem = ""
74
+
75
+ val_at_10 = int.from_bytes(raw[16:20], "little")
76
+
77
+ if name_in_mem and val_at_10 == len(name_in_mem):
78
+ return "bool", bool(raw[0])
79
+ else:
80
+ raw_int = int.from_bytes(raw[0:4], "little")
81
+ level_byte = (raw_int >> 8) & 0xFF
82
+ if level_byte in _VALID_FLOG_LEVELS:
83
+ return "flog", _decode_flog(raw_int)
84
+ else:
85
+ return "int", int.from_bytes(raw[0:4], "little", signed=True)
86
+ else:
87
+ str_len = int.from_bytes(raw[16:24], "little")
88
+ str_cap = int.from_bytes(raw[24:32], "little")
89
+
90
+ if str_cap == 15 and 0 <= str_len <= 15:
91
+ return "string", self._mem.read_string(addr)
92
+
93
+ if (str_cap >= 16 and str_cap < 0x100000
94
+ and 0 < str_len <= str_cap):
95
+ ptr_at_0 = int.from_bytes(raw[0:8], "little")
96
+ if ptr_at_0 > 0x10000:
97
+ return "string", self._mem.read_string(addr)
98
+
99
+ return "int", int.from_bytes(raw[0:4], "little", signed=True)
100
+
101
+ @property
102
+ def offsets(self) -> dict:
103
+ self._ensure_offsets()
104
+ return dict(self._offsets)
105
+
106
+ def get(self, name: str) -> FFlag | None:
107
+ self._ensure_offsets()
108
+ offset = self._offsets.get(name)
109
+ if offset is None:
110
+ return None
111
+ flag_type, value = self._reflect(name, offset)
112
+ return FFlag(name, flag_type, value, offset)
113
+
114
+ def get_many(self, *names: str) -> dict[str, FFlag]:
115
+ self._ensure_offsets()
116
+ out = {}
117
+ for n in names:
118
+ flag = self.get(n)
119
+ if flag is not None:
120
+ out[n] = flag
121
+ return out
122
+
123
+ def get_all(self) -> dict[str, FFlag]:
124
+ self._ensure_offsets()
125
+ out = {}
126
+ for name, offset in self._offsets.items():
127
+ try:
128
+ flag_type, value = self._reflect(name, offset)
129
+ out[name] = FFlag(name, flag_type, value, offset)
130
+ except OSError:
131
+ pass
132
+ return out
133
+
134
+ def set(self, name: str, value) -> FFlag:
135
+ self._ensure_offsets()
136
+ offset = self._offsets.get(name)
137
+ if offset is None:
138
+ raise KeyError(name)
139
+
140
+ flag_type, _ = self._reflect(name, offset)
141
+ addr = self._base + offset
142
+
143
+ if flag_type == "bool":
144
+ if not isinstance(value, (bool, int)):
145
+ raise TypeError(f"Expected bool for flag '{name}', got {type(value).__name__}")
146
+ self._mem.write_bool(addr, bool(value))
147
+
148
+ elif flag_type == "int":
149
+ if not isinstance(value, int):
150
+ raise TypeError(f"Expected int for flag '{name}', got {type(value).__name__}")
151
+ self._mem.write_int(addr, value)
152
+
153
+ elif flag_type == "string":
154
+ if not isinstance(value, str):
155
+ raise TypeError(f"Expected str for flag '{name}', got {type(value).__name__}")
156
+
157
+ self._mem.write_string(addr, str(value))
158
+
159
+ elif flag_type == "flog":
160
+ if not isinstance(value, (str, int)):
161
+ raise TypeError(f"Expected str or int for flag '{name}', got {type(value).__name__}")
162
+
163
+ self._mem.write_int(addr, _encode_flog(str(value)) if isinstance(value, str) else value)
164
+ else:
165
+ raise RuntimeError(f"Cannot write to flag with unknown type '{flag_type}'")
166
+
167
+ new_type, new_value = self._reflect(name, offset)
168
+ return FFlag(name, new_type, new_value, offset)
169
+
170
+ def __getitem__(self, name: str) -> FFlag:
171
+ flag = self.get(name)
172
+ if flag is None:
173
+ raise KeyError(name)
174
+ return flag
175
+
176
+ def __getattr__(self, name) -> FFlag:
177
+ flag = self.get(name)
178
+ if flag is None:
179
+ raise AttributeError(f"FFlag '{name}' not found")
180
+ return flag
181
+
182
+ def __setitem__(self, name: str, value):
183
+ self.set(name, value)
184
+
185
+ def __setattr__(self, name: str, value):
186
+ if name.startswith("_"):
187
+ object.__setattr__(self, name, value)
188
+ else:
189
+ self.set(name, value)
190
+
191
+ def __contains__(self, name: str) -> bool:
192
+ self._ensure_offsets()
193
+ return name in self._offsets
194
+
195
+ def __repr__(self):
196
+ self._ensure_offsets()
197
+ return f"FFlagManager({len(self._offsets)} flags)"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robloxmemoryapi
3
- Version: 0.2.1
3
+ Version: 0.2.2.1
4
4
  Summary: Python Library that abstracts reading and writing data from the Roblox DataModel
5
5
  Author-email: upio <notpoiu@users.noreply.github.com>, mstudio45 <mstudio45@users.noreply.github.com>, ActualMasterOogway <ActualMasterOogway@users.noreply.github.com>
6
6
  License: Copyright 2025 upio, mstudio45, master oogway
@@ -14,6 +14,7 @@ src/robloxmemoryapi/utils/luau/__init__.py
14
14
  src/robloxmemoryapi/utils/luau/parser.py
15
15
  src/robloxmemoryapi/utils/rbx/__init__.py
16
16
  src/robloxmemoryapi/utils/rbx/datastructures.py
17
+ src/robloxmemoryapi/utils/rbx/fflags.py
17
18
  src/robloxmemoryapi/utils/rbx/instance.py
18
19
  src/robloxmemoryapi/utils/rbx/bytecode/decryptor.py
19
20
  src/robloxmemoryapi/utils/rbx/bytecode/encryptor.py
@@ -1,22 +0,0 @@
1
- import requests
2
-
3
- # Offsets
4
- OffsetsRequest = requests.get("https://imtheo.lol/Offsets/Offsets.json")
5
-
6
- # Legacy Offsets
7
- OldOffsetsRequest = requests.get("https://offsets.ntgetwritewatch.workers.dev/offsets.json")
8
- try:
9
- OldOffsets = OldOffsetsRequest.json()
10
- except:
11
- OldOffsets = {}
12
-
13
- try:
14
- Offsets = OffsetsRequest.json()["Offsets"]
15
- except:
16
- Offsets = {}
17
-
18
- # Handle non-existant offsets
19
- try:
20
- Offsets["Camera"]["ViewportSize"] = int(OldOffsets["ViewportSize"], 16)
21
- except:
22
- Offsets["Camera"]["ViewportSize"] = 0x6AD28F