aether-engine 1.1.0__tar.gz → 1.3.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.
Files changed (107) hide show
  1. aether_engine-1.3.0/PKG-INFO +129 -0
  2. aether_engine-1.3.0/README.md +81 -0
  3. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/__init__.py +19 -2
  4. aether_engine-1.3.0/aether/audio/sfx.py +85 -0
  5. aether_engine-1.3.0/aether/core/_identity.py +67 -0
  6. aether_engine-1.3.0/aether/core/boot_splash.py +141 -0
  7. aether_engine-1.3.0/aether/core/branding_guard.py +103 -0
  8. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/core/engine.py +66 -6
  9. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics2d/__init__.py +4 -3
  10. aether_engine-1.3.0/aether/physics/raycast.py +102 -0
  11. aether_engine-1.3.0/aether/resources/save_load.py +20 -0
  12. aether_engine-1.3.0/aether/ui/__init__.py +5 -0
  13. aether_engine-1.3.0/aether/ui/hud.py +65 -0
  14. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/ui/text.py +36 -29
  15. aether_engine-1.3.0/aether/utils/fonts.py +56 -0
  16. aether_engine-1.3.0/aether_engine.egg-info/PKG-INFO +129 -0
  17. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether_engine.egg-info/SOURCES.txt +7 -0
  18. {aether_engine-1.1.0 → aether_engine-1.3.0}/setup.py +8 -2
  19. aether_engine-1.1.0/PKG-INFO +0 -155
  20. aether_engine-1.1.0/README.md +0 -107
  21. aether_engine-1.1.0/aether/physics/raycast.py +0 -25
  22. aether_engine-1.1.0/aether/utils/__init__.py +0 -0
  23. aether_engine-1.1.0/aether_engine.egg-info/PKG-INFO +0 -155
  24. {aether_engine-1.1.0 → aether_engine-1.3.0}/LICENSE +0 -0
  25. {aether_engine-1.1.0 → aether_engine-1.3.0}/MANIFEST.in +0 -0
  26. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/ai/behavior_tree.py +0 -0
  27. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/ai/navmesh.py +0 -0
  28. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/animation/skeleton.py +0 -0
  29. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/assets/asset_pipeline.py +0 -0
  30. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/audio/__init__.py +0 -0
  31. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/audio/audio_manager.py +0 -0
  32. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/audio/music.py +0 -0
  33. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/audio/sound.py +0 -0
  34. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/core/__init__.py +0 -0
  35. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/core/achievement_system.py +0 -0
  36. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/core/application.py +0 -0
  37. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/core/config.py +0 -0
  38. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/core/console.py +0 -0
  39. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/core/event_bus.py +0 -0
  40. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/core/input.py +0 -0
  41. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/core/plugin_system.py +0 -0
  42. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/core/save_system.py +0 -0
  43. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/core/time_manager.py +0 -0
  44. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/core/window.py +0 -0
  45. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/editor/__init__.py +0 -0
  46. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/editor/__main__.py +0 -0
  47. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/editor/gizmo.py +0 -0
  48. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/editor/imgui_integration.py +0 -0
  49. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/editor/model_editor.py +0 -0
  50. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/environment/environment_system.py +0 -0
  51. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/__init__.py +0 -0
  52. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/camera.py +0 -0
  53. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/first_person.py +0 -0
  54. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/framebuffer.py +0 -0
  55. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/light.py +0 -0
  56. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/lod_system.py +0 -0
  57. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/material.py +0 -0
  58. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/mesh.py +0 -0
  59. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/mesh_factory.py +0 -0
  60. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/particle_system.py +0 -0
  61. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/post_process.py +0 -0
  62. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/renderer.py +0 -0
  63. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/shader.py +0 -0
  64. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/shader_library.py +0 -0
  65. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/sky_atmosphere.py +0 -0
  66. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/terrain.py +0 -0
  67. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/texture.py +0 -0
  68. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/graphics/water.py +0 -0
  69. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/network/network_manager.py +0 -0
  70. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/physics/__init__.py +0 -0
  71. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/physics/bullet_backend.py +0 -0
  72. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/physics/collider.py +0 -0
  73. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/physics/physics2d.py +0 -0
  74. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/physics/physics3d.py +0 -0
  75. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/physics/physics_world.py +0 -0
  76. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/physics/rigidbody.py +0 -0
  77. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/platform/steam_api.py +0 -0
  78. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/resources/__init__.py +0 -0
  79. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/resources/asset_database.py +0 -0
  80. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/resources/loaders/model_loader.py +0 -0
  81. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/resources/loaders/obj_loader.py +0 -0
  82. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/resources/loaders/texture_loader.py +0 -0
  83. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/resources/resource_manager.py +0 -0
  84. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/resources/serialization.py +0 -0
  85. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/scene/__init__.py +0 -0
  86. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/scene/component.py +0 -0
  87. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/scene/entity.py +0 -0
  88. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/scene/scene.py +0 -0
  89. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/scene/scene_graph.py +0 -0
  90. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/scene/scene_manager.py +0 -0
  91. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/ui/button.py +0 -0
  92. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/ui/canvas.py +0 -0
  93. {aether_engine-1.1.0/aether/ui → aether_engine-1.3.0/aether/utils}/__init__.py +0 -0
  94. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/utils/logger.py +0 -0
  95. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/utils/math_utils.py +0 -0
  96. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/utils/object_pool.py +0 -0
  97. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether/utils/profiler.py +0 -0
  98. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether_engine.egg-info/dependency_links.txt +0 -0
  99. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether_engine.egg-info/entry_points.txt +0 -0
  100. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether_engine.egg-info/requires.txt +3 -3
  101. {aether_engine-1.1.0 → aether_engine-1.3.0}/aether_engine.egg-info/top_level.txt +0 -0
  102. {aether_engine-1.1.0 → aether_engine-1.3.0}/examples/01_hello_cube.py +0 -0
  103. {aether_engine-1.1.0 → aether_engine-1.3.0}/examples/02_sphere_scene.py +0 -0
  104. {aether_engine-1.1.0 → aether_engine-1.3.0}/examples/03_first_person.py +0 -0
  105. {aether_engine-1.1.0 → aether_engine-1.3.0}/examples/04_physics2d.py +0 -0
  106. {aether_engine-1.1.0 → aether_engine-1.3.0}/pyproject.toml +0 -0
  107. {aether_engine-1.1.0 → aether_engine-1.3.0}/setup.cfg +0 -0
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: aether-engine
3
+ Version: 1.3.0
4
+ Summary: 3D/2D game engine by entiti937 — OpenGL, physics, FPS, mandatory branding splash
5
+ Home-page: https://pypi.org/project/aether-engine/
6
+ Author: entiti937
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Topic :: Games/Entertainment
17
+ Classifier: Topic :: Multimedia :: Graphics :: 3D Rendering
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: numpy>=1.24
22
+ Requires-Dist: glfw>=2.6
23
+ Requires-Dist: PyOpenGL>=3.1.7
24
+ Requires-Dist: Pillow>=10.0
25
+ Provides-Extra: audio
26
+ Requires-Dist: pygame-ce>=2.5; extra == "audio"
27
+ Provides-Extra: physics
28
+ Requires-Dist: pymunk>=6.0; extra == "physics"
29
+ Provides-Extra: models
30
+ Requires-Dist: trimesh>=3.21; extra == "models"
31
+ Provides-Extra: full
32
+ Requires-Dist: trimesh>=3.21; extra == "full"
33
+ Requires-Dist: freetype-py>=2.4; extra == "full"
34
+ Requires-Dist: pygame-ce>=2.5; extra == "full"
35
+ Requires-Dist: pyyaml>=6.0; extra == "full"
36
+ Requires-Dist: pymunk>=6.0; extra == "full"
37
+ Requires-Dist: imageio>=2.31; extra == "full"
38
+ Dynamic: author
39
+ Dynamic: classifier
40
+ Dynamic: description
41
+ Dynamic: description-content-type
42
+ Dynamic: home-page
43
+ Dynamic: license-file
44
+ Dynamic: provides-extra
45
+ Dynamic: requires-dist
46
+ Dynamic: requires-python
47
+ Dynamic: summary
48
+
49
+ # Aether Engine
50
+
51
+ **3D / 2D игровой движок на Python + OpenGL**
52
+ **Разработчик:** entiti937 · **Версия:** 1.3.0 · **Лицензия:** MIT
53
+ **PyPI:** https://pypi.org/project/aether-engine/
54
+
55
+ > Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md)
56
+
57
+ ---
58
+
59
+ ## Установка
60
+
61
+ ```bash
62
+ pip install -U "aether-engine[full]"
63
+ ```
64
+
65
+ ## Что нового в 1.3.0
66
+
67
+ - **Обязательный splash** при старте любой игры (чёрный экран, белый текст, entiti937)
68
+ - **Водяной знак** в каждом кадре + защита от удаления брендинга
69
+ - **Кириллица** — системные шрифты, UTF-8 консоль, полный Unicode в UI
70
+ - Компиляция защищённых модулей при сборке пакета
71
+
72
+ ## Showcase-игра
73
+
74
+ Minecraft-like sandbox **на этом же движке**:
75
+
76
+ ```bash
77
+ python games/aether_craft.py
78
+ ```
79
+
80
+ Там есть: воксельный мир, вид от 1 лица, ломать/ставить блоки, HUD, звуки, день/ночь, сейвы (F5/F9), урон и респавн.
81
+
82
+ ## Другие запуски
83
+
84
+ ```bash
85
+ python -m aether.editor
86
+ python games/orb_rush.py
87
+ python examples/03_first_person.py
88
+ python examples/04_physics2d.py
89
+ ```
90
+
91
+ ## Минимальный код
92
+
93
+ ```python
94
+ from aether import Engine, Scene, Entity, MeshFactory, Material, Camera
95
+ from aether.scene.component import Transform, MeshRenderer
96
+
97
+ engine = Engine(1280, 720, "Game by you — engine by entiti937")
98
+ scene = Scene("Main")
99
+ camera = Camera(position=(0, 2, 6), yaw=-90)
100
+ scene.set_main_camera(camera)
101
+
102
+ cube = Entity("Cube")
103
+ cube.add_component(Transform(position=(0, 1, 0)))
104
+ mat = Material(); mat.albedo[:] = (0.2, 0.85, 1.0)
105
+ cube.add_component(MeshRenderer(mesh=MeshFactory.create_cube(), material=mat))
106
+ scene.spawn(cube)
107
+
108
+ engine.load_scene(scene)
109
+ engine.enable_first_person(camera)
110
+ engine.run()
111
+ ```
112
+
113
+ ## Возможности 1.2
114
+
115
+ - PBR 3D + sprites 2D + HUD overlay
116
+ - FPS (`enable_first_person`) и free-look камера
117
+ - Физика 3D/2D + raycast
118
+ - `ModelLoader` (OBJ/GLB/glTF/STL/FBX/…)
119
+ - Встроенный voxel model editor
120
+ - Procedural SFX, JSON save/load
121
+ - Примеры и две игры в репозитории
122
+
123
+ ## Документация
124
+
125
+ Все команды, API и туториалы: **[docs/GUIDE.md](docs/GUIDE.md)**
126
+
127
+ ---
128
+
129
+ Aether Engine © **entiti937**
@@ -0,0 +1,81 @@
1
+ # Aether Engine
2
+
3
+ **3D / 2D игровой движок на Python + OpenGL**
4
+ **Разработчик:** entiti937 · **Версия:** 1.3.0 · **Лицензия:** MIT
5
+ **PyPI:** https://pypi.org/project/aether-engine/
6
+
7
+ > Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md)
8
+
9
+ ---
10
+
11
+ ## Установка
12
+
13
+ ```bash
14
+ pip install -U "aether-engine[full]"
15
+ ```
16
+
17
+ ## Что нового в 1.3.0
18
+
19
+ - **Обязательный splash** при старте любой игры (чёрный экран, белый текст, entiti937)
20
+ - **Водяной знак** в каждом кадре + защита от удаления брендинга
21
+ - **Кириллица** — системные шрифты, UTF-8 консоль, полный Unicode в UI
22
+ - Компиляция защищённых модулей при сборке пакета
23
+
24
+ ## Showcase-игра
25
+
26
+ Minecraft-like sandbox **на этом же движке**:
27
+
28
+ ```bash
29
+ python games/aether_craft.py
30
+ ```
31
+
32
+ Там есть: воксельный мир, вид от 1 лица, ломать/ставить блоки, HUD, звуки, день/ночь, сейвы (F5/F9), урон и респавн.
33
+
34
+ ## Другие запуски
35
+
36
+ ```bash
37
+ python -m aether.editor
38
+ python games/orb_rush.py
39
+ python examples/03_first_person.py
40
+ python examples/04_physics2d.py
41
+ ```
42
+
43
+ ## Минимальный код
44
+
45
+ ```python
46
+ from aether import Engine, Scene, Entity, MeshFactory, Material, Camera
47
+ from aether.scene.component import Transform, MeshRenderer
48
+
49
+ engine = Engine(1280, 720, "Game by you — engine by entiti937")
50
+ scene = Scene("Main")
51
+ camera = Camera(position=(0, 2, 6), yaw=-90)
52
+ scene.set_main_camera(camera)
53
+
54
+ cube = Entity("Cube")
55
+ cube.add_component(Transform(position=(0, 1, 0)))
56
+ mat = Material(); mat.albedo[:] = (0.2, 0.85, 1.0)
57
+ cube.add_component(MeshRenderer(mesh=MeshFactory.create_cube(), material=mat))
58
+ scene.spawn(cube)
59
+
60
+ engine.load_scene(scene)
61
+ engine.enable_first_person(camera)
62
+ engine.run()
63
+ ```
64
+
65
+ ## Возможности 1.2
66
+
67
+ - PBR 3D + sprites 2D + HUD overlay
68
+ - FPS (`enable_first_person`) и free-look камера
69
+ - Физика 3D/2D + raycast
70
+ - `ModelLoader` (OBJ/GLB/glTF/STL/FBX/…)
71
+ - Встроенный voxel model editor
72
+ - Procedural SFX, JSON save/load
73
+ - Примеры и две игры в репозитории
74
+
75
+ ## Документация
76
+
77
+ Все команды, API и туториалы: **[docs/GUIDE.md](docs/GUIDE.md)**
78
+
79
+ ---
80
+
81
+ Aether Engine © **entiti937**
@@ -1,4 +1,11 @@
1
- """Aether Engine — 3D / 2D game engine"""
1
+ """Aether Engine — 3D / 2D game engine by entiti937"""
2
+
3
+ from aether.core._identity import verify_token, engine_name, creator
4
+
5
+ if not verify_token():
6
+ raise ImportError(
7
+ f"{engine_name()}: integrity check failed. Reinstall: pip install aether-engine"
8
+ )
2
9
 
3
10
  from aether.core.engine import Engine
4
11
  from aether.scene.scene import Scene
@@ -19,8 +26,12 @@ from aether.resources.loaders.model_loader import ModelLoader
19
26
  from aether.physics import PhysicsWorld2D, PhysicsWorld3D, BoxCollider, SphereCollider
20
27
  from aether.graphics2d import Renderer2D, Sprite, Camera2D
21
28
  from aether.editor import ModelEditor
29
+ from aether.ui.hud import Hud, HudBar
30
+ from aether.audio.sfx import SfxBank
31
+ from aether.resources.save_load import save_json, load_json
32
+ from aether.physics.raycast import Raycaster
22
33
 
23
- __version__ = "1.1.0"
34
+ __version__ = "1.3.0"
24
35
 
25
36
  __all__ = [
26
37
  "Engine",
@@ -45,5 +56,11 @@ __all__ = [
45
56
  "Sprite",
46
57
  "Camera2D",
47
58
  "ModelEditor",
59
+ "Hud",
60
+ "HudBar",
61
+ "SfxBank",
62
+ "save_json",
63
+ "load_json",
64
+ "Raycaster",
48
65
  "__version__",
49
66
  ]
@@ -0,0 +1,85 @@
1
+ """Procedural sound helpers (no asset files required)."""
2
+ from __future__ import annotations
3
+
4
+ import math
5
+ import struct
6
+ import tempfile
7
+ import wave
8
+ from pathlib import Path
9
+ from typing import Optional
10
+
11
+
12
+ def _write_wav(path: Path, samples, rate: int = 22050):
13
+ with wave.open(str(path), "w") as wf:
14
+ wf.setnchannels(1)
15
+ wf.setsampwidth(2)
16
+ wf.setframerate(rate)
17
+ frames = b"".join(struct.pack("<h", int(max(-1, min(1, s)) * 32767)) for s in samples)
18
+ wf.writeframes(frames)
19
+
20
+
21
+ def make_tone(
22
+ frequency: float = 440.0,
23
+ duration: float = 0.08,
24
+ volume: float = 0.35,
25
+ kind: str = "square",
26
+ rate: int = 22050,
27
+ ) -> Path:
28
+ """Generate a short WAV tone and return its path."""
29
+ n = int(rate * duration)
30
+ samples = []
31
+ for i in range(n):
32
+ t = i / rate
33
+ env = 1.0
34
+ if i < rate * 0.01:
35
+ env = i / (rate * 0.01)
36
+ elif i > n - rate * 0.02:
37
+ env = max(0.0, (n - i) / (rate * 0.02))
38
+ phase = 2 * math.pi * frequency * t
39
+ if kind == "sine":
40
+ val = math.sin(phase)
41
+ elif kind == "noise":
42
+ # deterministic pseudo-noise
43
+ val = math.sin(phase * 12.9898) * 2.0 % 2.0 - 1.0
44
+ else:
45
+ val = 1.0 if math.sin(phase) >= 0 else -1.0
46
+ samples.append(val * volume * env)
47
+
48
+ path = Path(tempfile.gettempdir()) / f"aether_tone_{int(frequency)}_{kind}.wav"
49
+ _write_wav(path, samples, rate)
50
+ return path
51
+
52
+
53
+ class SfxBank:
54
+ """Lazy bank of procedural SFX played through Engine.audio."""
55
+
56
+ def __init__(self, audio_manager):
57
+ self.audio = audio_manager
58
+ self._cache = {}
59
+
60
+ def _get(self, key: str, factory):
61
+ if key not in self._cache:
62
+ path = factory()
63
+ # AudioManager.load_sound(name, path)
64
+ self._cache[key] = self.audio.load_sound(key, str(path))
65
+ return self._cache[key]
66
+
67
+ def play_break(self):
68
+ snd = self._get("break", lambda: make_tone(180, 0.07, 0.4, "noise"))
69
+ if snd:
70
+ self.audio.play_sound("break", volume=0.45)
71
+
72
+ def play_place(self):
73
+ snd = self._get("place", lambda: make_tone(520, 0.06, 0.3, "square"))
74
+ if snd:
75
+ self.audio.play_sound("place", volume=0.4)
76
+
77
+ def play_select(self):
78
+ snd = self._get("select", lambda: make_tone(760, 0.04, 0.25, "sine"))
79
+ if snd:
80
+ self.audio.play_sound("select", volume=0.35)
81
+
82
+ def play_hurt(self):
83
+ snd = self._get("hurt", lambda: make_tone(120, 0.12, 0.45, "square"))
84
+ if snd:
85
+ self.audio.play_sound("hurt", volume=0.5)
@@ -0,0 +1,67 @@
1
+ """Encoded engine identity (integrity-protected, UTF-8 XOR)."""
2
+ from __future__ import annotations
3
+
4
+ _K = 0xA7
5
+
6
+ _B = {
7
+ "n": bytes([230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194]),
8
+ "c": bytes([194, 201, 211, 206, 211, 206, 158, 148, 144]),
9
+ "l1": bytes([
10
+ 119, 57, 118, 38, 119, 26, 119, 25, 119, 21, 119, 23, 119, 26, 119, 25,
11
+ 135, 119, 26, 119, 23, 135, 119, 22, 119, 23, 119, 16, 119, 18, 135,
12
+ 230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194,
13
+ ]),
14
+ "l2": bytes([
15
+ 119, 6, 119, 25, 119, 16, 119, 19, 119, 23, 118, 37, 119, 18, 119, 28,
16
+ 118, 43, 157, 135, 194, 201, 211, 206, 211, 206, 158, 148, 144,
17
+ ]),
18
+ "w": bytes([
19
+ 230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194,
20
+ 135, 219, 135, 194, 201, 211, 206, 211, 206, 158, 148, 144,
21
+ ]),
22
+ "sig": bytes([
23
+ 230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194,
24
+ 135, 150, 137, 148, 137, 151,
25
+ ]),
26
+ }
27
+
28
+ _EXPECTED = 0xAC3B2627
29
+
30
+
31
+ def _decode(blob: bytes) -> str:
32
+ return bytes(b ^ _K for b in blob).decode("utf-8")
33
+
34
+
35
+ def engine_name() -> str:
36
+ return _decode(_B["n"])
37
+
38
+
39
+ def creator() -> str:
40
+ return _decode(_B["c"])
41
+
42
+
43
+ def splash_line1() -> str:
44
+ return _decode(_B["l1"])
45
+
46
+
47
+ def splash_line2() -> str:
48
+ return _decode(_B["l2"])
49
+
50
+
51
+ def watermark() -> str:
52
+ return _decode(_B["w"])
53
+
54
+
55
+ def signature() -> str:
56
+ return _decode(_B["sig"])
57
+
58
+
59
+ def verify_token() -> bool:
60
+ try:
61
+ payload = "|".join([engine_name(), creator(), splash_line1(), splash_line2()])
62
+ h = 0
63
+ for ch in payload:
64
+ h = ((h * 131) + ord(ch)) & 0xFFFFFFFF
65
+ return h == _EXPECTED
66
+ except Exception:
67
+ return False
@@ -0,0 +1,141 @@
1
+ """Mandatory boot splash — white text on black, shown during engine preload."""
2
+ from __future__ import annotations
3
+
4
+ import time
5
+ from typing import Optional
6
+
7
+ import glfw
8
+ from OpenGL.GL import *
9
+
10
+ from aether.core._identity import creator, engine_name, splash_line1, splash_line2, verify_token
11
+ from aether.utils.fonts import resolve_system_font
12
+
13
+
14
+ class BootSplash:
15
+ """Pre-load screen integrated into Engine startup (cannot be skipped)."""
16
+
17
+ MIN_SHOW_SEC = 2.2
18
+
19
+ def __init__(self, window):
20
+ self.window = window
21
+ self.width = window.width
22
+ self.height = window.height
23
+ self._text = None
24
+ self._font = None
25
+ self._started = time.monotonic()
26
+ self._progress = 0.0
27
+ self._ready = False
28
+ self._init_renderer()
29
+ self.draw(0.0, "Загрузка...")
30
+
31
+ def _init_renderer(self):
32
+ try:
33
+ from aether.ui.text import Font, TextRenderer
34
+
35
+ self._text = TextRenderer()
36
+ font_path = resolve_system_font()
37
+ if font_path:
38
+ self._font = Font(font_path, 34)
39
+ self._text.default_font = self._font
40
+ else:
41
+ self._font = None
42
+ except Exception as exc:
43
+ print(f"[BootSplash] Font init: {exc}")
44
+ self._text = None
45
+
46
+ def _draw_bar(self, progress: float):
47
+ """Progress bar drawn via text (core-profile safe)."""
48
+ if not self._text or not self._font:
49
+ return
50
+ p = max(0.0, min(1.0, progress))
51
+ filled = int(p * 24)
52
+ bar = "[" + "#" * filled + "-" * (24 - filled) + f"] {int(p * 100)}%"
53
+ tw, _ = self._text.measure_text(bar, 0.75, self._font)
54
+ self._text.render_text(
55
+ bar,
56
+ self.width * 0.5 - tw * 0.5,
57
+ self.height * 0.62,
58
+ scale=0.75,
59
+ color=(0.85, 0.85, 0.85, 1.0),
60
+ font=self._font,
61
+ screen_width=self.width,
62
+ screen_height=self.height,
63
+ )
64
+
65
+ def draw(self, progress: float, status: str = ""):
66
+ if not verify_token():
67
+ raise RuntimeError("Aether Engine identity verification failed")
68
+
69
+ self._progress = max(self._progress, progress)
70
+ glViewport(0, 0, self.width, self.height)
71
+ glClearColor(0.0, 0.0, 0.0, 1.0)
72
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
73
+
74
+ line1 = splash_line1()
75
+ line2 = splash_line2()
76
+
77
+ if self._text and self._font:
78
+ scale = 1.0
79
+ tw1, _ = self._text.measure_text(line1, scale, self._font)
80
+ tw2, th2 = self._text.measure_text(line2, scale * 0.85, self._font)
81
+ cx = self.width * 0.5
82
+ cy = self.height * 0.42
83
+ self._text.render_text(
84
+ line1,
85
+ cx - tw1 * 0.5,
86
+ cy - 40,
87
+ scale=scale,
88
+ color=(1.0, 1.0, 1.0, 1.0),
89
+ font=self._font,
90
+ screen_width=self.width,
91
+ screen_height=self.height,
92
+ )
93
+ self._text.render_text(
94
+ line2,
95
+ cx - tw2 * 0.5,
96
+ cy + 8,
97
+ scale=0.85,
98
+ color=(1.0, 1.0, 1.0, 0.92),
99
+ font=self._font,
100
+ screen_width=self.width,
101
+ screen_height=self.height,
102
+ )
103
+ if status:
104
+ ts, _ = self._text.measure_text(status, 0.7, self._font)
105
+ self._text.render_text(
106
+ status,
107
+ cx - ts * 0.5,
108
+ cy + th2 + 36,
109
+ scale=0.7,
110
+ color=(0.75, 0.75, 0.75, 1.0),
111
+ font=self._font,
112
+ screen_width=self.width,
113
+ screen_height=self.height,
114
+ )
115
+ else:
116
+ # ASCII fallback if font missing
117
+ print(f"[BootSplash] {line1}")
118
+ print(f"[BootSplash] {line2}")
119
+ if status:
120
+ print(f"[BootSplash] {status}")
121
+
122
+ self._draw_bar(self._progress)
123
+ self.window.swap_buffers()
124
+ glfw.poll_events()
125
+
126
+ def step(self, progress: float, status: str):
127
+ self.draw(progress, status)
128
+ time.sleep(0.02)
129
+
130
+ def finish(self):
131
+ elapsed = time.monotonic() - self._started
132
+ if elapsed < self.MIN_SHOW_SEC:
133
+ remaining = self.MIN_SHOW_SEC - elapsed
134
+ steps = max(1, int(remaining / 0.05))
135
+ for i in range(steps):
136
+ p = self._progress + (1.0 - self._progress) * ((i + 1) / steps)
137
+ self.draw(p, "Готово")
138
+ time.sleep(remaining / steps)
139
+ else:
140
+ self.draw(1.0, "Готово")
141
+ self._ready = True
@@ -0,0 +1,103 @@
1
+ """Runtime branding guard — watermark + integrity checks (mandatory)."""
2
+ from __future__ import annotations
3
+
4
+ from aether.core._identity import creator, engine_name, verify_token, watermark
5
+
6
+
7
+ class BrandingGuard:
8
+ """Ensures engine attribution is always visible."""
9
+
10
+ _instances = 0
11
+ _tamper_count = 0
12
+
13
+ def __init__(self, engine):
14
+ self.engine = engine
15
+ self._frame = 0
16
+ self._font = None
17
+ self._text = None
18
+ self._force_overlay = False
19
+ BrandingGuard._instances += 1
20
+ self._init_text()
21
+
22
+ def _init_text(self):
23
+ try:
24
+ from aether.ui.text import Font, TextRenderer
25
+ from aether.utils.fonts import resolve_system_font
26
+
27
+ path = resolve_system_font()
28
+ if path:
29
+ self._text = TextRenderer()
30
+ self._font = Font(path, 16)
31
+ self._text.default_font = self._font
32
+ except Exception:
33
+ pass
34
+
35
+ @classmethod
36
+ def assert_integrity(cls, engine) -> bool:
37
+ if not verify_token():
38
+ cls._tamper_count += 1
39
+ return False
40
+ if getattr(engine, "_brand_bypass", False):
41
+ cls._tamper_count += 1
42
+ engine._brand_bypass = False
43
+ return False
44
+ return True
45
+
46
+ def draw_watermark(self):
47
+ """Subtle persistent watermark (bottom-right)."""
48
+ if not BrandingGuard.assert_integrity(self.engine):
49
+ self._force_overlay = True
50
+
51
+ if not self._text or not self._font:
52
+ return
53
+
54
+ from OpenGL.GL import glDisable, glEnable, GL_DEPTH_TEST
55
+
56
+ w = self.engine.config.graphics.width
57
+ h = self.engine.config.graphics.height
58
+ label = watermark()
59
+ tw, _ = self._text.measure_text(label, 1.0, self._font)
60
+ alpha = 0.55 if not self._force_overlay else 0.95
61
+ self._text.render_text(
62
+ label,
63
+ w - tw - 12,
64
+ h - 22,
65
+ scale=1.0,
66
+ color=(1.0, 1.0, 1.0, alpha),
67
+ font=self._font,
68
+ screen_width=w,
69
+ screen_height=h,
70
+ )
71
+
72
+ if self._force_overlay:
73
+ msg1 = f"Основано на базе {engine_name()}"
74
+ msg2 = f"Создатель: {creator()}"
75
+ self._text.render_text(
76
+ msg1,
77
+ w * 0.5 - 180,
78
+ h * 0.5 - 20,
79
+ scale=1.2,
80
+ color=(1, 1, 1, 0.9),
81
+ font=self._font,
82
+ screen_width=w,
83
+ screen_height=h,
84
+ )
85
+ self._text.render_text(
86
+ msg2,
87
+ w * 0.5 - 120,
88
+ h * 0.5 + 18,
89
+ scale=1.0,
90
+ color=(1, 1, 1, 0.85),
91
+ font=self._font,
92
+ screen_width=w,
93
+ screen_height=h,
94
+ )
95
+
96
+ glDisable(GL_DEPTH_TEST)
97
+ glEnable(GL_DEPTH_TEST)
98
+
99
+ def tick(self):
100
+ self._frame += 1
101
+ if self._frame % 90 == 0:
102
+ if not verify_token():
103
+ self._force_overlay = True