ExternalAPI 0.1.0__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.
@@ -0,0 +1,11 @@
1
+ from .include.core import inject
2
+ from .include.core.get import get
3
+ from .include.core.edit import edit
4
+ from .include.core.pars import pars
5
+
6
+
7
+ def init(process_name: str = "cs2.exe"):
8
+ return inject.init(process_name)
9
+
10
+
11
+ __all__ = ["init", "inject", "get", "edit", "pars"]
@@ -0,0 +1,5 @@
1
+ from .parser import parse_offsets, get_address_by_name
2
+ from .manager import ApiManager
3
+
4
+ __all__ = ["parse_offsets", "get_address_by_name", "ApiManager"]
5
+
@@ -0,0 +1,12 @@
1
+ from typing import Optional
2
+
3
+ from .parser import get_address_by_name
4
+
5
+
6
+ class ApiManager:
7
+
8
+ def __init__(self, offsets_path: Optional[str] = None) -> None:
9
+ self._offsets_path = offsets_path
10
+
11
+ def get_address(self, name: str) -> int:
12
+ return get_address_by_name(name, self._offsets_path)
@@ -0,0 +1,113 @@
1
+ import requests
2
+ from pathlib import Path
3
+ from typing import Dict, Tuple, Optional
4
+
5
+ GITHUB_USER = "EclipseCS2"
6
+ REPO_NAME = "ExternalAPI"
7
+ RAW_URL = f"https://raw.githubusercontent.com/EclipseCS2/ExternalAPI/refs/heads/main/ExternalAPI/offsets.cfg"
8
+
9
+ def _default_offsets_path() -> Path:
10
+ """Определяет путь к файлу offsets.cfg в корне проекта."""
11
+ return Path(__file__).resolve().parents[2] / "offsets.cfg"
12
+
13
+ def download_offsets(path: Path) -> bool:
14
+ """
15
+ Скачивает свежий файл офсетов с GitHub и сохраняет его локально.
16
+ """
17
+ try:
18
+ print(f"[API] Checking for updates at: {RAW_URL}")
19
+ response = requests.get(RAW_URL, timeout=10)
20
+ if response.status_code == 200:
21
+ path.write_text(response.text, encoding="utf-8")
22
+ print(f"[API] Success! Offsets saved to {path.name}")
23
+ return True
24
+ else:
25
+ print(f"[API] Failed to download: Status {response.status_code}")
26
+ return False
27
+ except Exception as e:
28
+ print(f"[API] Network error during update: {e}")
29
+ return False
30
+
31
+ def parse_offsets(path: str | None = None) -> Dict[str, int]:
32
+ """Парсит файл офсетов и возвращает словарь {имя: адрес}."""
33
+ cfg_path = Path(path) if path is not None else _default_offsets_path()
34
+ offsets: Dict[str, int] = {}
35
+
36
+ if not cfg_path.is_file():
37
+ if not download_offsets(cfg_path):
38
+ return offsets
39
+
40
+ text = cfg_path.read_text(encoding="utf-8", errors="ignore")
41
+ for raw_line in text.splitlines():
42
+ line = raw_line.strip()
43
+ if not line or line.startswith("#"):
44
+ continue
45
+ if "=" not in line:
46
+ continue
47
+
48
+ name, value = line.split("=", 1)
49
+ name = name.strip()
50
+ value = value.strip().strip("'\"")
51
+
52
+ if not name or not value:
53
+ continue
54
+
55
+ try:
56
+ addr = int(value, 0)
57
+ offsets[name] = addr
58
+ except ValueError:
59
+ continue
60
+
61
+ return offsets
62
+
63
+ def parse_offsets_with_modules(path: str | None = None) -> Dict[str, Tuple[int, Optional[str]]]:
64
+ """Парсит офсеты и привязывает их к модулям (DLL)."""
65
+ cfg_path = Path(path) if path is not None else _default_offsets_path()
66
+ result: Dict[str, Tuple[int, Optional[str]]] = {}
67
+ current_module: Optional[str] = None
68
+
69
+ if not cfg_path.is_file():
70
+ if not download_offsets(cfg_path):
71
+ return result
72
+
73
+ text = cfg_path.read_text(encoding="utf-8", errors="ignore")
74
+ for raw_line in text.splitlines():
75
+ line = raw_line.strip()
76
+ if not line:
77
+ continue
78
+
79
+ if line.startswith("#"):
80
+ # Извлекаем имя модуля из комментария, например: "# client.dll"
81
+ potential_module = line.lstrip("#").strip()
82
+ if potential_module.lower().endswith(".dll"):
83
+ current_module = potential_module
84
+ continue
85
+
86
+ if "=" not in line:
87
+ continue
88
+
89
+ name, value = line.split("=", 1)
90
+ name = name.strip()
91
+ value = value.strip().strip("'\"")
92
+
93
+ try:
94
+ addr = int(value, 0)
95
+ result[name] = (addr, current_module)
96
+ except ValueError:
97
+ continue
98
+
99
+ return result
100
+
101
+ def get_address_by_name(name: str, path: str | None = None) -> int:
102
+ """Возвращает адрес по его имени. Выбрасывает ошибку, если имя не найдено."""
103
+ offsets = parse_offsets(path)
104
+ if name not in offsets:
105
+ raise KeyError(f"Offset '{name}' not found in offsets.cfg")
106
+ return offsets[name]
107
+
108
+ def get_address_and_module(name: str, path: str | None = None) -> Tuple[int, Optional[str]]:
109
+ """Возвращает кортеж (адрес, имя_модуля) по имени офсета."""
110
+ offsets = parse_offsets_with_modules(path)
111
+ if name not in offsets:
112
+ raise KeyError(f"Offset '{name}' not found in offsets.cfg")
113
+ return offsets[name]
@@ -0,0 +1,5 @@
1
+ from . import Api
2
+ from . import core
3
+
4
+ __all__ = ["Api", "core"]
5
+
@@ -0,0 +1,21 @@
1
+ from .memory import Memory
2
+ from .manager import CoreManager, set_default_process, get_default_process
3
+ from .inject import init as inject_init
4
+ from .get import get, get_value
5
+ from .edit import edit, set_value
6
+ from .pars import pars, get_offset
7
+
8
+ __all__ = [
9
+ "Memory",
10
+ "CoreManager",
11
+ "set_default_process",
12
+ "get_default_process",
13
+ "inject_init",
14
+ "get",
15
+ "get_value",
16
+ "edit",
17
+ "set_value",
18
+ "pars",
19
+ "get_offset",
20
+ ]
21
+
@@ -0,0 +1,42 @@
1
+ from typing import Optional, Union
2
+
3
+ from .manager import CoreManager
4
+ from ..validation import normalize_address
5
+
6
+
7
+ def set_value(target: Union[int, str], value: int | float, process: int | str | None = None, offsets_path: Optional[str] = None) -> int:
8
+ core = CoreManager(process, offsets_path=offsets_path)
9
+
10
+ if isinstance(target, str):
11
+ addr = core.get_address(target)
12
+ else:
13
+ addr = normalize_address(target)
14
+
15
+ if isinstance(value, float):
16
+ core.memory.write_float(addr, value)
17
+ else:
18
+ core.memory.write_int(addr, int(value))
19
+
20
+ return addr
21
+
22
+
23
+ class _Editor:
24
+ def __call__(
25
+ self,
26
+ target: Union[int, str],
27
+ value: int | float,
28
+ *,
29
+ process: int | str | None = None,
30
+ offsets_path: Optional[str] = None,
31
+ ) -> int:
32
+ return set_value(target, value, process=process, offsets_path=offsets_path)
33
+
34
+ def __getattr__(self, name: str):
35
+ raise AttributeError(
36
+ f"Синтаксис edit.{name}(...) отключён. Используйте edit('{name}', ...)"
37
+ )
38
+
39
+
40
+ edit = _Editor()
41
+
42
+ __all__ = ["set_value", "edit"]
@@ -0,0 +1,39 @@
1
+ from typing import Optional, Union
2
+
3
+ from .manager import CoreManager
4
+ from ..validation import normalize_address
5
+
6
+
7
+ def get_value(target: Union[int, str], process: int | str | None = None, as_float: bool = False, offsets_path: Optional[str] = None) -> int | float:
8
+ core = CoreManager(process, offsets_path=offsets_path)
9
+
10
+ if isinstance(target, str):
11
+ addr = core.get_address(target)
12
+ else:
13
+ addr = normalize_address(target)
14
+
15
+ if as_float:
16
+ return core.memory.read_float(addr)
17
+ return core.memory.read_int(addr)
18
+
19
+
20
+ class _Getter:
21
+ def __call__(
22
+ self,
23
+ target: Union[int, str],
24
+ *,
25
+ process: int | str | None = None,
26
+ as_float: bool = False,
27
+ offsets_path: Optional[str] = None,
28
+ ) -> int | float:
29
+ return get_value(target, process=process, as_float=as_float, offsets_path=offsets_path)
30
+
31
+ def __getattr__(self, name: str):
32
+ raise AttributeError(
33
+ f"Синтаксис get.{name}() отключён. Используйте get('{name}')"
34
+ )
35
+
36
+
37
+ get = _Getter()
38
+
39
+ __all__ = ["get_value", "get"]
@@ -0,0 +1,16 @@
1
+ import pymem
2
+ import pymem.process
3
+
4
+ from .memory import Memory
5
+ from .manager import set_default_process
6
+
7
+
8
+ def init(process_name: str = "cs2.exe") -> Memory:
9
+ try:
10
+ proc_entry = pymem.process.process_from_name(process_name)
11
+ except ProcessLookupError:
12
+ raise RuntimeError(f"Process '{process_name}' not found")
13
+
14
+ pid = proc_entry.th32ProcessID
15
+ set_default_process(pid)
16
+ return Memory(pid)
@@ -0,0 +1,60 @@
1
+ from typing import Optional
2
+
3
+ from ..Api.manager import ApiManager
4
+ from ..Api.parser import get_address_and_module
5
+ from .memory import Memory
6
+
7
+
8
+ _default_process: int | str | None = None
9
+
10
+
11
+ def set_default_process(process: int | str) -> None:
12
+ global _default_process
13
+ _default_process = process
14
+
15
+
16
+ def get_default_process() -> int | str:
17
+ if _default_process is None:
18
+ raise RuntimeError("Default process is not set.")
19
+ return _default_process
20
+
21
+
22
+ class CoreManager:
23
+ def __init__(self, process: int | str | None = None, offsets_path: Optional[str] = None) -> None:
24
+ if process is None:
25
+ process = get_default_process()
26
+
27
+ self.api_manager = ApiManager(offsets_path)
28
+ self._offsets_path = offsets_path
29
+ self.memory = Memory(process)
30
+
31
+ def get_address(self, name: str) -> int:
32
+ offset = self.api_manager.get_address(name)
33
+
34
+ needs_base = name.startswith("dw") or name in [
35
+ "attack",
36
+ "attack2",
37
+ "back",
38
+ "duck",
39
+ "forward",
40
+ "jump",
41
+ "left",
42
+ "right",
43
+ "use",
44
+ "reload",
45
+ "sprint",
46
+ "lookatweapon",
47
+ "showscores",
48
+ "turnleft",
49
+ "turnright",
50
+ "zoom",
51
+ ]
52
+
53
+ if not needs_base:
54
+ return offset
55
+
56
+ _, module_name = get_address_and_module(name, self._offsets_path)
57
+ dll_name = module_name or "client.dll"
58
+
59
+ base = self.memory.get_module_base(dll_name)
60
+ return base + offset
@@ -0,0 +1,55 @@
1
+ import pymem
2
+ import pymem.process
3
+
4
+
5
+ class Memory:
6
+
7
+ def __init__(self, process: int | str) -> None:
8
+ if isinstance(process, int):
9
+ pm = pymem.Pymem()
10
+ pm.open_process_from_id(process)
11
+ else:
12
+ pm = pymem.Pymem(process)
13
+
14
+ self._pm = pm
15
+
16
+ def get_module_base(self, module_name: str) -> int:
17
+ module = pymem.process.module_from_name(self._pm.process_handle, module_name)
18
+ if not module:
19
+ return 0
20
+ return module.lpBaseOfDll
21
+
22
+ # --- указатели (8 байт, x64) ---
23
+ def read_longlong(self, address: int) -> int:
24
+ return self._pm.read_longlong(address)
25
+
26
+ # --- базовые байты ---
27
+ def read_bytes(self, address: int, size: int) -> bytes:
28
+ return self._pm.read_bytes(address, size)
29
+
30
+ def write_bytes(self, address: int, data: bytes | bytearray) -> None:
31
+ self._pm.write_bytes(address, data)
32
+
33
+ # --- int (4 байта) ---
34
+ def read_int(self, address: int) -> int:
35
+ return self._pm.read_int(address)
36
+
37
+ def write_int(self, address: int, value: int) -> None:
38
+ self._pm.write_int(address, int(value))
39
+
40
+ # --- float (4 байта) ---
41
+ def read_float(self, address: int) -> float:
42
+ return self._pm.read_float(address)
43
+
44
+ def write_float(self, address: int, value: float) -> None:
45
+ self._pm.write_float(address, float(value))
46
+
47
+ # --- double (8 байт) ---
48
+ def read_double(self, address: int) -> float:
49
+ return self._pm.read_double(address)
50
+
51
+ def write_double(self, address: int, value: float) -> None:
52
+ self._pm.write_double(address, float(value))
53
+
54
+ def close(self) -> None:
55
+ self._pm.close_process()
@@ -0,0 +1,54 @@
1
+ from typing import Optional
2
+
3
+ from ..Api.manager import ApiManager
4
+ from ..validation import normalize_offset
5
+ from .manager import CoreManager
6
+
7
+
8
+ def get_offset(name: str, offsets_path: Optional[str] = None) -> int:
9
+ api = ApiManager(offsets_path)
10
+ raw = normalize_offset(api.get_address(name))
11
+
12
+ if not (name.startswith("dw") or name in [
13
+ "attack",
14
+ "attack2",
15
+ "back",
16
+ "duck",
17
+ "forward",
18
+ "jump",
19
+ "left",
20
+ "right",
21
+ "use",
22
+ "reload",
23
+ "sprint",
24
+ "lookatweapon",
25
+ "showscores",
26
+ "turnleft",
27
+ "turnright",
28
+ "zoom",
29
+ ]):
30
+ return raw
31
+
32
+ core = CoreManager(process=None, offsets_path=offsets_path)
33
+
34
+ if name == "dwLocalPlayerPawn":
35
+ ptr_addr = core.get_address(name) # base + offset (указатель)
36
+ return core.memory.read_longlong(ptr_addr) # адрес pawn
37
+
38
+ return core.get_address(name)
39
+
40
+
41
+ class _Parser:
42
+ def __call__(self, target: str, *, offsets_path: Optional[str] = None) -> int:
43
+ return get_offset(target, offsets_path=offsets_path)
44
+
45
+ def __getattr__(self, name: str):
46
+ raise AttributeError(
47
+ f"Синтаксис pars.{name}() отключён. Используйте pars('{name}')"
48
+ )
49
+
50
+
51
+ pars = _Parser()
52
+
53
+ __all__ = ["get_offset", "pars"]
54
+
@@ -0,0 +1,143 @@
1
+ from typing import Union, Iterable, Mapping, Any, Optional
2
+
3
+
4
+ NumberLike = Union[int, str]
5
+
6
+
7
+ def to_int(value: NumberLike) -> int:
8
+ if isinstance(value, int):
9
+ return value
10
+ if isinstance(value, str):
11
+ text = value.strip()
12
+ if not text:
13
+ raise ValueError("Empty string cannot be converted to int")
14
+ return int(text, 0)
15
+ raise TypeError(f"Unsupported type for to_int: {type(value)!r}")
16
+
17
+
18
+ def to_float(value: Union[NumberLike, float]) -> float:
19
+ if isinstance(value, float):
20
+ return value
21
+ if isinstance(value, int):
22
+ return float(value)
23
+ if isinstance(value, str):
24
+ text = value.strip()
25
+ if not text:
26
+ raise ValueError("Empty string cannot be converted to float")
27
+ # Попробуем сначала как обычное число, потом как hex
28
+ try:
29
+ return float(text)
30
+ except ValueError:
31
+ return float(int(text, 0))
32
+ raise TypeError(f"Unsupported type for to_float: {type(value)!r}")
33
+
34
+
35
+ def to_bool(value: Any) -> bool:
36
+ if isinstance(value, bool):
37
+ return value
38
+ if isinstance(value, int):
39
+ if value in (0, 1):
40
+ return bool(value)
41
+ raise ValueError(f"Cannot convert int {value!r} to bool (only 0 or 1 allowed)")
42
+ if isinstance(value, str):
43
+ text = value.strip().lower()
44
+ if text in ("", "0", "false", "no", "off"):
45
+ return False
46
+ if text in ("1", "true", "yes", "on"):
47
+ return True
48
+ raise ValueError(f"Cannot convert string {value!r} to bool")
49
+ raise TypeError(f"Unsupported type for to_bool: {type(value)!r}")
50
+
51
+
52
+ def normalize_offset(value: NumberLike) -> int:
53
+ return to_int(value)
54
+
55
+
56
+ def normalize_address(value: NumberLike) -> int:
57
+ addr = to_int(value)
58
+ if addr < 0:
59
+ raise ValueError("Address must be non-negative")
60
+ hex_str = hex(addr)
61
+ return int(hex_str, 16)
62
+
63
+
64
+ def to_hex(value: NumberLike) -> str:
65
+ return hex(to_int(value))
66
+
67
+
68
+ def ensure_positive_int(value: NumberLike, *, allow_zero: bool = False) -> int:
69
+ iv = to_int(value)
70
+ if allow_zero:
71
+ if iv < 0:
72
+ raise ValueError(f"Value must be >= 0, got {iv}")
73
+ else:
74
+ if iv <= 0:
75
+ raise ValueError(f"Value must be > 0, got {iv}")
76
+ return iv
77
+
78
+
79
+ def ensure_in_range(
80
+ value: NumberLike,
81
+ *,
82
+ min_value: Optional[int] = None,
83
+ max_value: Optional[int] = None,
84
+ ) -> int:
85
+ iv = to_int(value)
86
+ if min_value is not None and iv < min_value:
87
+ raise ValueError(f"Value must be >= {min_value}, got {iv}")
88
+ if max_value is not None and iv > max_value:
89
+ raise ValueError(f"Value must be <= {max_value}, got {iv}")
90
+ return iv
91
+
92
+
93
+ def ensure_non_empty_string(value: Any) -> str:
94
+ if not isinstance(value, str):
95
+ raise TypeError(f"Expected non-empty string, got {type(value)!r}")
96
+ text = value.strip()
97
+ if not text:
98
+ raise ValueError("String must be non-empty")
99
+ return text
100
+
101
+
102
+ def ensure_iterable_not_empty(value: Iterable[Any], *, name: str = "value") -> Iterable[Any]:
103
+ try:
104
+ if len(value) == 0: # type: ignore[arg-type]
105
+ raise ValueError(f"{name} must not be empty")
106
+ return value
107
+ except TypeError:
108
+ items = list(value)
109
+ if not items:
110
+ raise ValueError(f"{name} must not be empty")
111
+ return items
112
+
113
+
114
+ def ensure_mapping_has_keys(
115
+ mapping: Mapping[str, Any],
116
+ required_keys: Iterable[str],
117
+ *,
118
+ name: str = "mapping",
119
+ ) -> Mapping[str, Any]:
120
+ """
121
+ Проверка, что в словаре есть все требуемые ключи.
122
+ Удобно для настройки/конфигов.
123
+ """
124
+ missing = [k for k in required_keys if k not in mapping]
125
+ if missing:
126
+ raise KeyError(f"{name} is missing required keys: {', '.join(missing)}")
127
+ return mapping
128
+
129
+
130
+ __all__ = [
131
+ "to_int",
132
+ "to_float",
133
+ "to_bool",
134
+ "normalize_offset",
135
+ "normalize_address",
136
+ "to_hex",
137
+ "ensure_positive_int",
138
+ "ensure_in_range",
139
+ "ensure_non_empty_string",
140
+ "ensure_iterable_not_empty",
141
+ "ensure_mapping_has_keys",
142
+ ]
143
+
@@ -0,0 +1,78 @@
1
+ Metadata-Version: 2.4
2
+ Name: ExternalAPI
3
+ Version: 0.1.0
4
+ Summary: ExternalAPI
5
+ Author: Eclipse
6
+ License: Copyright (c) 2026 Eclipse Hack
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
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Dynamic: license-file
17
+
18
+
19
+
20
+ ---
21
+
22
+ # ExternalAPI Eclipse 🌑
23
+
24
+ **CS2 Eclipse** is a high-performance External Memory Interface (EMI) designed for Counter-Strike 2. This framework provides a clean abstraction layer over Source 2 engine offsets, enabling seamless interaction with game memory for research and development purposes.
25
+
26
+ ---
27
+
28
+ ## 🌑 Installation & Dependencies
29
+
30
+ The project relies on low-level libraries to handle process memory and Windows API calls. To set up your environment, ensure you have Python installed and run the following command:
31
+
32
+ ```bash
33
+ python -m pip install -r requirements.txt
34
+
35
+ ```
36
+
37
+ > **Core Dependencies:** `Pymem`, `pywin32`.
38
+
39
+ ---
40
+
41
+ ## 🌑 Technical Specifications (Core Offsets)
42
+
43
+ The API is architected to support all critical memory addresses, categorized by their function within the game engine:
44
+
45
+ ### 1. Global & Engine Access
46
+
47
+ Primary entry points within `client.dll`:
48
+
49
+ * **`dwEntitySystem` / `dwEntityList**` — Global entity registry management.
50
+ * **`dwLocalPlayerController`** — Local player metadata and network state.
51
+ * **`dwViewMatrix`** — $4 \times 4$ transformation matrix for World-to-Screen calculations.
52
+ * **`dwGameRules`** — Internal match state and round parameters.
53
+
54
+ ### 2. Player Pawn & Data Members
55
+
56
+ Specific offsets for real-time entity state monitoring:
57
+
58
+ * **Combat:** `m_iHealth`, `m_ArmorValue`, `m_iShotsFired`, `m_aimPunchAngle`.
59
+ * **Movement:** `m_vecVelocity`, `m_fFlags`, `m_vOldOrigin`.
60
+ * **Status:** `m_bIsScoped`, `m_bIsDefusing`, `m_flFlashDuration`, `m_lifeState`.
61
+
62
+ ### 3. World & Objects
63
+
64
+ Environment-specific offsets:
65
+
66
+ * **C4 Dynamics:** `dwPlantedC4`, `m_flC4Blow`, `m_bBombPlanted`.
67
+ * **Equipment:** `m_pClippingWeapon`, `m_iItemDefinitionIndex`, `m_iClip1`.
68
+
69
+ ---
70
+
71
+ ## ⚠️ Disclaimer
72
+
73
+ **Educational Purpose Only.** This framework is intended for reverse engineering and software architecture research.
74
+
75
+ * The developer is not responsible for any account restrictions or bans.
76
+ * Usage on VAC-secured (Valve Anti-Cheat) servers is strictly discouraged.
77
+
78
+ ---
@@ -0,0 +1,20 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ ExternalAPI/__init__.py
5
+ ExternalAPI.egg-info/PKG-INFO
6
+ ExternalAPI.egg-info/SOURCES.txt
7
+ ExternalAPI.egg-info/dependency_links.txt
8
+ ExternalAPI.egg-info/top_level.txt
9
+ ExternalAPI/include/__init__.py
10
+ ExternalAPI/include/validation.py
11
+ ExternalAPI/include/Api/__init__.py
12
+ ExternalAPI/include/Api/manager.py
13
+ ExternalAPI/include/Api/parser.py
14
+ ExternalAPI/include/core/__init__.py
15
+ ExternalAPI/include/core/edit.py
16
+ ExternalAPI/include/core/get.py
17
+ ExternalAPI/include/core/inject.py
18
+ ExternalAPI/include/core/manager.py
19
+ ExternalAPI/include/core/memory.py
20
+ ExternalAPI/include/core/pars.py
@@ -0,0 +1,2 @@
1
+ ExternalAPI
2
+ dist
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2026 Eclipse Hack
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,78 @@
1
+ Metadata-Version: 2.4
2
+ Name: ExternalAPI
3
+ Version: 0.1.0
4
+ Summary: ExternalAPI
5
+ Author: Eclipse
6
+ License: Copyright (c) 2026 Eclipse Hack
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
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Dynamic: license-file
17
+
18
+
19
+
20
+ ---
21
+
22
+ # ExternalAPI Eclipse 🌑
23
+
24
+ **CS2 Eclipse** is a high-performance External Memory Interface (EMI) designed for Counter-Strike 2. This framework provides a clean abstraction layer over Source 2 engine offsets, enabling seamless interaction with game memory for research and development purposes.
25
+
26
+ ---
27
+
28
+ ## 🌑 Installation & Dependencies
29
+
30
+ The project relies on low-level libraries to handle process memory and Windows API calls. To set up your environment, ensure you have Python installed and run the following command:
31
+
32
+ ```bash
33
+ python -m pip install -r requirements.txt
34
+
35
+ ```
36
+
37
+ > **Core Dependencies:** `Pymem`, `pywin32`.
38
+
39
+ ---
40
+
41
+ ## 🌑 Technical Specifications (Core Offsets)
42
+
43
+ The API is architected to support all critical memory addresses, categorized by their function within the game engine:
44
+
45
+ ### 1. Global & Engine Access
46
+
47
+ Primary entry points within `client.dll`:
48
+
49
+ * **`dwEntitySystem` / `dwEntityList**` — Global entity registry management.
50
+ * **`dwLocalPlayerController`** — Local player metadata and network state.
51
+ * **`dwViewMatrix`** — $4 \times 4$ transformation matrix for World-to-Screen calculations.
52
+ * **`dwGameRules`** — Internal match state and round parameters.
53
+
54
+ ### 2. Player Pawn & Data Members
55
+
56
+ Specific offsets for real-time entity state monitoring:
57
+
58
+ * **Combat:** `m_iHealth`, `m_ArmorValue`, `m_iShotsFired`, `m_aimPunchAngle`.
59
+ * **Movement:** `m_vecVelocity`, `m_fFlags`, `m_vOldOrigin`.
60
+ * **Status:** `m_bIsScoped`, `m_bIsDefusing`, `m_flFlashDuration`, `m_lifeState`.
61
+
62
+ ### 3. World & Objects
63
+
64
+ Environment-specific offsets:
65
+
66
+ * **C4 Dynamics:** `dwPlantedC4`, `m_flC4Blow`, `m_bBombPlanted`.
67
+ * **Equipment:** `m_pClippingWeapon`, `m_iItemDefinitionIndex`, `m_iClip1`.
68
+
69
+ ---
70
+
71
+ ## ⚠️ Disclaimer
72
+
73
+ **Educational Purpose Only.** This framework is intended for reverse engineering and software architecture research.
74
+
75
+ * The developer is not responsible for any account restrictions or bans.
76
+ * Usage on VAC-secured (Valve Anti-Cheat) servers is strictly discouraged.
77
+
78
+ ---
@@ -0,0 +1,61 @@
1
+
2
+
3
+ ---
4
+
5
+ # ExternalAPI Eclipse 🌑
6
+
7
+ **CS2 Eclipse** is a high-performance External Memory Interface (EMI) designed for Counter-Strike 2. This framework provides a clean abstraction layer over Source 2 engine offsets, enabling seamless interaction with game memory for research and development purposes.
8
+
9
+ ---
10
+
11
+ ## 🌑 Installation & Dependencies
12
+
13
+ The project relies on low-level libraries to handle process memory and Windows API calls. To set up your environment, ensure you have Python installed and run the following command:
14
+
15
+ ```bash
16
+ python -m pip install -r requirements.txt
17
+
18
+ ```
19
+
20
+ > **Core Dependencies:** `Pymem`, `pywin32`.
21
+
22
+ ---
23
+
24
+ ## 🌑 Technical Specifications (Core Offsets)
25
+
26
+ The API is architected to support all critical memory addresses, categorized by their function within the game engine:
27
+
28
+ ### 1. Global & Engine Access
29
+
30
+ Primary entry points within `client.dll`:
31
+
32
+ * **`dwEntitySystem` / `dwEntityList**` — Global entity registry management.
33
+ * **`dwLocalPlayerController`** — Local player metadata and network state.
34
+ * **`dwViewMatrix`** — $4 \times 4$ transformation matrix for World-to-Screen calculations.
35
+ * **`dwGameRules`** — Internal match state and round parameters.
36
+
37
+ ### 2. Player Pawn & Data Members
38
+
39
+ Specific offsets for real-time entity state monitoring:
40
+
41
+ * **Combat:** `m_iHealth`, `m_ArmorValue`, `m_iShotsFired`, `m_aimPunchAngle`.
42
+ * **Movement:** `m_vecVelocity`, `m_fFlags`, `m_vOldOrigin`.
43
+ * **Status:** `m_bIsScoped`, `m_bIsDefusing`, `m_flFlashDuration`, `m_lifeState`.
44
+
45
+ ### 3. World & Objects
46
+
47
+ Environment-specific offsets:
48
+
49
+ * **C4 Dynamics:** `dwPlantedC4`, `m_flC4Blow`, `m_bBombPlanted`.
50
+ * **Equipment:** `m_pClippingWeapon`, `m_iItemDefinitionIndex`, `m_iClip1`.
51
+
52
+ ---
53
+
54
+ ## ⚠️ Disclaimer
55
+
56
+ **Educational Purpose Only.** This framework is intended for reverse engineering and software architecture research.
57
+
58
+ * The developer is not responsible for any account restrictions or bans.
59
+ * Usage on VAC-secured (Valve Anti-Cheat) servers is strictly discouraged.
60
+
61
+ ---
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ExternalAPI"
7
+ version = "0.1.0"
8
+ description = "ExternalAPI"
9
+ readme = { file = "README.md", content-type = "text/markdown" }
10
+ requires-python = ">=3.10"
11
+ license = { file = "LICENSE" }
12
+ authors = [{ name = "Eclipse" }]
13
+ dynamic = ["dependencies"]
14
+
15
+ [tool.setuptools]
16
+ include-package-data = true
17
+
18
+ [tool.setuptools.dynamic]
19
+ dependencies = { file = ["requirements.txt"] }
20
+
21
+ [tool.setuptools.packages.find]
22
+ exclude = ["tests*", "docs*", "examples*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+