fix: preserve gameplay data and correct duel evaluation

- Preserve snake customizations across database migrations and merges.
- Lazily load optional storage backends for SQLite maintenance scripts.
- Match Apex territory and nearest-food tie-breaking semantics.
- Resolve duel occupancy after simultaneous movement and food growth.
- Recompute simulated head-to-head danger after body growth.
- Add regression coverage and declare the aiofiles dependency.
This commit is contained in:
2026-08-01 18:16:04 +02:00
parent 4f022d3d01
commit c646392b84
14 changed files with 323 additions and 107 deletions
+32 -4
View File
@@ -1,12 +1,40 @@
from typing import TYPE_CHECKING, Any
from .GameplayDatabase import GameplayDatabase
from .backend import GameplayBackendBuilder
from .LocalStorage import LocalStorage
from .EdgeDB import EdgeDB
if TYPE_CHECKING:
from .EdgeDB import EdgeDB
from .LocalStorage import LocalStorage
__all__ = (
"EdgeDB",
"GameplayBackendBuilder",
"GameplayDatabase",
"LocalStorage",
"StorageLoader",
)
def __getattr__(name:str):
"""Load optional storage backends only when explicitly requested.
Database maintenance scripts import SQLite backend modules through this
package. Eagerly importing LocalStorage used to make those scripts require
unrelated web-storage dependencies such as aiofiles.
"""
if name == "LocalStorage":
from .LocalStorage import LocalStorage
return LocalStorage
if name == "EdgeDB":
from .EdgeDB import EdgeDB
return EdgeDB
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
class StorageLoader:
@classmethod
def build(self, selected_storage:str) -> LocalStorage|EdgeDB:
storage_module = __import__(f"server.database.{selected_storage}", fromlist=[selected_storage])
def build(cls, selected_storage:str) -> Any:
storage_module = __import__(
f"server.database.{selected_storage}", fromlist=[selected_storage],
)
storage_class = getattr(storage_module, selected_storage)
return storage_class