diff --git a/src/GameCult.Mesh/CultMeshDocumentRegistries.cs b/src/GameCult.Mesh/CultMeshDocumentRegistries.cs
new file mode 100644
index 00000000..a210671a
--- /dev/null
+++ b/src/GameCult.Mesh/CultMeshDocumentRegistries.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using GameCult.Caching;
+using GameCult.Networking;
+
+namespace GameCult.Mesh
+{
+ public static partial class CultMesh
+ {
+ ///
+ /// Creates a CultCache document registry and resolves the supplied document types into it.
+ ///
+ public static CultDocumentRegistry CreateCultCacheDocumentRegistry(params Type[] documentTypes)
+ {
+ return CreateCultCacheDocumentRegistry((IEnumerable)documentTypes);
+ }
+
+ ///
+ /// Creates a CultCache document registry and resolves the supplied document types into it.
+ ///
+ public static CultDocumentRegistry CreateCultCacheDocumentRegistry(IEnumerable documentTypes)
+ {
+ if (documentTypes == null)
+ {
+ throw new ArgumentNullException(nameof(documentTypes));
+ }
+
+ var registry = new CultDocumentRegistry();
+ foreach (var documentType in DistinctDocumentTypes(documentTypes))
+ {
+ registry.GetRequired(documentType);
+ }
+
+ return registry;
+ }
+
+ ///
+ /// Creates a CultNet document registry with default bindings for the supplied document types.
+ ///
+ public static CultNetDocumentRegistry CreateCultNetDocumentRegistry(params Type[] documentTypes)
+ {
+ return CreateCultNetDocumentRegistry((IEnumerable)documentTypes);
+ }
+
+ ///
+ /// Creates a CultNet document registry with default bindings for the supplied document types.
+ ///
+ public static CultNetDocumentRegistry CreateCultNetDocumentRegistry(IEnumerable documentTypes)
+ {
+ return CreateCultNetDocumentRegistry(documentTypes, null);
+ }
+
+ ///
+ /// Creates a CultNet document registry with default bindings for the supplied document types.
+ ///
+ public static CultNetDocumentRegistry CreateCultNetDocumentRegistry(
+ IEnumerable documentTypes,
+ CultDocumentRegistry? documents)
+ {
+ if (documentTypes == null)
+ {
+ throw new ArgumentNullException(nameof(documentTypes));
+ }
+
+ documents ??= CreateCultCacheDocumentRegistry(documentTypes);
+ var registry = new CultNetDocumentRegistry(documents);
+ var descriptors = DistinctDocumentTypes(documentTypes)
+ .Select(documentType => documents.GetRequired(documentType))
+ .ToArray();
+ foreach (var descriptorGroup in descriptors.GroupBy(descriptor => descriptor.SchemaId, StringComparer.Ordinal))
+ {
+ foreach (var descriptor in descriptorGroup.Reverse())
+ {
+ registry.Register(CultNetDocumentBinding.ForDocument(descriptor.DocumentType, documents));
+ }
+ }
+
+ return registry;
+ }
+
+ ///
+ /// Creates a CultNet document registry with default bindings for the supplied document types.
+ ///
+ public static CultNetDocumentRegistry CreateCultNetDocumentRegistry(
+ CultDocumentRegistry documents,
+ params Type[] documentTypes)
+ {
+ return CreateCultNetDocumentRegistry(documentTypes, documents);
+ }
+
+ ///
+ /// Creates a CultNet document registry with default bindings for the supplied document types.
+ ///
+ public static CultNetDocumentRegistry CreateCultNetDocumentRegistry(
+ CultDocumentRegistry documents,
+ IEnumerable documentTypes)
+ {
+ return CreateCultNetDocumentRegistry(documentTypes, documents);
+ }
+
+ private static IEnumerable DistinctDocumentTypes(IEnumerable documentTypes)
+ {
+ return documentTypes
+ .Select(documentType => documentType ?? throw new ArgumentException(
+ "Document type collections cannot contain null entries.",
+ nameof(documentTypes)))
+ .Distinct();
+ }
+ }
+}
diff --git a/src/GameCult.Networking/CultNetDocumentRegistry.cs b/src/GameCult.Networking/CultNetDocumentRegistry.cs
index d6446b2d..fe039dfa 100644
--- a/src/GameCult.Networking/CultNetDocumentRegistry.cs
+++ b/src/GameCult.Networking/CultNetDocumentRegistry.cs
@@ -94,6 +94,42 @@ namespace GameCult.Networking
? payloadDeserializer(payload)
: CultDocumentMessagePackSerialization.Deserialize(payload));
}
+
+ ///
+ /// Creates a document binding for a CultCache document type discovered at runtime.
+ ///
+ public static CultNetDocumentBinding ForDocument(
+ Type documentType,
+ CultDocumentRegistry? registry = null,
+ string? schemaId = null,
+ Func