aether-engine 1.4.0__tar.gz → 1.7.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 (128) hide show
  1. {aether_engine-1.4.0 → aether_engine-1.7.0}/MANIFEST.in +1 -0
  2. {aether_engine-1.4.0/aether_engine.egg-info → aether_engine-1.7.0}/PKG-INFO +16 -17
  3. {aether_engine-1.4.0 → aether_engine-1.7.0}/README.md +11 -12
  4. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/__init__.py +9 -3
  5. aether_engine-1.7.0/aether/accelerators/__init__.py +30 -0
  6. aether_engine-1.7.0/aether/accelerators/gameplay.py +54 -0
  7. aether_engine-1.4.0/aether/graphics/voxel_mesh.py → aether_engine-1.7.0/aether/accelerators/voxel.py +101 -47
  8. aether_engine-1.7.0/aether/assets/__pycache__/asset_pipeline.cpython-314.pyc +0 -0
  9. aether_engine-1.7.0/aether/assets/fonts/AetherSans.ttf +0 -0
  10. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/cli.py +35 -13
  11. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/_identity.py +6 -7
  12. aether_engine-1.7.0/aether/core/boot_splash.py +208 -0
  13. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/engine.py +13 -17
  14. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/input.py +5 -0
  15. aether_engine-1.7.0/aether/editor/__init__.py +3 -0
  16. aether_engine-1.7.0/aether/graphics/voxel_mesh.py +4 -0
  17. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics2d/__init__.py +7 -0
  18. aether_engine-1.7.0/aether/resources/asset_loader.py +143 -0
  19. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/loaders/model_loader.py +14 -0
  20. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/scene/component.py +8 -0
  21. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/utils/fonts.py +8 -0
  22. aether_engine-1.7.0/aether/web/__init__.py +4 -0
  23. aether_engine-1.7.0/aether/web/bot_detect.py +53 -0
  24. aether_engine-1.7.0/aether/web/config.py +61 -0
  25. aether_engine-1.7.0/aether/web/content.py +427 -0
  26. aether_engine-1.7.0/aether/web/notifications.py +43 -0
  27. aether_engine-1.7.0/aether/web/plain.py +57 -0
  28. aether_engine-1.7.0/aether/web/plain_content.py +166 -0
  29. aether_engine-1.7.0/aether/web/registry.py +42 -0
  30. aether_engine-1.7.0/aether/web/secrets.py +29 -0
  31. aether_engine-1.7.0/aether/web/server.py +165 -0
  32. aether_engine-1.7.0/aether/web/templates.py +198 -0
  33. aether_engine-1.7.0/aether/web/urls.py +20 -0
  34. {aether_engine-1.4.0 → aether_engine-1.7.0/aether_engine.egg-info}/PKG-INFO +16 -17
  35. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether_engine.egg-info/SOURCES.txt +18 -4
  36. aether_engine-1.7.0/aether_engine.egg-info/entry_points.txt +2 -0
  37. {aether_engine-1.4.0 → aether_engine-1.7.0}/examples/03_first_person.py +1 -1
  38. {aether_engine-1.4.0 → aether_engine-1.7.0}/setup.py +1 -2
  39. aether_engine-1.4.0/aether/core/boot_splash.py +0 -217
  40. aether_engine-1.4.0/aether/editor/__init__.py +0 -5
  41. aether_engine-1.4.0/aether/editor/__main__.py +0 -6
  42. aether_engine-1.4.0/aether/editor/gizmo.py +0 -346
  43. aether_engine-1.4.0/aether/editor/imgui_integration.py +0 -468
  44. aether_engine-1.4.0/aether/editor/model_editor.py +0 -253
  45. aether_engine-1.4.0/aether_engine.egg-info/entry_points.txt +0 -3
  46. {aether_engine-1.4.0 → aether_engine-1.7.0}/LICENSE +0 -0
  47. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/__main__.py +0 -0
  48. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ai/behavior_tree.py +0 -0
  49. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ai/navmesh.py +0 -0
  50. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/animation/skeleton.py +0 -0
  51. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/assets/asset_pipeline.py +0 -0
  52. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/audio/__init__.py +0 -0
  53. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/audio/audio_manager.py +0 -0
  54. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/audio/music.py +0 -0
  55. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/audio/sfx.py +0 -0
  56. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/audio/sound.py +0 -0
  57. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/__init__.py +0 -0
  58. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/achievement_system.py +0 -0
  59. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/application.py +0 -0
  60. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/branding_guard.py +0 -0
  61. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/config.py +0 -0
  62. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/console.py +0 -0
  63. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/event_bus.py +0 -0
  64. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/plugin_system.py +0 -0
  65. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/save_system.py +0 -0
  66. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/time_manager.py +0 -0
  67. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/core/window.py +0 -0
  68. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/environment/environment_system.py +0 -0
  69. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/__init__.py +0 -0
  70. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/camera.py +0 -0
  71. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/first_person.py +0 -0
  72. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/framebuffer.py +0 -0
  73. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/light.py +0 -0
  74. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/lod_system.py +0 -0
  75. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/material.py +0 -0
  76. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/mesh.py +0 -0
  77. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/mesh_factory.py +0 -0
  78. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/particle_system.py +0 -0
  79. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/post_process.py +0 -0
  80. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/renderer.py +0 -0
  81. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/shader.py +0 -0
  82. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/shader_library.py +0 -0
  83. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/sky_atmosphere.py +0 -0
  84. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/terrain.py +0 -0
  85. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/texture.py +0 -0
  86. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/graphics/water.py +0 -0
  87. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/network/network_manager.py +0 -0
  88. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/__init__.py +0 -0
  89. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/bullet_backend.py +0 -0
  90. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/collider.py +0 -0
  91. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/physics2d.py +0 -0
  92. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/physics3d.py +0 -0
  93. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/physics_world.py +0 -0
  94. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/raycast.py +0 -0
  95. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/physics/rigidbody.py +0 -0
  96. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/platform/steam_api.py +0 -0
  97. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/__init__.py +0 -0
  98. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/asset_database.py +0 -0
  99. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/loaders/obj_loader.py +0 -0
  100. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/loaders/texture_loader.py +0 -0
  101. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/resource_manager.py +0 -0
  102. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/save_load.py +0 -0
  103. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/resources/serialization.py +0 -0
  104. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/scene/__init__.py +0 -0
  105. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/scene/entity.py +0 -0
  106. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/scene/scene.py +0 -0
  107. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/scene/scene_graph.py +0 -0
  108. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/scene/scene_manager.py +0 -0
  109. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/tools/__init__.py +0 -0
  110. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/tools/packager.py +0 -0
  111. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ui/__init__.py +0 -0
  112. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ui/button.py +0 -0
  113. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ui/canvas.py +0 -0
  114. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ui/hud.py +0 -0
  115. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/ui/text.py +0 -0
  116. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/utils/__init__.py +0 -0
  117. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/utils/logger.py +0 -0
  118. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/utils/math_utils.py +0 -0
  119. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/utils/object_pool.py +0 -0
  120. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether/utils/profiler.py +0 -0
  121. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether_engine.egg-info/dependency_links.txt +0 -0
  122. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether_engine.egg-info/requires.txt +4 -4
  123. {aether_engine-1.4.0 → aether_engine-1.7.0}/aether_engine.egg-info/top_level.txt +0 -0
  124. {aether_engine-1.4.0 → aether_engine-1.7.0}/examples/01_hello_cube.py +0 -0
  125. {aether_engine-1.4.0 → aether_engine-1.7.0}/examples/02_sphere_scene.py +0 -0
  126. {aether_engine-1.4.0 → aether_engine-1.7.0}/examples/04_physics2d.py +0 -0
  127. {aether_engine-1.4.0 → aether_engine-1.7.0}/pyproject.toml +0 -0
  128. {aether_engine-1.4.0 → aether_engine-1.7.0}/setup.cfg +0 -0
@@ -7,6 +7,7 @@ recursive-include aether *.glsl
7
7
  recursive-include aether *.vert
8
8
  recursive-include aether *.frag
9
9
  recursive-include aether *.json
10
+ recursive-include aether/assets *
10
11
 
11
12
  recursive-include examples *.py
12
13
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aether-engine
3
- Version: 1.4.0
3
+ Version: 1.7.0
4
4
  Summary: 3D/2D game engine by entiti937 — OpenGL, physics, FPS, mandatory branding splash
5
5
  Home-page: https://pypi.org/project/aether-engine/
6
6
  Author: entiti937
@@ -31,13 +31,13 @@ Requires-Dist: trimesh>=3.21; extra == "models"
31
31
  Provides-Extra: build
32
32
  Requires-Dist: pyinstaller>=6.0; extra == "build"
33
33
  Provides-Extra: full
34
- Requires-Dist: pymunk>=6.0; extra == "full"
35
- Requires-Dist: pygame-ce>=2.5; extra == "full"
34
+ Requires-Dist: freetype-py>=2.4; extra == "full"
36
35
  Requires-Dist: imageio>=2.31; extra == "full"
36
+ Requires-Dist: pygame-ce>=2.5; extra == "full"
37
+ Requires-Dist: pyyaml>=6.0; extra == "full"
37
38
  Requires-Dist: trimesh>=3.21; extra == "full"
38
- Requires-Dist: freetype-py>=2.4; extra == "full"
39
+ Requires-Dist: pymunk>=6.0; extra == "full"
39
40
  Requires-Dist: pyinstaller>=6.0; extra == "full"
40
- Requires-Dist: pyyaml>=6.0; extra == "full"
41
41
  Dynamic: author
42
42
  Dynamic: classifier
43
43
  Dynamic: description
@@ -52,7 +52,7 @@ Dynamic: summary
52
52
  # Aether Engine
53
53
 
54
54
  **3D / 2D игровой движок на Python + OpenGL**
55
- **Разработчик:** entiti937 · **Версия:** 1.4.0 · **Лицензия:** MIT
55
+ **Разработчик:** entiti937 · **Версия:** 1.7.0 · **Лицензия:** MIT
56
56
  **PyPI:** https://pypi.org/project/aether-engine/
57
57
 
58
58
  > Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md)
@@ -65,13 +65,11 @@ Dynamic: summary
65
65
  pip install -U "aether-engine[full]"
66
66
  ```
67
67
 
68
- ## Что нового в 1.4.0
68
+ ## Что нового в 1.7.0
69
69
 
70
- - Splash как в Unreal (fade-in) — **только при загрузке**, не в игре
71
- - `aether help` / `aether build` — справка и сборка в `.exe`
72
- - `ChunkedVoxelMesher` — оптимизация вокселей на уровне движка
73
- - Исправлен текст splash (кириллица через Pillow)
74
- - Убран постоянный водяной знак во время геймплея
70
+ - **AssetLoader** — загрузка любых моделей, спрайтов и текстур (OBJ/GLB/FBX/PNG/WebP/…)
71
+ - `Sprite.from_image(path)` — быстрый импорт 2D-графики
72
+ - Встроенный редактор **убран** (может вернуться в будущих версиях)
75
73
 
76
74
  ## Showcase-игра
77
75
 
@@ -88,10 +86,10 @@ python games/aether_craft.py
88
86
  ## Другие запуски
89
87
 
90
88
  ```bash
91
- python -m aether.editor
92
89
  python games/orb_rush.py
93
90
  python examples/03_first_person.py
94
91
  python examples/04_physics2d.py
92
+ aether website
95
93
  ```
96
94
 
97
95
  ## Минимальный код
@@ -116,15 +114,16 @@ engine.enable_first_person(camera)
116
114
  engine.run()
117
115
  ```
118
116
 
119
- ## Возможности 1.2
117
+ ## Возможности
120
118
 
121
119
  - PBR 3D + sprites 2D + HUD overlay
122
120
  - FPS (`enable_first_person`) и free-look камера
123
121
  - Физика 3D/2D + raycast
124
- - `ModelLoader` (OBJ/GLB/glTF/STL/FBX/…)
125
- - Встроенный voxel model editor
122
+ - `AssetLoader` / `ModelLoader` — OBJ/GLB/glTF/STL/FBX/STEP/… + PNG/WebP спрайты
126
123
  - Procedural SFX, JSON save/load
127
- - Примеры и две игры в репозитории
124
+ - Примеры и игры в репозитории
125
+
126
+ > Встроенный редактор моделей/пиксель-арт **временно убран** — планируется вернуть позже. Сейчас используйте Blender, Aseprite и т.д., файлы подгружайте через `AssetLoader`.
128
127
 
129
128
  ## Документация
130
129
 
@@ -1,7 +1,7 @@
1
1
  # Aether Engine
2
2
 
3
3
  **3D / 2D игровой движок на Python + OpenGL**
4
- **Разработчик:** entiti937 · **Версия:** 1.4.0 · **Лицензия:** MIT
4
+ **Разработчик:** entiti937 · **Версия:** 1.7.0 · **Лицензия:** MIT
5
5
  **PyPI:** https://pypi.org/project/aether-engine/
6
6
 
7
7
  > Полное руководство: [`docs/GUIDE.md`](docs/GUIDE.md)
@@ -14,13 +14,11 @@
14
14
  pip install -U "aether-engine[full]"
15
15
  ```
16
16
 
17
- ## Что нового в 1.4.0
17
+ ## Что нового в 1.7.0
18
18
 
19
- - Splash как в Unreal (fade-in) — **только при загрузке**, не в игре
20
- - `aether help` / `aether build` — справка и сборка в `.exe`
21
- - `ChunkedVoxelMesher` — оптимизация вокселей на уровне движка
22
- - Исправлен текст splash (кириллица через Pillow)
23
- - Убран постоянный водяной знак во время геймплея
19
+ - **AssetLoader** — загрузка любых моделей, спрайтов и текстур (OBJ/GLB/FBX/PNG/WebP/…)
20
+ - `Sprite.from_image(path)` — быстрый импорт 2D-графики
21
+ - Встроенный редактор **убран** (может вернуться в будущих версиях)
24
22
 
25
23
  ## Showcase-игра
26
24
 
@@ -37,10 +35,10 @@ python games/aether_craft.py
37
35
  ## Другие запуски
38
36
 
39
37
  ```bash
40
- python -m aether.editor
41
38
  python games/orb_rush.py
42
39
  python examples/03_first_person.py
43
40
  python examples/04_physics2d.py
41
+ aether website
44
42
  ```
45
43
 
46
44
  ## Минимальный код
@@ -65,15 +63,16 @@ engine.enable_first_person(camera)
65
63
  engine.run()
66
64
  ```
67
65
 
68
- ## Возможности 1.2
66
+ ## Возможности
69
67
 
70
68
  - PBR 3D + sprites 2D + HUD overlay
71
69
  - FPS (`enable_first_person`) и free-look камера
72
70
  - Физика 3D/2D + raycast
73
- - `ModelLoader` (OBJ/GLB/glTF/STL/FBX/…)
74
- - Встроенный voxel model editor
71
+ - `AssetLoader` / `ModelLoader` — OBJ/GLB/glTF/STL/FBX/STEP/… + PNG/WebP спрайты
75
72
  - Procedural SFX, JSON save/load
76
- - Примеры и две игры в репозитории
73
+ - Примеры и игры в репозитории
74
+
75
+ > Встроенный редактор моделей/пиксель-арт **временно убран** — планируется вернуть позже. Сейчас используйте Blender, Aseprite и т.д., файлы подгружайте через `AssetLoader`.
77
76
 
78
77
  ## Документация
79
78
 
@@ -23,17 +23,19 @@ from aether.graphics.material import Material
23
23
  from aether.graphics.camera import Camera
24
24
  from aether.graphics.first_person import FirstPersonController
25
25
  from aether.resources.loaders.model_loader import ModelLoader
26
+ from aether.resources.asset_loader import AssetLoader
26
27
  from aether.physics import PhysicsWorld2D, PhysicsWorld3D, BoxCollider, SphereCollider
27
28
  from aether.graphics2d import Renderer2D, Sprite, Camera2D
28
- from aether.editor import ModelEditor
29
29
  from aether.ui.hud import Hud, HudBar
30
30
  from aether.audio.sfx import SfxBank
31
31
  from aether.resources.save_load import save_json, load_json
32
32
  from aether.physics.raycast import Raycaster
33
33
 
34
- __version__ = "1.4.0"
34
+ __version__ = "1.7.0"
35
35
 
36
36
  from aether.graphics.voxel_mesh import ChunkedVoxelMesher
37
+ from aether.accelerators import is_native_available, backend_name
38
+ from aether.accelerators.gameplay import npc_move_toward, roll_loot
37
39
 
38
40
  __all__ = [
39
41
  "Engine",
@@ -50,6 +52,7 @@ __all__ = [
50
52
  "Camera",
51
53
  "FirstPersonController",
52
54
  "ModelLoader",
55
+ "AssetLoader",
53
56
  "PhysicsWorld2D",
54
57
  "PhysicsWorld3D",
55
58
  "BoxCollider",
@@ -57,7 +60,6 @@ __all__ = [
57
60
  "Renderer2D",
58
61
  "Sprite",
59
62
  "Camera2D",
60
- "ModelEditor",
61
63
  "Hud",
62
64
  "HudBar",
63
65
  "SfxBank",
@@ -65,5 +67,9 @@ __all__ = [
65
67
  "load_json",
66
68
  "Raycaster",
67
69
  "ChunkedVoxelMesher",
70
+ "is_native_available",
71
+ "backend_name",
72
+ "npc_move_toward",
73
+ "roll_loot",
68
74
  "__version__",
69
75
  ]
@@ -0,0 +1,30 @@
1
+ """Rust/C++ accelerators — prebuilt binaries, no user toolchain required."""
2
+ from __future__ import annotations
3
+
4
+ from typing import Optional
5
+
6
+ _NATIVE = None
7
+ _NATIVE_TRIED = False
8
+
9
+
10
+ def is_native_available() -> bool:
11
+ """True when the precompiled Rust extension is loaded."""
12
+ global _NATIVE, _NATIVE_TRIED
13
+ if _NATIVE_TRIED:
14
+ return _NATIVE is not None
15
+ _NATIVE_TRIED = True
16
+ try:
17
+ import aether_native as mod # type: ignore
18
+
19
+ _NATIVE = mod
20
+ except ImportError:
21
+ _NATIVE = None
22
+ return _NATIVE is not None
23
+
24
+
25
+ def native_module():
26
+ return _NATIVE
27
+
28
+
29
+ def backend_name() -> str:
30
+ return "rust" if is_native_available() else "python"
@@ -0,0 +1,54 @@
1
+ """Game systems accelerated by Rust when the prebuilt extension is present."""
2
+ from __future__ import annotations
3
+
4
+ from typing import List, Tuple
5
+
6
+ from aether.accelerators import is_native_available, native_module
7
+
8
+
9
+ def npc_move_toward(
10
+ pos: Tuple[float, float],
11
+ target: Tuple[float, float],
12
+ speed: float,
13
+ attack_range: float = 1.5,
14
+ ) -> Tuple[float, float, int]:
15
+ """
16
+ Move NPC toward target. Returns (x, z, action).
17
+ action: 0=idle, 1=chase, 2=attack
18
+ """
19
+ px, pz = pos
20
+ tx, tz = target
21
+ if is_native_available():
22
+ nx, nz, act = native_module().npc_tick(px, pz, tx, tz, speed, attack_range)
23
+ return nx, nz, int(act)
24
+
25
+ dx, dz = tx - px, tz - pz
26
+ dist = (dx * dx + dz * dz) ** 0.5
27
+ if dist < 0.05:
28
+ return px, pz, 0
29
+ if dist <= attack_range:
30
+ return px, pz, 2
31
+ step = min(speed, dist)
32
+ return px + dx / dist * step, pz + dz / dist * step, 1
33
+
34
+
35
+ def roll_loot(weights: List[float], seed: int = 0) -> int:
36
+ """Weighted random loot index, or -1."""
37
+ if not weights:
38
+ return -1
39
+ if is_native_available():
40
+ return int(native_module().loot_roll(weights, seed & 0xFFFFFFFFFFFFFFFF))
41
+
42
+ import random
43
+
44
+ rng = random.Random(seed)
45
+ total = sum(weights)
46
+ if total <= 0:
47
+ return -1
48
+ pick = rng.uniform(0, total)
49
+ acc = 0.0
50
+ for i, w in enumerate(weights):
51
+ acc += w
52
+ if pick <= acc:
53
+ return i
54
+ return len(weights) - 1
@@ -1,10 +1,11 @@
1
- """Chunked voxel mesh builder — engine-level optimization for block worlds."""
1
+ """Voxel mesh acceleration — Rust when available, pure Python fallback."""
2
2
  from __future__ import annotations
3
3
 
4
- from typing import Callable, Dict, List, Optional, Tuple
4
+ from typing import Dict, List, Tuple
5
5
 
6
6
  import numpy as np
7
7
 
8
+ from aether.accelerators import is_native_available, native_module
8
9
  from aether.graphics.mesh import Mesh
9
10
 
10
11
  Vec3i = Tuple[int, int, int]
@@ -19,6 +20,84 @@ DEFAULT_FACES = [
19
20
  ]
20
21
 
21
22
 
23
+ def _python_build_chunk(
24
+ get_block,
25
+ x0: int,
26
+ y0: int,
27
+ z0: int,
28
+ x1: int,
29
+ y1: int,
30
+ z1: int,
31
+ block_types: set | None,
32
+ faces,
33
+ ) -> Dict[int, Mesh]:
34
+ buckets: Dict[int, list] = {}
35
+ for x in range(x0, x1):
36
+ for y in range(y0, y1):
37
+ for z in range(z0, z1):
38
+ bid = get_block(x, y, z)
39
+ if bid == 0:
40
+ continue
41
+ if block_types and bid not in block_types:
42
+ continue
43
+ if bid not in buckets:
44
+ buckets[bid] = [[], []]
45
+ verts, inds = buckets[bid]
46
+ index = len(verts) // 8
47
+ for normal, corners in faces:
48
+ nx, ny, nz = normal
49
+ if get_block(x + nx, y + ny, z + nz) != 0:
50
+ continue
51
+ face = []
52
+ for px, py, pz in corners:
53
+ verts.extend([x + px, y + py, z + pz, float(nx), float(ny), float(nz), 0.0, 0.0])
54
+ face.append(index)
55
+ index += 1
56
+ inds.extend([face[0], face[1], face[2], face[0], face[2], face[3]])
57
+
58
+ meshes = {}
59
+ for bid, (verts, inds) in buckets.items():
60
+ if inds:
61
+ meshes[bid] = Mesh(
62
+ np.array(verts, dtype=np.float32),
63
+ np.array(inds, dtype=np.uint32),
64
+ )
65
+ return meshes
66
+
67
+
68
+ def _rust_build_chunk(
69
+ get_block,
70
+ x0: int,
71
+ y0: int,
72
+ z0: int,
73
+ x1: int,
74
+ y1: int,
75
+ z1: int,
76
+ block_types: set | None,
77
+ ) -> Dict[int, Mesh]:
78
+ mod = native_module()
79
+ blocks: List[Tuple[int, int, int, int]] = []
80
+ for x in range(x0, x1):
81
+ for y in range(y0, y1):
82
+ for z in range(z0, z1):
83
+ bid = get_block(x, y, z)
84
+ if bid == 0:
85
+ continue
86
+ if block_types and bid not in block_types:
87
+ continue
88
+ blocks.append((x, y, z, int(bid)))
89
+
90
+ raw = mod.build_chunk_mesh(blocks, x0, y0, z0, x1, y1, z1)
91
+ meshes = {}
92
+ for bid, (verts, inds) in raw.items():
93
+ if inds:
94
+ meshes[int(bid)] = Mesh(
95
+ np.array(verts, dtype=np.float32),
96
+ np.array(inds, dtype=np.uint32),
97
+ )
98
+ return meshes
99
+
100
+
22
101
  class VoxelChunk:
23
102
  __slots__ = ("cx", "cy", "cz", "meshes", "dirty")
24
103
 
@@ -31,15 +110,15 @@ class VoxelChunk:
31
110
  class ChunkedVoxelMesher:
32
111
  """
33
112
  Incrementally rebuilds only dirty 16³ chunks instead of the whole world.
34
- Use from any voxel game for stable FPS when editing blocks.
113
+ Uses Rust mesh builder when the prebuilt extension is installed.
35
114
  """
36
115
 
37
116
  CHUNK_SIZE = 16
38
117
 
39
118
  def __init__(
40
119
  self,
41
- get_block: Callable[[int, int, int], int],
42
- block_types: Optional[set] = None,
120
+ get_block,
121
+ block_types=None,
43
122
  chunk_size: int = 16,
44
123
  ):
45
124
  self.get_block = get_block
@@ -48,13 +127,13 @@ class ChunkedVoxelMesher:
48
127
  self.chunks: Dict[Vec3i, VoxelChunk] = {}
49
128
  self._dirty: set[Vec3i] = set()
50
129
  self.faces = DEFAULT_FACES
130
+ self._use_rust = is_native_available()
51
131
 
52
132
  def _chunk_of(self, x: int, y: int, z: int) -> Vec3i:
53
133
  s = self.chunk_size
54
134
  return (x // s, y // s, z // s)
55
135
 
56
136
  def mark_dirty_at(self, x: int, y: int, z: int):
57
- """Mark chunk + neighbors dirty (face culling crosses boundaries)."""
58
137
  cx, cy, cz = self._chunk_of(x, y, z)
59
138
  for dx in (-1, 0, 1):
60
139
  for dy in (-1, 0, 1):
@@ -66,8 +145,6 @@ class ChunkedVoxelMesher:
66
145
 
67
146
  def rebuild_all(self):
68
147
  self._dirty.clear()
69
- seen = set()
70
- # Caller must iterate blocks and call mark_dirty_at OR we expose full rebuild:
71
148
  for key in list(self.chunks.keys()):
72
149
  self._dirty.add(key)
73
150
 
@@ -84,48 +161,27 @@ class ChunkedVoxelMesher:
84
161
  x0, y0, z0 = cx * s, cy * s, cz * s
85
162
  x1, y1, z1 = x0 + s, y0 + s, z0 + s
86
163
 
87
- buckets: Dict[int, list] = {}
88
-
89
- for x in range(x0, x1):
90
- for y in range(y0, y1):
91
- for z in range(z0, z1):
92
- bid = self.get_block(x, y, z)
93
- if bid == 0:
94
- continue
95
- if self.block_types and bid not in self.block_types:
96
- continue
97
- if bid not in buckets:
98
- buckets[bid] = [[], []]
99
- verts, inds = buckets[bid]
100
- index = len(verts) // 8
101
- for normal, corners in self.faces:
102
- nx, ny, nz = normal
103
- if self.get_block(x + nx, y + ny, z + nz) != 0:
104
- continue
105
- face = []
106
- for px, py, pz in corners:
107
- verts.extend([x + px, y + py, z + pz, float(nx), float(ny), float(nz), 0.0, 0.0])
108
- face.append(index)
109
- index += 1
110
- inds.extend([face[0], face[1], face[2], face[0], face[2], face[3]])
164
+ if self._use_rust:
165
+ fresh = _rust_build_chunk(
166
+ self.get_block, x0, y0, z0, x1, y1, z1, self.block_types or None
167
+ )
168
+ else:
169
+ fresh = _python_build_chunk(
170
+ self.get_block, x0, y0, z0, x1, y1, z1, self.block_types or None, self.faces
171
+ )
111
172
 
173
+ chunk = self.chunks.get((cx, cy, cz))
112
174
  meshes = {}
113
- for bid, (verts, inds) in buckets.items():
114
- if inds:
115
- key = (cx, cy, cz, bid)
116
- chunk = self.chunks.get((cx, cy, cz))
117
- old = chunk.meshes.get(bid) if chunk else None
118
- v_arr = np.array(verts, dtype=np.float32)
119
- i_arr = np.array(inds, dtype=np.uint32)
120
- if old is not None:
121
- old.update_data(v_arr, i_arr)
122
- meshes[bid] = old
123
- else:
124
- meshes[bid] = Mesh(v_arr, i_arr)
175
+ for bid, mesh in fresh.items():
176
+ old = chunk.meshes.get(bid) if chunk else None
177
+ if old is not None:
178
+ old.update_data(mesh.vertices, mesh.indices)
179
+ meshes[bid] = old
180
+ else:
181
+ meshes[bid] = mesh
125
182
  return meshes
126
183
 
127
184
  def flush_dirty(self):
128
- """Rebuild dirty chunks. Returns (meshes, removed_keys)."""
129
185
  out = {}
130
186
  removed = []
131
187
  for key in list(self._dirty):
@@ -152,7 +208,6 @@ class ChunkedVoxelMesher:
152
208
  return out, removed
153
209
 
154
210
  def get_all_meshes(self) -> Dict[Tuple[int, int, int, int], Mesh]:
155
- """Return all chunk meshes (for initial scene build)."""
156
211
  result = {}
157
212
  for key, ch in self.chunks.items():
158
213
  for bid, mesh in ch.meshes.items():
@@ -167,7 +222,6 @@ class ChunkedVoxelMesher:
167
222
  self._dirty.add(key)
168
223
 
169
224
  def prime_chunks(self, blocks: Dict[Vec3i, int]):
170
- """Register all block positions and mark their chunks dirty."""
171
225
  for (x, y, z) in blocks.keys():
172
226
  self.ensure_chunk(x, y, z)
173
227
  self.mark_dirty_at(x, y, z)
@@ -43,15 +43,24 @@ FPS / КАМЕРА
43
43
  engine.set_mode("2d")
44
44
  engine.renderer2d.add_sprite(Sprite(x=0, y=0, width=40, height=40, color=(1,0,0,1)))
45
45
 
46
- МОДЕЛИ
47
- from aether import ModelLoader
48
- mesh = ModelLoader.load("hero.glb") # obj/stl/glb/gltf/fbx...
49
-
50
- ВОКСЕЛИ (ОПТИМИЗАЦИЯ)
51
- from aether.graphics.voxel_mesh import ChunkedVoxelMesher
46
+ МОДЕЛИ И АССЕТЫ
47
+ from aether import AssetLoader, ModelLoader
48
+ mesh = AssetLoader.load_model("hero.glb") # obj/stl/glb/gltf/fbx/step/…
49
+ sprite = AssetLoader.load_sprite("icon.png") # png/jpg/webp/gif/…
50
+ tex_id = AssetLoader.load_texture("wall.jpg")
51
+ engine.renderer2d.add_sprite(sprite)
52
+
53
+ ВОКСЕЛИ (ОПТИМИЗАЦИЯ + RUST)
54
+ from aether import ChunkedVoxelMesher, is_native_available, backend_name
55
+ print(backend_name()) # "rust" if prebuilt extension installed, else "python"
52
56
  mesher = ChunkedVoxelMesher(world.get_block)
53
57
  mesher.notify_block_change(x, y, z)
54
- meshes = mesher.flush_dirty() # только изменённые чанки
58
+ meshes, removed = mesher.flush_dirty()
59
+
60
+ NPC / НАГРАДЫ (RUST, опционально)
61
+ from aether import npc_move_toward, roll_loot
62
+ x, z, action = npc_move_toward((0,0), (10,5), speed=0.15)
63
+ item = roll_loot([50, 30, 20], seed=42)
55
64
 
56
65
  ФИЗИКА
57
66
  engine.physics3d.create_body_for_entity(ent, collider=BoxCollider())
@@ -72,7 +81,8 @@ FPS / КАМЕРА
72
81
  ИНСТРУМЕНТЫ
73
82
  aether help — эта справка
74
83
  aether docs — краткий гайд по API
75
- aether-editor — 3D редактор моделей
84
+ aether website — documentation site (domain in bell notifications)
85
+ python -m aether website
76
86
  python games/aether_craft.py
77
87
 
78
88
  ПОЛНАЯ ДОКУМЕНТАЦИЯ: docs/GUIDE.md
@@ -106,11 +116,10 @@ def cmd_build(args):
106
116
  )
107
117
 
108
118
 
109
- def cmd_editor(_args):
110
- from aether.editor.model_editor import main as editor_main
119
+ def cmd_website(args):
120
+ from aether.web.server import run_server
111
121
 
112
- editor_main()
113
- return 0
122
+ return run_server(host=args.host, port=args.port, open_browser=not args.no_browser)
114
123
 
115
124
 
116
125
  def main(argv=None):
@@ -124,7 +133,6 @@ def main(argv=None):
124
133
 
125
134
  sub.add_parser("help", help="Краткая справка по API и командам").set_defaults(func=cmd_help)
126
135
  sub.add_parser("docs", help="Полная документация").set_defaults(func=cmd_docs)
127
- sub.add_parser("editor", help="3D редактор моделей").set_defaults(func=cmd_editor)
128
136
 
129
137
  p_build = sub.add_parser("build", help="Собрать игру в .exe (PyInstaller)")
130
138
  p_build.add_argument("script", help="Путь к main.py игры")
@@ -134,6 +142,20 @@ def main(argv=None):
134
142
  p_build.add_argument("--icon", help="Иконка .ico", default=None)
135
143
  p_build.set_defaults(func=cmd_build)
136
144
 
145
+ p_web = sub.add_parser(
146
+ "website",
147
+ help="Локальный сайт документации (градиент, хеш-URL, настройки домена)",
148
+ )
149
+ p_web.add_argument(
150
+ "--port",
151
+ type=int,
152
+ default=9999,
153
+ help="TCP порт (1–65535; по умолчанию 9999)",
154
+ )
155
+ p_web.add_argument("--host", default="0.0.0.0", help="Адрес привязки (0.0.0.0 = доступ из сети)")
156
+ p_web.add_argument("--no-browser", action="store_true", help="Не открывать браузер")
157
+ p_web.set_defaults(func=cmd_website)
158
+
137
159
  args = parser.parse_args(argv)
138
160
  if not args.command:
139
161
  cmd_help(args)
@@ -7,13 +7,12 @@ _B = {
7
7
  "n": bytes([230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194]),
8
8
  "c": bytes([194, 201, 211, 206, 211, 206, 158, 148, 144]),
9
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,
10
+ 247, 200, 208, 194, 213, 194, 195, 135, 197, 222, 135, 230, 194, 211, 207, 194,
11
+ 213, 135, 226, 201, 192, 206, 201, 194,
13
12
  ]),
14
13
  "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,
14
+ 228, 213, 194, 198, 211, 194, 195, 135, 197, 222, 135, 194, 201, 211, 206, 211,
15
+ 206, 158, 148, 144,
17
16
  ]),
18
17
  "w": bytes([
19
18
  230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194,
@@ -21,11 +20,11 @@ _B = {
21
20
  ]),
22
21
  "sig": bytes([
23
22
  230, 194, 211, 207, 194, 213, 135, 226, 201, 192, 206, 201, 194,
24
- 135, 150, 137, 147, 137, 151,
23
+ 135, 150, 137, 144, 137, 151,
25
24
  ]),
26
25
  }
27
26
 
28
- _EXPECTED = 0xAC3B2627
27
+ _EXPECTED = 0xD5E52D08
29
28
 
30
29
 
31
30
  def _decode(blob: bytes) -> str: