robloxmemoryapi 0.2.7__tar.gz → 0.2.8__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 (22) hide show
  1. {robloxmemoryapi-0.2.7/src/robloxmemoryapi.egg-info → robloxmemoryapi-0.2.8}/PKG-INFO +1 -1
  2. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/pyproject.toml +1 -1
  3. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi/utils/rbx/datastructures.py +23 -0
  4. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi/utils/rbx/instance.py +666 -7
  5. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8/src/robloxmemoryapi.egg-info}/PKG-INFO +1 -1
  6. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/LICENSE.md +0 -0
  7. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/README.md +0 -0
  8. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/setup.cfg +0 -0
  9. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi/__init__.py +0 -0
  10. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi/utils/__init__.py +0 -0
  11. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi/utils/luau/__init__.py +0 -0
  12. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi/utils/luau/parser.py +0 -0
  13. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi/utils/memory.py +0 -0
  14. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi/utils/offsets.py +0 -0
  15. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi/utils/rbx/__init__.py +0 -0
  16. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi/utils/rbx/bytecode/decryptor.py +0 -0
  17. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi/utils/rbx/bytecode/encryptor.py +0 -0
  18. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi/utils/rbx/fflags.py +0 -0
  19. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi.egg-info/SOURCES.txt +0 -0
  20. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi.egg-info/dependency_links.txt +0 -0
  21. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/src/robloxmemoryapi.egg-info/requires.txt +0 -0
  22. {robloxmemoryapi-0.2.7 → robloxmemoryapi-0.2.8}/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.7
3
+ Version: 0.2.8
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.7"
7
+ version = "0.2.8"
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"
@@ -47,6 +47,21 @@ class Vector2:
47
47
  and self.Y == other.Y
48
48
  )
49
49
 
50
+ class NumberRange:
51
+ def __init__(self, min_val=0, max_val=None):
52
+ self.Min = float(min_val)
53
+ self.Max = float(max_val if max_val is not None else min_val)
54
+
55
+ def __repr__(self):
56
+ return f"{self.Min}, {self.Max}"
57
+
58
+ def __eq__(self, other):
59
+ return (
60
+ isinstance(other, NumberRange)
61
+ and self.Min == other.Min
62
+ and self.Max == other.Max
63
+ )
64
+
50
65
  class Color3:
51
66
  def __init__(self, r=0, g=0, b=0):
52
67
  self.R = float(r)
@@ -391,6 +406,14 @@ class AnimationTrack:
391
406
  def IsPlaying(self):
392
407
  return self.memory_module.read_bool(self.raw_address, self._offsets["IsPlaying"])
393
408
 
409
+ @property
410
+ def TimePosition(self):
411
+ return self.memory_module.read_float(self.raw_address, self._offsets["TimePosition"])
412
+
413
+ @TimePosition.setter
414
+ def TimePosition(self, value: float):
415
+ self.memory_module.write_float(self.raw_address + self._offsets["TimePosition"], float(value))
416
+
394
417
  class FFlag:
395
418
  __slots__ = ("name", "type", "_value", "offset", "_manager")
396
419
 
@@ -31,6 +31,13 @@ air_properties_offsets = Offsets["AirProperties"]
31
31
  seat_offsets = Offsets["Seat"]
32
32
  meshpart_offsets = Offsets["MeshPart"]
33
33
  textures_offsets = Offsets["Textures"]
34
+ beam_offsets = Offsets["Beam"]
35
+ particleemitter_offsets = Offsets["ParticleEmitter"]
36
+ vehicleseat_offsets = Offsets["VehicleSeat"]
37
+ weld_offsets = Offsets["Weld"]
38
+ weldconstraint_offsets = Offsets["WeldConstraint"]
39
+ surfaceappearance_offsets = Offsets["SurfaceAppearance"]
40
+ script_offsets = Offsets["Script"]
34
41
 
35
42
  ROTATION_MATRIX_FLOATS = 9
36
43
 
@@ -751,6 +758,26 @@ class RBXInstance:
751
758
  flags &= ~Offsets["PrimitiveFlags"]["CanTouch"]
752
759
  self._write_primitive_flags(flags)
753
760
 
761
+ @property
762
+ def CanQuery(self):
763
+ if "part" not in self.ClassName.lower():
764
+ return None
765
+
766
+ flags = self._read_primitive_flags()
767
+ return bool(flags & Offsets["PrimitiveFlags"]["CanQuery"])
768
+
769
+ @CanQuery.setter
770
+ def CanQuery(self, value: bool):
771
+ if "part" not in self.ClassName.lower():
772
+ raise AttributeError("CanQuery is only available on BasePart-derived instances.")
773
+
774
+ flags = self._read_primitive_flags()
775
+ if value:
776
+ flags |= Offsets["PrimitiveFlags"]["CanQuery"]
777
+ else:
778
+ flags &= ~Offsets["PrimitiveFlags"]["CanQuery"]
779
+ self._write_primitive_flags(flags)
780
+
754
781
  # Animator props #
755
782
  def GetPlayingAnimationTracks(self):
756
783
  if self.ClassName != "Animator":
@@ -1288,23 +1315,33 @@ class RBXInstance:
1288
1315
  # colorcorrection props #
1289
1316
  @property
1290
1317
  def Brightness(self):
1291
- if self.ClassName == "ColorCorrectionEffect":
1318
+ cn = self.ClassName
1319
+ if cn == "ColorCorrectionEffect":
1292
1320
  return self.memory_module.read_float(
1293
1321
  self.raw_address,
1294
1322
  Offsets["ColorCorrectionEffect"]["Brightness"]
1295
1323
  )
1324
+ elif cn == "Beam":
1325
+ return self.memory_module.read_float(self.raw_address, beam_offsets["Brightness"])
1326
+ elif cn == "ParticleEmitter":
1327
+ return self.memory_module.read_float(self.raw_address, particleemitter_offsets["Brightness"])
1296
1328
  return None
1297
1329
 
1298
1330
  @Brightness.setter
1299
1331
  def Brightness(self, value: float):
1300
- if self.ClassName == "ColorCorrectionEffect":
1301
- self._ensure_writable()
1332
+ cn = self.ClassName
1333
+ self._ensure_writable()
1334
+ if cn == "ColorCorrectionEffect":
1302
1335
  self.memory_module.write_float(
1303
1336
  self.raw_address + Offsets["ColorCorrectionEffect"]["Brightness"],
1304
1337
  float(value)
1305
1338
  )
1339
+ elif cn == "Beam":
1340
+ self.memory_module.write_float(self.raw_address + beam_offsets["Brightness"], float(value))
1341
+ elif cn == "ParticleEmitter":
1342
+ self.memory_module.write_float(self.raw_address + particleemitter_offsets["Brightness"], float(value))
1306
1343
  else:
1307
- raise AttributeError("Brightness is only available on ColorCorrectionEffect instances (use LightingService.Brightness for Lighting).")
1344
+ raise AttributeError("Brightness is only available on ColorCorrectionEffect, Beam, or ParticleEmitter instances.")
1308
1345
 
1309
1346
  @property
1310
1347
  def Contrast(self):
@@ -2289,6 +2326,62 @@ class RBXInstance:
2289
2326
  else:
2290
2327
  threading.Thread(target=execute_move, daemon=True).start()
2291
2328
 
2329
+ @property
2330
+ def AutomaticScalingEnabled(self):
2331
+ if self.ClassName != "Humanoid":
2332
+ return None
2333
+ return self.memory_module.read_bool(
2334
+ self.raw_address,
2335
+ humanoid_offsets["AutomaticScalingEnabled"]
2336
+ )
2337
+
2338
+ @AutomaticScalingEnabled.setter
2339
+ def AutomaticScalingEnabled(self, value: bool):
2340
+ if self.ClassName != "Humanoid":
2341
+ raise AttributeError("AutomaticScalingEnabled is only available on Humanoid instances.")
2342
+ self._ensure_writable()
2343
+ self.memory_module.write_bool(
2344
+ self.raw_address + humanoid_offsets["AutomaticScalingEnabled"],
2345
+ bool(value)
2346
+ )
2347
+
2348
+ @property
2349
+ def UseJumpPower(self):
2350
+ if self.ClassName != "Humanoid":
2351
+ return None
2352
+ return self.memory_module.read_bool(
2353
+ self.raw_address,
2354
+ humanoid_offsets["UseJumpPower"]
2355
+ )
2356
+
2357
+ @UseJumpPower.setter
2358
+ def UseJumpPower(self, value: bool):
2359
+ if self.ClassName != "Humanoid":
2360
+ raise AttributeError("UseJumpPower is only available on Humanoid instances.")
2361
+ self._ensure_writable()
2362
+ self.memory_module.write_bool(
2363
+ self.raw_address + humanoid_offsets["UseJumpPower"],
2364
+ bool(value)
2365
+ )
2366
+
2367
+ @property
2368
+ def WalkTimer(self):
2369
+ if self.ClassName != "Humanoid":
2370
+ return None
2371
+ return self.memory_module.read_double(
2372
+ self.raw_address + humanoid_offsets["WalkTimer"]
2373
+ )
2374
+
2375
+ @WalkTimer.setter
2376
+ def WalkTimer(self, value: float):
2377
+ if self.ClassName != "Humanoid":
2378
+ raise AttributeError("WalkTimer is only available on Humanoid instances.")
2379
+ self._ensure_writable()
2380
+ self.memory_module.write_double(
2381
+ self.raw_address + humanoid_offsets["WalkTimer"],
2382
+ float(value)
2383
+ )
2384
+
2292
2385
  # adornee / animation props #
2293
2386
  @property
2294
2387
  def Adornee(self):
@@ -2384,6 +2477,8 @@ class RBXInstance:
2384
2477
  bytecode_offset = Offsets["LocalScript"]["ByteCode"]
2385
2478
  elif classname == "ModuleScript":
2386
2479
  bytecode_offset = Offsets["ModuleScript"]["ByteCode"]
2480
+ elif classname == "Script":
2481
+ bytecode_offset = script_offsets["ByteCode"]
2387
2482
  else:
2388
2483
  return None
2389
2484
 
@@ -2406,8 +2501,10 @@ class RBXInstance:
2406
2501
  bytecode_offset = Offsets["LocalScript"]["ByteCode"]
2407
2502
  elif classname == "ModuleScript":
2408
2503
  bytecode_offset = Offsets["ModuleScript"]["ByteCode"]
2504
+ elif classname == "Script":
2505
+ bytecode_offset = script_offsets["ByteCode"]
2409
2506
  else:
2410
- raise AttributeError("Bytecode can only be written for LocalScript or ModuleScript.")
2507
+ raise AttributeError("Bytecode can only be written for LocalScript, ModuleScript, or Script.")
2411
2508
 
2412
2509
  encoded_data = encryptor.encode_roblox(value)
2413
2510
  new_size = len(encoded_data)
@@ -2431,6 +2528,8 @@ class RBXInstance:
2431
2528
  guid_offset = Offsets["LocalScript"]["GUID"]
2432
2529
  elif classname == "ModuleScript":
2433
2530
  guid_offset = Offsets["ModuleScript"]["GUID"]
2531
+ elif classname == "Script":
2532
+ guid_offset = script_offsets["GUID"]
2434
2533
  else:
2435
2534
  return None
2436
2535
  return self.memory_module.read_string(
@@ -2445,6 +2544,8 @@ class RBXInstance:
2445
2544
  hash_offset = Offsets["LocalScript"]["Hash"]
2446
2545
  elif classname == "ModuleScript":
2447
2546
  hash_offset = Offsets["ModuleScript"]["Hash"]
2547
+ elif classname == "Script":
2548
+ hash_offset = script_offsets["Hash"]
2448
2549
  else:
2449
2550
  return None
2450
2551
  return self.memory_module.read_string(
@@ -2599,6 +2700,471 @@ class RBXInstance:
2599
2700
  except: pass
2600
2701
  return "Unknown"
2601
2702
 
2703
+ # modulescript props #
2704
+ @property
2705
+ def IsCoreScript(self):
2706
+ if self.ClassName != "ModuleScript":
2707
+ return None
2708
+ return self.memory_module.read_bool(
2709
+ self.raw_address,
2710
+ Offsets["ModuleScript"]["IsCoreScript"]
2711
+ )
2712
+
2713
+ # beam props #
2714
+ @property
2715
+ def Attachment0(self):
2716
+ cn = self.ClassName
2717
+ if cn == "Beam":
2718
+ ptr = self.memory_module.get_pointer(self.raw_address, beam_offsets["Attachment0"])
2719
+ return RBXInstance(ptr, self.memory_module) if ptr != 0 else None
2720
+ return None
2721
+
2722
+ @property
2723
+ def Attachment1(self):
2724
+ cn = self.ClassName
2725
+ if cn == "Beam":
2726
+ ptr = self.memory_module.get_pointer(self.raw_address, beam_offsets["Attachment1"])
2727
+ return RBXInstance(ptr, self.memory_module) if ptr != 0 else None
2728
+ return None
2729
+
2730
+ @property
2731
+ def CurveSize0(self):
2732
+ if self.ClassName != "Beam":
2733
+ return None
2734
+ return self.memory_module.read_float(self.raw_address, beam_offsets["CurveSize0"])
2735
+
2736
+ @CurveSize0.setter
2737
+ def CurveSize0(self, value: float):
2738
+ if self.ClassName != "Beam":
2739
+ raise AttributeError("CurveSize0 is only available on Beam instances.")
2740
+ self._ensure_writable()
2741
+ self.memory_module.write_float(self.raw_address + beam_offsets["CurveSize0"], float(value))
2742
+
2743
+ @property
2744
+ def CurveSize1(self):
2745
+ if self.ClassName != "Beam":
2746
+ return None
2747
+ return self.memory_module.read_float(self.raw_address, beam_offsets["CurveSize1"])
2748
+
2749
+ @CurveSize1.setter
2750
+ def CurveSize1(self, value: float):
2751
+ if self.ClassName != "Beam":
2752
+ raise AttributeError("CurveSize1 is only available on Beam instances.")
2753
+ self._ensure_writable()
2754
+ self.memory_module.write_float(self.raw_address + beam_offsets["CurveSize1"], float(value))
2755
+
2756
+ @property
2757
+ def LightEmission(self):
2758
+ cn = self.ClassName
2759
+ if cn == "Beam":
2760
+ return self.memory_module.read_float(self.raw_address, beam_offsets["LightEmission"])
2761
+ elif cn == "ParticleEmitter":
2762
+ return self.memory_module.read_float(self.raw_address, particleemitter_offsets["LightEmission"])
2763
+ return None
2764
+
2765
+ @LightEmission.setter
2766
+ def LightEmission(self, value: float):
2767
+ cn = self.ClassName
2768
+ self._ensure_writable()
2769
+ if cn == "Beam":
2770
+ self.memory_module.write_float(self.raw_address + beam_offsets["LightEmission"], float(value))
2771
+ elif cn == "ParticleEmitter":
2772
+ self.memory_module.write_float(self.raw_address + particleemitter_offsets["LightEmission"], float(value))
2773
+ else:
2774
+ raise AttributeError("LightEmission is only available on Beam or ParticleEmitter instances.")
2775
+
2776
+ @property
2777
+ def LightInfluence(self):
2778
+ cn = self.ClassName
2779
+ if cn == "Beam":
2780
+ return self.memory_module.read_float(self.raw_address, beam_offsets["LightInfluence"])
2781
+ elif cn == "ParticleEmitter":
2782
+ return self.memory_module.read_float(self.raw_address, particleemitter_offsets["LightInfluence"])
2783
+ return None
2784
+
2785
+ @LightInfluence.setter
2786
+ def LightInfluence(self, value: float):
2787
+ cn = self.ClassName
2788
+ self._ensure_writable()
2789
+ if cn == "Beam":
2790
+ self.memory_module.write_float(self.raw_address + beam_offsets["LightInfluence"], float(value))
2791
+ elif cn == "ParticleEmitter":
2792
+ self.memory_module.write_float(self.raw_address + particleemitter_offsets["LightInfluence"], float(value))
2793
+ else:
2794
+ raise AttributeError("LightInfluence is only available on Beam or ParticleEmitter instances.")
2795
+
2796
+ @property
2797
+ def TextureLength(self):
2798
+ if self.ClassName != "Beam":
2799
+ return None
2800
+ return self.memory_module.read_float(self.raw_address, beam_offsets["TextureLength"])
2801
+
2802
+ @TextureLength.setter
2803
+ def TextureLength(self, value: float):
2804
+ if self.ClassName != "Beam":
2805
+ raise AttributeError("TextureLength is only available on Beam instances.")
2806
+ self._ensure_writable()
2807
+ self.memory_module.write_float(self.raw_address + beam_offsets["TextureLength"], float(value))
2808
+
2809
+ @property
2810
+ def TextureSpeed(self):
2811
+ if self.ClassName != "Beam":
2812
+ return None
2813
+ return self.memory_module.read_float(self.raw_address, beam_offsets["TextureSpeed"])
2814
+
2815
+ @TextureSpeed.setter
2816
+ def TextureSpeed(self, value: float):
2817
+ if self.ClassName != "Beam":
2818
+ raise AttributeError("TextureSpeed is only available on Beam instances.")
2819
+ self._ensure_writable()
2820
+ self.memory_module.write_float(self.raw_address + beam_offsets["TextureSpeed"], float(value))
2821
+
2822
+ @property
2823
+ def Width0(self):
2824
+ if self.ClassName != "Beam":
2825
+ return None
2826
+ return self.memory_module.read_float(self.raw_address, beam_offsets["Width0"])
2827
+
2828
+ @Width0.setter
2829
+ def Width0(self, value: float):
2830
+ if self.ClassName != "Beam":
2831
+ raise AttributeError("Width0 is only available on Beam instances.")
2832
+ self._ensure_writable()
2833
+ self.memory_module.write_float(self.raw_address + beam_offsets["Width0"], float(value))
2834
+
2835
+ @property
2836
+ def Width1(self):
2837
+ if self.ClassName != "Beam":
2838
+ return None
2839
+ return self.memory_module.read_float(self.raw_address, beam_offsets["Width1"])
2840
+
2841
+ @Width1.setter
2842
+ def Width1(self, value: float):
2843
+ if self.ClassName != "Beam":
2844
+ raise AttributeError("Width1 is only available on Beam instances.")
2845
+ self._ensure_writable()
2846
+ self.memory_module.write_float(self.raw_address + beam_offsets["Width1"], float(value))
2847
+
2848
+ @property
2849
+ def ZOffset(self):
2850
+ cn = self.ClassName
2851
+ if cn == "Beam":
2852
+ return self.memory_module.read_float(self.raw_address, beam_offsets["ZOffset"])
2853
+ elif cn == "ParticleEmitter":
2854
+ return self.memory_module.read_float(self.raw_address, particleemitter_offsets["ZOffset"])
2855
+ return None
2856
+
2857
+ @ZOffset.setter
2858
+ def ZOffset(self, value: float):
2859
+ cn = self.ClassName
2860
+ self._ensure_writable()
2861
+ if cn == "Beam":
2862
+ self.memory_module.write_float(self.raw_address + beam_offsets["ZOffset"], float(value))
2863
+ elif cn == "ParticleEmitter":
2864
+ self.memory_module.write_float(self.raw_address + particleemitter_offsets["ZOffset"], float(value))
2865
+ else:
2866
+ raise AttributeError("ZOffset is only available on Beam or ParticleEmitter instances.")
2867
+
2868
+ # particleemitter props #
2869
+ @property
2870
+ def Acceleration(self):
2871
+ if self.ClassName != "ParticleEmitter":
2872
+ return None
2873
+ data = self.memory_module.read_floats(
2874
+ self.raw_address + particleemitter_offsets["Acceleration"], 3
2875
+ )
2876
+ return Vector3(*data)
2877
+
2878
+ @Acceleration.setter
2879
+ def Acceleration(self, value):
2880
+ if self.ClassName != "ParticleEmitter":
2881
+ raise AttributeError("Acceleration is only available on ParticleEmitter instances.")
2882
+ self._ensure_writable()
2883
+ vec = self._as_vector3(value, "Acceleration")
2884
+ self.memory_module.write_floats(
2885
+ self.raw_address + particleemitter_offsets["Acceleration"],
2886
+ (vec.X, vec.Y, vec.Z)
2887
+ )
2888
+
2889
+ @property
2890
+ def Drag(self):
2891
+ if self.ClassName != "ParticleEmitter":
2892
+ return None
2893
+ return self.memory_module.read_float(self.raw_address, particleemitter_offsets["Drag"])
2894
+
2895
+ @Drag.setter
2896
+ def Drag(self, value: float):
2897
+ if self.ClassName != "ParticleEmitter":
2898
+ raise AttributeError("Drag is only available on ParticleEmitter instances.")
2899
+ self._ensure_writable()
2900
+ self.memory_module.write_float(self.raw_address + particleemitter_offsets["Drag"], float(value))
2901
+
2902
+ @property
2903
+ def Lifetime(self):
2904
+ if self.ClassName != "ParticleEmitter":
2905
+ return None
2906
+ data = self.memory_module.read_floats(
2907
+ self.raw_address + particleemitter_offsets["Lifetime"], 2
2908
+ )
2909
+ return NumberRange(*data)
2910
+
2911
+ @property
2912
+ def Rate(self):
2913
+ if self.ClassName != "ParticleEmitter":
2914
+ return None
2915
+ return self.memory_module.read_float(self.raw_address, particleemitter_offsets["Rate"])
2916
+
2917
+ @Rate.setter
2918
+ def Rate(self, value: float):
2919
+ if self.ClassName != "ParticleEmitter":
2920
+ raise AttributeError("Rate is only available on ParticleEmitter instances.")
2921
+ self._ensure_writable()
2922
+ self.memory_module.write_float(self.raw_address + particleemitter_offsets["Rate"], float(value))
2923
+
2924
+ @property
2925
+ def RotSpeed(self):
2926
+ if self.ClassName != "ParticleEmitter":
2927
+ return None
2928
+ data = self.memory_module.read_floats(
2929
+ self.raw_address + particleemitter_offsets["RotSpeed"], 2
2930
+ )
2931
+ return NumberRange(*data)
2932
+
2933
+ @RotSpeed.setter
2934
+ def RotSpeed(self, value):
2935
+ if self.ClassName != "ParticleEmitter":
2936
+ raise AttributeError("RotSpeed is only available on ParticleEmitter instances.")
2937
+ self._ensure_writable()
2938
+ if isinstance(value, NumberRange):
2939
+ vals = (value.Min, value.Max)
2940
+ elif isinstance(value, (tuple, list)) and len(value) == 2:
2941
+ vals = (float(value[0]), float(value[1]))
2942
+ else:
2943
+ v = float(value)
2944
+ vals = (v, v)
2945
+ self.memory_module.write_floats(
2946
+ self.raw_address + particleemitter_offsets["RotSpeed"], vals
2947
+ )
2948
+
2949
+ @property
2950
+ def ParticleSpeed(self):
2951
+ """Speed property for ParticleEmitter (named ParticleSpeed to avoid conflict with AnimationTrack.Speed)."""
2952
+ if self.ClassName != "ParticleEmitter":
2953
+ return None
2954
+ data = self.memory_module.read_floats(
2955
+ self.raw_address + particleemitter_offsets["Speed"], 2
2956
+ )
2957
+ return NumberRange(*data)
2958
+
2959
+ @ParticleSpeed.setter
2960
+ def ParticleSpeed(self, value):
2961
+ if self.ClassName != "ParticleEmitter":
2962
+ raise AttributeError("ParticleSpeed is only available on ParticleEmitter instances.")
2963
+ self._ensure_writable()
2964
+ if isinstance(value, NumberRange):
2965
+ vals = (value.Min, value.Max)
2966
+ elif isinstance(value, (tuple, list)) and len(value) == 2:
2967
+ vals = (float(value[0]), float(value[1]))
2968
+ else:
2969
+ v = float(value)
2970
+ vals = (v, v)
2971
+ self.memory_module.write_floats(
2972
+ self.raw_address + particleemitter_offsets["Speed"], vals
2973
+ )
2974
+
2975
+ @property
2976
+ def SpreadAngle(self):
2977
+ if self.ClassName != "ParticleEmitter":
2978
+ return None
2979
+ data = self.memory_module.read_floats(
2980
+ self.raw_address + particleemitter_offsets["SpreadAngle"], 2
2981
+ )
2982
+ return Vector2(*data)
2983
+
2984
+ @property
2985
+ def TimeScale(self):
2986
+ if self.ClassName != "ParticleEmitter":
2987
+ return None
2988
+ return self.memory_module.read_float(self.raw_address, particleemitter_offsets["TimeScale"])
2989
+
2990
+ @TimeScale.setter
2991
+ def TimeScale(self, value: float):
2992
+ if self.ClassName != "ParticleEmitter":
2993
+ raise AttributeError("TimeScale is only available on ParticleEmitter instances.")
2994
+ self._ensure_writable()
2995
+ self.memory_module.write_float(self.raw_address + particleemitter_offsets["TimeScale"], float(value))
2996
+
2997
+ @property
2998
+ def VelocityInheritance(self):
2999
+ if self.ClassName != "ParticleEmitter":
3000
+ return None
3001
+ return self.memory_module.read_float(self.raw_address, particleemitter_offsets["VelocityInheritance"])
3002
+
3003
+ @VelocityInheritance.setter
3004
+ def VelocityInheritance(self, value: float):
3005
+ if self.ClassName != "ParticleEmitter":
3006
+ raise AttributeError("VelocityInheritance is only available on ParticleEmitter instances.")
3007
+ self._ensure_writable()
3008
+ self.memory_module.write_float(self.raw_address + particleemitter_offsets["VelocityInheritance"], float(value))
3009
+
3010
+ # vehicleseat props #
3011
+ @property
3012
+ def MaxSpeed(self):
3013
+ if self.ClassName != "VehicleSeat":
3014
+ return None
3015
+ return self.memory_module.read_float(self.raw_address, vehicleseat_offsets["MaxSpeed"])
3016
+
3017
+ @MaxSpeed.setter
3018
+ def MaxSpeed(self, value: float):
3019
+ if self.ClassName != "VehicleSeat":
3020
+ raise AttributeError("MaxSpeed is only available on VehicleSeat instances.")
3021
+ self._ensure_writable()
3022
+ self.memory_module.write_float(self.raw_address + vehicleseat_offsets["MaxSpeed"], float(value))
3023
+
3024
+ @property
3025
+ def SteerFloat(self):
3026
+ if self.ClassName != "VehicleSeat":
3027
+ return None
3028
+ return self.memory_module.read_float(self.raw_address, vehicleseat_offsets["SteerFloat"])
3029
+
3030
+ @SteerFloat.setter
3031
+ def SteerFloat(self, value: float):
3032
+ if self.ClassName != "VehicleSeat":
3033
+ raise AttributeError("SteerFloat is only available on VehicleSeat instances.")
3034
+ self._ensure_writable()
3035
+ self.memory_module.write_float(self.raw_address + vehicleseat_offsets["SteerFloat"], float(value))
3036
+
3037
+ @property
3038
+ def ThrottleFloat(self):
3039
+ if self.ClassName != "VehicleSeat":
3040
+ return None
3041
+ return self.memory_module.read_float(self.raw_address, vehicleseat_offsets["ThrottleFloat"])
3042
+
3043
+ @ThrottleFloat.setter
3044
+ def ThrottleFloat(self, value: float):
3045
+ if self.ClassName != "VehicleSeat":
3046
+ raise AttributeError("ThrottleFloat is only available on VehicleSeat instances.")
3047
+ self._ensure_writable()
3048
+ self.memory_module.write_float(self.raw_address + vehicleseat_offsets["ThrottleFloat"], float(value))
3049
+
3050
+ @property
3051
+ def Torque(self):
3052
+ if self.ClassName != "VehicleSeat":
3053
+ return None
3054
+ return self.memory_module.read_float(self.raw_address, vehicleseat_offsets["Torque"])
3055
+
3056
+ @Torque.setter
3057
+ def Torque(self, value: float):
3058
+ if self.ClassName != "VehicleSeat":
3059
+ raise AttributeError("Torque is only available on VehicleSeat instances.")
3060
+ self._ensure_writable()
3061
+ self.memory_module.write_float(self.raw_address + vehicleseat_offsets["Torque"], float(value))
3062
+
3063
+ @property
3064
+ def TurnSpeed(self):
3065
+ if self.ClassName != "VehicleSeat":
3066
+ return None
3067
+ return self.memory_module.read_float(self.raw_address, vehicleseat_offsets["TurnSpeed"])
3068
+
3069
+ @TurnSpeed.setter
3070
+ def TurnSpeed(self, value: float):
3071
+ if self.ClassName != "VehicleSeat":
3072
+ raise AttributeError("TurnSpeed is only available on VehicleSeat instances.")
3073
+ self._ensure_writable()
3074
+ self.memory_module.write_float(self.raw_address + vehicleseat_offsets["TurnSpeed"], float(value))
3075
+
3076
+ # weld / weldconstraint props #
3077
+ @property
3078
+ def Part0(self):
3079
+ cn = self.ClassName
3080
+ if cn == "Weld":
3081
+ ptr = self.memory_module.get_pointer(self.raw_address, weld_offsets["Part0"])
3082
+ elif cn == "WeldConstraint":
3083
+ ptr = self.memory_module.get_pointer(self.raw_address, weldconstraint_offsets["Part0"])
3084
+ else:
3085
+ return None
3086
+ return RBXInstance(ptr, self.memory_module) if ptr != 0 else None
3087
+
3088
+ @Part0.setter
3089
+ def Part0(self, value):
3090
+ cn = self.ClassName
3091
+ if cn != "Weld" and cn != "WeldConstraint":
3092
+ raise AttributeError("Part0 is only available on Weld or WeldConstraint instances.")
3093
+ self._ensure_writable()
3094
+ if value is None:
3095
+ target = 0
3096
+ elif isinstance(value, RBXInstance):
3097
+ target = value.raw_address
3098
+ elif isinstance(value, int):
3099
+ target = value
3100
+ else:
3101
+ raise TypeError("Part0 must be set to an RBXInstance, int address, or None.")
3102
+ offset = weld_offsets["Part0"] if cn == "Weld" else weldconstraint_offsets["Part0"]
3103
+ self.memory_module.write_long(self.raw_address + offset, target)
3104
+
3105
+ @property
3106
+ def Part1(self):
3107
+ cn = self.ClassName
3108
+ if cn == "Weld":
3109
+ ptr = self.memory_module.get_pointer(self.raw_address, weld_offsets["Part1"])
3110
+ elif cn == "WeldConstraint":
3111
+ ptr = self.memory_module.get_pointer(self.raw_address, weldconstraint_offsets["Part1"])
3112
+ else:
3113
+ return None
3114
+ return RBXInstance(ptr, self.memory_module) if ptr != 0 else None
3115
+
3116
+ @Part1.setter
3117
+ def Part1(self, value):
3118
+ cn = self.ClassName
3119
+ if cn != "Weld" and cn != "WeldConstraint":
3120
+ raise AttributeError("Part1 is only available on Weld or WeldConstraint instances.")
3121
+ self._ensure_writable()
3122
+ if value is None:
3123
+ target = 0
3124
+ elif isinstance(value, RBXInstance):
3125
+ target = value.raw_address
3126
+ elif isinstance(value, int):
3127
+ target = value
3128
+ else:
3129
+ raise TypeError("Part1 must be set to an RBXInstance, int address, or None.")
3130
+ offset = weld_offsets["Part1"] if cn == "Weld" else weldconstraint_offsets["Part1"]
3131
+ self.memory_module.write_long(self.raw_address + offset, target)
3132
+
3133
+ # surfaceappearance props #
3134
+ @property
3135
+ def AlphaMode(self):
3136
+ if self.ClassName != "SurfaceAppearance":
3137
+ return None
3138
+ return self.memory_module.read_int(
3139
+ self.raw_address, surfaceappearance_offsets["AlphaMode"]
3140
+ )
3141
+
3142
+ @AlphaMode.setter
3143
+ def AlphaMode(self, value: int):
3144
+ if self.ClassName != "SurfaceAppearance":
3145
+ raise AttributeError("AlphaMode is only available on SurfaceAppearance instances.")
3146
+ self._ensure_writable()
3147
+ self.memory_module.write_int(
3148
+ self.raw_address + surfaceappearance_offsets["AlphaMode"], int(value)
3149
+ )
3150
+
3151
+ @property
3152
+ def EmissiveStrength(self):
3153
+ if self.ClassName != "SurfaceAppearance":
3154
+ return None
3155
+ return self.memory_module.read_float(
3156
+ self.raw_address, surfaceappearance_offsets["EmissiveStrength"]
3157
+ )
3158
+
3159
+ @EmissiveStrength.setter
3160
+ def EmissiveStrength(self, value: float):
3161
+ if self.ClassName != "SurfaceAppearance":
3162
+ raise AttributeError("EmissiveStrength is only available on SurfaceAppearance instances.")
3163
+ self._ensure_writable()
3164
+ self.memory_module.write_float(
3165
+ self.raw_address + surfaceappearance_offsets["EmissiveStrength"], float(value)
3166
+ )
3167
+
2602
3168
  class AttributeValue:
2603
3169
  def __init__(self, address, name, type_name, memory_module):
2604
3170
  self.address = address
@@ -2772,10 +3338,37 @@ class PlayerClass(RBXInstance):
2772
3338
  return RBXInstance(TeamAddress, self.memory_module)
2773
3339
 
2774
3340
  @property
2775
- def Country(self):
3341
+ def LocaleId(self):
2776
3342
  return self.memory_module.read_string(
2777
3343
  self.raw_address,
2778
- self.offset_base["Country"]
3344
+ self.offset_base["LocaleId"]
3345
+ )
3346
+
3347
+ @property
3348
+ def Country(self):
3349
+ """Deprecated: Use LocaleId instead. Renamed in V2.1.4."""
3350
+ return self.LocaleId
3351
+
3352
+ @property
3353
+ def TeamColor(self):
3354
+ return self.memory_module.read_int(
3355
+ self.raw_address,
3356
+ self.offset_base["TeamColor"]
3357
+ )
3358
+
3359
+ @TeamColor.setter
3360
+ def TeamColor(self, value: int):
3361
+ self._ensure_writable()
3362
+ self.memory_module.write_int(
3363
+ self.raw_address + self.offset_base["TeamColor"],
3364
+ int(value)
3365
+ )
3366
+
3367
+ @property
3368
+ def AccountAge(self):
3369
+ return self.memory_module.read_int(
3370
+ self.raw_address,
3371
+ self.offset_base["AccountAge"]
2779
3372
  )
2780
3373
 
2781
3374
  @property
@@ -3870,3 +4463,69 @@ class MouseService(ServiceBase):
3870
4463
  if input_obj is None:
3871
4464
  return
3872
4465
  input_obj.MousePosition = value
4466
+
4467
+ @property
4468
+ def InputObject2(self):
4469
+ if self.failed:
4470
+ return None
4471
+ try:
4472
+ input_ptr = self.memory_module.get_pointer(
4473
+ self.instance.raw_address,
4474
+ self.offset_base["InputObject2"]
4475
+ )
4476
+ if input_ptr == 0:
4477
+ return None
4478
+ return InputObject(input_ptr, self.memory_module)
4479
+ except (KeyError, OSError):
4480
+ return None
4481
+
4482
+ class UserInputService(ServiceBase):
4483
+ def __init__(self, memory_module, game: DataModel):
4484
+ super().__init__()
4485
+ self.memory_module = memory_module
4486
+ self.offset_base = Offsets["UserInputService"]
4487
+ try:
4488
+ uis_instance: RBXInstance = game.GetRawService("UserInputService")
4489
+ if uis_instance is None or uis_instance.ClassName != "UserInputService":
4490
+ self.failed = True
4491
+ else:
4492
+ self.instance = uis_instance
4493
+ except (KeyError, OSError):
4494
+ self.failed = True
4495
+
4496
+ @property
4497
+ def WindowInputState(self):
4498
+ if self.failed:
4499
+ return None
4500
+ try:
4501
+ ptr = self.memory_module.get_pointer(
4502
+ self.instance.raw_address,
4503
+ self.offset_base["WindowInputState"]
4504
+ )
4505
+ if ptr == 0:
4506
+ return None
4507
+ return WindowInputState(ptr, self.memory_module)
4508
+ except (KeyError, OSError):
4509
+ return None
4510
+
4511
+ class WindowInputState(RBXInstance):
4512
+ def __init__(self, raw_address: int, memory_module):
4513
+ super().__init__(raw_address, memory_module)
4514
+ self._wis_offsets = Offsets["WindowInputState"]
4515
+
4516
+ @property
4517
+ def CapsLock(self):
4518
+ return self.memory_module.read_bool(
4519
+ self.raw_address,
4520
+ self._wis_offsets["CapsLock"]
4521
+ )
4522
+
4523
+ @property
4524
+ def CurrentTextBox(self):
4525
+ ptr = self.memory_module.get_pointer(
4526
+ self.raw_address,
4527
+ self._wis_offsets["CurrentTextBox"]
4528
+ )
4529
+ if ptr == 0:
4530
+ return None
4531
+ return RBXInstance(ptr, self.memory_module)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: robloxmemoryapi
3
- Version: 0.2.7
3
+ Version: 0.2.8
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