Static helper that loads a Model from a .vmdl path, a mount:// path, or a Workshop package ident. Caches the load Task and mounts Workshop packages before loading their primary asset, falling back to Model.Error on failure.
namespace HorizonRP;
/// <summary>
/// Charge un modèle à partir de ce que le staff a saisi : un chemin « .vmdl »,
/// un chemin « mount://… » (jeu monté) ou un identifiant de paquet du Workshop
/// « org.paquet ». Chaque machine charge elle-même : le réseau ne transporte que
/// le texte (CityObject.ModelSource), jamais la ressource.
/// </summary>
public static class ModelSources
{
static readonly Dictionary<string, Task<Model>> cache = new();
/// <summary>Ce que l'API accepte aussi (modelOf dans api.php).</summary>
public static bool LooksValid( string source )
{
if ( string.IsNullOrWhiteSpace( source ) || source.Length > 255 )
return false;
return source.EndsWith( ".vmdl", StringComparison.OrdinalIgnoreCase )
|| source.StartsWith( "mount://", StringComparison.OrdinalIgnoreCase )
|| Package.TryParseIdent( source, out _ );
}
public static Task<Model> Load( string source )
{
if ( string.IsNullOrWhiteSpace( source ) )
return Task.FromResult( Model.Error );
// Un modèle en erreur n'est pas gardé : son paquet peut être monté un peu plus tard.
if ( !cache.TryGetValue( source, out var task ) || task.IsCompleted && (task.Result is null || task.Result.IsError) )
{
task = LoadUncached( source );
cache[source] = task;
}
return task;
}
static async Task<Model> LoadUncached( string source )
{
try
{
if ( source.EndsWith( ".vmdl", StringComparison.OrdinalIgnoreCase ) || source.StartsWith( "mount://", StringComparison.OrdinalIgnoreCase ) )
return await Model.LoadAsync( source ) ?? Model.Error;
// Un paquet du Workshop : on le télécharge et on prend son modèle principal.
var package = await Package.Fetch( source, false );
if ( package is null || !string.Equals( package.TypeName, "model", StringComparison.OrdinalIgnoreCase ) )
{
Log.Warning( $"[Horizon] {source} n'est pas un modèle du Workshop" );
return Model.Error;
}
await package.MountAsync();
return await Model.LoadAsync( package.PrimaryAsset ) ?? Model.Error;
}
catch ( Exception e )
{
Log.Warning( $"[Horizon] Modèle {source} illisible : {e.Message}" );
return Model.Error;
}
}
}