Client LuaCsForBarotrauma
ContentFile.cs
1 #nullable enable
2 using System;
3 using System.Collections.Generic;
4 using System.Collections.Immutable;
5 using Barotrauma.IO;
6 using System.Linq;
7 using System.Reflection;
8 using System.Xml.Linq;
9 
10 namespace Barotrauma
11 {
12  [AttributeUsage(AttributeTargets.Class, Inherited = true)]
13  public class NotSyncedInMultiplayer : Attribute { }
14 
22  public abstract class ContentFile
23  {
24  public class TypeInfo
25  {
26  public readonly Type Type;
27  public readonly bool RequiredByCorePackage;
28  public readonly bool NotSyncedInMultiplayer;
29  public readonly ImmutableHashSet<Type>? AlternativeTypes;
30  public readonly ImmutableHashSet<Identifier> Names;
31  private readonly MethodInfo? contentPathMutator;
32 
33  public TypeInfo(Type type)
34  {
35  Type = type;
36 
37  var reqByCoreAttribute = type.GetCustomAttribute<RequiredByCorePackage>();
38  RequiredByCorePackage = reqByCoreAttribute != null;
39  var notSyncedInMultiplayerAttribute = type.GetCustomAttribute<NotSyncedInMultiplayer>();
40  NotSyncedInMultiplayer = notSyncedInMultiplayerAttribute != null;
41  AlternativeTypes = reqByCoreAttribute?.AlternativeTypes;
42  contentPathMutator
43  = Type.GetMethod(nameof(MutateContentPath), BindingFlags.Static | BindingFlags.Public);
44 
45  HashSet<Identifier> names = new HashSet<Identifier> { type.Name.RemoveFromEnd("File").ToIdentifier() };
46  if (type.GetCustomAttribute<AlternativeContentTypeNames>(inherit: false)?.Names is { } altNames)
47  {
48  names.UnionWith(altNames);
49  }
50 
51  Names = names.ToImmutableHashSet();
52  }
53 
55  => (ContentPath?)contentPathMutator?.Invoke(null, new object[] { path })
56  ?? path;
57 
58  public ContentFile? CreateInstance(ContentPackage contentPackage, ContentPath path) =>
59  (ContentFile?)Activator.CreateInstance(Type, contentPackage, path);
60  }
61 
62  public readonly static ImmutableHashSet<TypeInfo> Types;
63  static ContentFile()
64  {
65  Types = ReflectionUtils.GetDerivedNonAbstract<ContentFile>()
66  .Select(t => new TypeInfo(t))
67  .ToImmutableHashSet();
68  }
69 
70  public static bool IsLegacyContentType(XElement contentFileElement, ContentPackage package, bool logWarning)
71  {
72  Identifier elemName = contentFileElement.NameAsIdentifier();
73  if (elemName == "TraitorMissions")
74  {
75  if (logWarning)
76  {
77  DebugConsole.AddWarning(
78  $"The content type \"TraitorMission\" in content package \"{package.Name}\" is no longer supported." +
79  $" Traitor missions should be implemented using the scripted event system and the content type TraitorEvents.",
80  package);
81  }
82  return true;
83  }
84  return false;
85  }
86 
87  public static Result<ContentFile, ContentPackage.LoadError> CreateFromXElement(ContentPackage contentPackage, XElement element)
88  {
89  static Result<ContentFile, ContentPackage.LoadError> fail(string error, Exception? exception = null)
90  => Result<ContentFile, ContentPackage.LoadError>.Failure(new ContentPackage.LoadError(error, exception));
91 
92  Identifier elemName = element.NameAsIdentifier();
93  var type = Types.FirstOrDefault(t => t.Names.Contains(elemName));
94  var filePath = element.GetAttributeContentPath("file", contentPackage);
95  if (type is null)
96  {
97  return fail($"Invalid content type \"{elemName}\"");
98  }
99  if (filePath is null)
100  {
101  return fail($"No content path defined for file of type \"{elemName}\"");
102  }
103 
104  using var errorCatcher = DebugConsole.ErrorCatcher.Create();
105  try
106  {
107  filePath = type.MutateContentPath(filePath);
108  if (!File.Exists(filePath.FullPath))
109  {
110  return fail($"Failed to load file \"{filePath}\" of type \"{elemName}\": file not found.");
111  }
112 
113  var file = type.CreateInstance(contentPackage, filePath);
114  if (file is null) { return fail($"Content type {type.Type.Name} is not implemented correctly"); }
115 
116  if (errorCatcher.Errors.Any())
117  {
118  return fail(
119  $"Errors were issued to the debug console when loading \"{filePath}\" of type \"{elemName}\"");
120  }
121  return Result<ContentFile, ContentPackage.LoadError>.Success(file);
122  }
123  catch (Exception e)
124  {
125  return fail($"Failed to load file \"{filePath}\" of type \"{elemName}\": {e.Message}", e);
126  }
127  }
128 
129  protected ContentFile(ContentPackage contentPackage, ContentPath path)
130  {
131  ContentPackage = contentPackage;
132  Path = path;
133  Hash = CalculateHash();
134  }
135 
137  public readonly ContentPath Path;
138  public readonly Md5Hash Hash;
139  public abstract void LoadFile();
140  public abstract void UnloadFile();
141  public abstract void Sort();
142 
143  public virtual void Preload(Action<Sprite> addPreloadedSprite) { }
144 
145  public virtual Md5Hash CalculateHash()
146  {
147  return Md5Hash.CalculateForFile(Path.Value, Md5Hash.StringHashOptions.IgnoreWhitespace);
148  }
149 
150  public bool NotSyncedInMultiplayer => Types.Any(t => t.Type == GetType() && t.NotSyncedInMultiplayer);
151  }
152 }
readonly ImmutableHashSet< Identifier > Names
Definition: CorePackage.cs:24
readonly? ImmutableHashSet< Type > AlternativeTypes
Definition: ContentFile.cs:29
readonly ImmutableHashSet< Identifier > Names
Definition: ContentFile.cs:30
readonly bool NotSyncedInMultiplayer
Definition: ContentFile.cs:28
ContentPath MutateContentPath(ContentPath path)
ContentFile? CreateInstance(ContentPackage contentPackage, ContentPath path)
readonly bool RequiredByCorePackage
Definition: ContentFile.cs:27
Base class for content file types, which are loaded from filelist.xml via reflection....
Definition: ContentFile.cs:23
static Result< ContentFile, ContentPackage.LoadError > CreateFromXElement(ContentPackage contentPackage, XElement element)
Definition: ContentFile.cs:87
static bool IsLegacyContentType(XElement contentFileElement, ContentPackage package, bool logWarning)
Definition: ContentFile.cs:70
static readonly ImmutableHashSet< TypeInfo > Types
Definition: ContentFile.cs:62
readonly ContentPath Path
Definition: ContentFile.cs:137
abstract void UnloadFile()
readonly Md5Hash Hash
Definition: ContentFile.cs:138
abstract void Sort()
readonly ContentPackage ContentPackage
Definition: ContentFile.cs:136
virtual Md5Hash CalculateHash()
Definition: ContentFile.cs:145
virtual void Preload(Action< Sprite > addPreloadedSprite)
Definition: ContentFile.cs:143
ContentFile(ContentPackage contentPackage, ContentPath path)
Definition: ContentFile.cs:129
abstract void LoadFile()
readonly record struct LoadError(string Message, Exception? Exception)
string???????????? Value
Definition: ContentPath.cs:27
static Md5Hash CalculateForFile(string path, StringHashOptions options)
Definition: Md5Hash.cs:114