Aller au contenu

Architecture technique de l'immobilier

Frontières et données

PostgreSQL reste autoritaire. La migration 0037_buildings crée les agences, bâtiments, points, copropriétaires, contrats, badges, reçus, paramètres et permissions de groupe ciblées. Elle ajoute les rattachements optionnels aux stockages et garages sans modifier le comportement des instances autonomes. building_badge est un item système protégé des outils génériques de suppression.

La migration publiée 0037_buildings avait omis le DEFAULT now() SQL sur les colonnes non nulles created_at/updated_at. 0039_building_timestamps, appliquée après le merge 0038, restaure ce contrat sur les huit tables portant TimestampMixin. Elle ne réécrit aucune ligne et son downgrade retire seulement les defaults. Sans ce correctif, le premier INSERT d'agence échoue en 503 avant que l'ORM puisse relire l'entité.

Le modèle Building stocke un polygone XY de 3 à 32 sommets, min_z/max_z, ses bounds indexées, son routing bucket, une révision optimiste et un ownership XOR. Les plafonds applicatifs sont 64 portes, 8 points de gestion, 8 garages et 32 stockages par bâtiment.

La validation refuse les sommets dupliqués, surfaces nulles et auto-intersections. Les points de gestion et portes doivent rester dans le prisme. Les reçus conservent l'UUID historique sans clé étrangère vers le bâtiment afin qu'une suppression sûre n'efface ni ne bloque la piste d'audit.

api/app/services/buildings.py porte les invariants. Le routeur api/app/routers/buildings.py expose deux surfaces :

  • /internal/v1/buildings/*, protégée par la clé API interne et appelée par FXServer ;
  • /admin/buildings/*, protégée par session Discord, permission et CSRF.

Transactions et concurrence

L'achat verrouille l'inventaire personnel puis le bâtiment et les agrégats financiers. Le débit bancaire réutilise debit_account_no_commit; le cash réutilise debit_item_no_commit. Le badge, l'ownership, les permissions de groupe et le reçu sont commités dans la même transaction. Le request_id rend un retry terminé idempotent.

Les transferts et recoveries verrouillent le bâtiment, contrôlent les garages, révoquent les accès et recréent le badge maître dans une transaction unique. Une recovery contenant des véhicules exige un mapping complet vers des garages actifs, externes et compatibles. La suppression de personnage ou groupe est bloquée tant qu'il possède un bâtiment.

Les permissions BLD_<building>_<type>_<resource> sont persistées dans group_scoped_permissions. Elles rejoignent le catalogue effectif du groupe et suivent l'héritage des rangs ; seul le rang owner les reçoit implicitement à leur création. Une permission scoped désactivée ou rattachée à un ancien groupe est filtrée du calcul effectif, même si une ligne de rang historique porte encore son code.

Sécurité des accès

Le client n'envoie jamais une identité ou une position faisant autorité. medusa-buildings dérive le personnage actif, les rôles Discord, la position et le routing bucket sur FXServer. L'API revalide la proximité de l'agence, de la porte ou du point de gestion. Une émission de badge cible uniquement un personnage connecté dans le rayon serveur.

Les codes utilisent le hachage PIN bancaire existant. Redis limite les erreurs par personnage/porte ; si Redis est indisponible, le code échoue fermé, tandis qu'un badge peut toujours être contrôlé en base. L'ouverture recherche un badge actif réellement présent dans l'inventaire personnel courant et vérifie son scope. Les mutations administratives web et en jeu produisent un audit nominatif sans code secret.

Runtime FiveM

medusa-buildings dépend de medusa-core, medusa-player-utils, medusa-moderation, medusa-interactions, medusa-inventory, medusa-groups et medusa-entity-placement. Le serveur charge le snapshot canonique et le diffuse après commit. Le client recalcule toutes les 1,5 seconde la cellule proche à streamDistance : seuls les Peds, blips, points, props et portes voisins sont créés. Les changements de cellule nettoient interactions, blips, props et DoorSystem.

La diffusion monde est volontairement expurgée : elle contient la géométrie et les champs utiles au streaming, jamais l'adresse, le prix ni les owners. Le catalogue complet et la gestion ne sont renvoyés qu'après un contrôle serveur de proximité à l'agence ou au point de gestion.

medusa-entity-placement expose aussi selectEntity(options, callback). Le raycast accepte les types configurés, active le contour uniquement lors d'un changement de cible, emploie un cylindre pour les Peds et ne supprime jamais une entité existante à la fin de la sélection.

Le placement d'une agence depuis F10 suit un handoff de focus explicite : medusa-admin masque sa NUI et appelle SetNuiFocus(false, false) avant de déléguer à medusa-buildings. La touche E reste ainsi la propriété exclusive du placement. La validation produit un request_id; le serveur valide et normalise le payload, force le routing bucket depuis la session puis acquitte le client uniquement après le commit API. adminAgencySaveResult affiche le succès ou l'échec dans le jeu et ne restaure World Edit qu'après cet acquittement. F8 journalise les étapes received, committed ou failed, tandis que F9 reçoit le diagnostic API structuré sous le préfixe IMMOBILIER.

La NUI du module démarre avec sa racine hidden, garde html/body transparents et confine ses surfaces sombres au panneau AR visible. Toute fermeture rend le focus et libère le verrou d'interaction.

Exploitation

ensure medusa-buildings se trouve après ses dépendances et avant medusa-admin. Un arrêt de ressource nettoie les entités locales. Au prochain snapshot, les échéances de relock dépassées sont appliquées avant diffusion et les bâtiments inactifs ou sans owner forcent leurs portes verrouillées.