Static helper that runs once per map load to separate moveable props (bins, benches, chairs) from the locally-loaded map and network them from the host. The host unparents each Rigidbody prop, clears NotNetworked, tags it prop_carte and spawns it; clients destroy their local duplicate copies.
namespace HorizonRP;
/// <summary>
/// Les props mobiles de la carte (412 sur Union City : poubelles, bancs, cartons, pots de fleurs,
/// chaises, bennes…) partagés en réseau (Julien, 29/10/2026).
///
/// La carte est chargée par chaque machine de son côté : ses objets n'existent pas en réseau. Un
/// prop mobile bousculé ne bougeait donc que chez celui qui le poussait (chacun voyait la poubelle
/// ailleurs), et le physgun du staff les refusait (« Décor de la carte »). Une fois la carte
/// chargée, l'hôte sort chaque prop mobile de la carte et le partage en réseau ; chaque client
/// détruit sa propre copie et ne garde que celle de l'hôte. Les props fixes restent du décor.
///
/// Appelé à chaque image fixe par HorizonGame, sur toutes les machines ; ne fait son travail
/// qu'une fois par chargement de carte. Les positions ne sont pas sauvegardées : un redémarrage
/// remet tout en place.
/// </summary>
public static class MapProps
{
/// <summary>L'étiquette des props de la carte partagés (un prop posé par le staff ne l'a pas).</summary>
public const string Tag = "prop_carte";
static GameObject handledMap;
public static void Update( Scene scene )
{
if ( scene is null || scene.IsEditor )
return;
var map = scene.GetAllComponents<MapInstance>().FirstOrDefault();
if ( map is null || !map.IsLoaded || map.GameObject == handledMap )
return;
handledMap = map.GameObject;
// Mobiles : un corps physique qui bouge, encore rangé sous la carte. Pas de test sur
// Network.Active : un enfant de la carte le donne vrai alors que la carte n'est pas en
// réseau (le premier essai n'en a trouvé aucun).
var props = map.GameObject.GetComponentsInChildren<Rigidbody>( true )
.Where( x => x.MotionEnabled && x.GameObject.Root == map.GameObject )
.Select( x => x.GameObject )
.Distinct()
.ToList();
if ( Networking.IsHost )
{
foreach ( var go in props )
{
// Hors de la carte (un objet partagé ne peut pas pendre sous un objet local),
// à la même place.
go.SetParent( null, true );
go.Flags &= ~GameObjectFlags.NotNetworked;
go.Tags.Add( Tag );
go.NetworkMode = NetworkMode.Object;
go.NetworkSpawn();
}
Log.Info( $"[Horizon] {props.Count} prop(s) mobile(s) de la carte partagé(s) en réseau" );
}
else
{
// Ceux de l'hôte arrivent par le réseau, hors de la carte : ils ne sont pas dans la liste.
foreach ( var go in props )
go.Destroy();
Log.Info( $"[Horizon] {props.Count} copie(s) locale(s) de props de la carte retirée(s)" );
}
}
}