/// <summary> /// Please define 'bind'. I think it means.. fields are bound to other things /// </summary> protected virtual void BindManifest(ContentManifestEntry manifest) { //TODO: consider: prevent from being bound more than once. That's just illegal. //just keep a bool for that purpose? What a waste. //What if the binding is just so internal to the engine that it's not possible for a user to do it more than once? //That would be preferred var myType = GetType(); attributes = manifest.Attributes; //bind all children if (manifest.Children != null) { foreach (var child in manifest.Children) { var name = child.Name; //even though the manifest may have come from reflection... //it also may NOT have. so we need to reflect again in order to do the binding var fieldInfo = myType.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); //check the current value. if it's null, we need to instantiate a proxy var o = fieldInfo.GetValue(this); if (o == null) { o = CreateBoundContentProxy(fieldInfo.FieldType, name, child); fieldInfo.SetValue(this, o); } //bind this child var childContentField = o as ContentBase; if (childContentField == null) { //if it isn't content, then it is a subcontent holder //but what to do about that? we need to set its members //our old concept was to automatically create directories here. but that's actually inappropriate.. //if the holder were also a directory, we would automatically do it.. //--although I would need to make sure directories hosted on other things work //and really, that's pretty bad //maybe I need a special process here that goes off into the great beyond filling in unknown types... //but.. uh.. it could be pretty cool to have these be directories. maybe lets try it and see how it goes. //or maybe a new type that can enumerate its members } else { childContentField.BindManifest(child); } } } }
/// <summary> /// Creates a content proxy which is.. 'bound'.. to a field? /// That's the intent, but it looks like the different logic in here isn't directly related to that. /// It should be possible to do oven stuff without... man, I dont know /// </summary> protected virtual ContentBase CreateBoundContentProxy(Type type, string name, ContentManifestEntry manifestEntry) { //manifestEntry has to come in so we can use it during creating content.. yeah.. i should have thought of that. //I need to clean that up ContentBase content = CreateContentProxy(type, name); content.attributes = manifestEntry.Attributes; //if we're bruted, just create the baked loader and move on #if !BRUTED //try getting the pipeline we'd use to bake this content. If there's such a pipeline, set us up to use it var pipeline = Manager.PipelineConnector.GetPipeline(content); if (pipeline != null) { //TODO - I had intended a 1:1 correspondence of loaders to content instances, but maybe I can have the loaders have the required name and directory context? I think it should be possible content._loader = new Loaders.ProtoLoader(content) { directoryOwner = this as ContentDirectory, //I guess we know this is true name = name }; return(content); } #endif //if we weren't able to setup the proto loader, we can still load it baked //this probably doesnt make any sense in a Proto target.. //maybe it makes sense for binary content not managed by a content type really? //but we should probably have some setup that copies it over, anyway //else, a ProtoSlimLoader which loads it straight from the content directory. //not sure yet. anyway, this is harmless. content._loader = new Loaders.BakedLoader(content) { directoryOwner = this as ContentDirectory, //I guess we know this is true name = name }; return(content); }
protected override ContentBase CreateBoundContentProxy(Type type, string name, ContentManifestEntry manifestEntry) { //special handling for directories--we need to create subdirectories specially //EDIT: do we really? can't it get dealt with, like, by tracking the directory owner, and pulling information from that? var content = base.CreateBoundContentProxy(type, name, manifestEntry); if (typeof(ContentDirectory).IsAssignableFrom(type)) { var newSubdir = (ContentDirectory)content; newSubdir.RawContentDiskRoot = System.IO.Path.Combine(RawContentDiskRoot, name); newSubdir.BakedContentDiskRoot = System.IO.Path.Combine(BakedContentDiskRoot, name); //special handling: var backendSubdirAttrib = content.Attributes.GetAttribute <BackendSubdirectory>(); if (backendSubdirAttrib != null) { newSubdir.RawContentDiskRoot = System.IO.Path.Combine(newSubdir.RawContentDiskRoot, Manager.SelectedBackend.ToString()); newSubdir.BakedContentDiskRoot = System.IO.Path.Combine(newSubdir.BakedContentDiskRoot, Manager.SelectedBackend.ToString()); } newSubdir.LogicalPath = $"{LogicalPath}/{name}"; //it's responsible for loading itself.. kind of weird.. //that's different from other content (which is concretely realized on the filesystem) newSubdir._loader = newSubdir; } //we need to track all the content we create, that's one of the purposes of a directory childContent[name] = content; return(content); }
static ContentManifestEntry _Reflect(Type targetType, bool inContent) { bool iAmDirectory = targetType.IsSubclassOf(typeof(ContentDirectory)); ContentManifestEntry ret = new ContentManifestEntry() { Children = new List <ContentManifestEntry>() }; foreach (var fi in targetType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { var fieldType = fi.FieldType; bool isContentType = fieldType.IsSubclassOf(typeof(ContentBase)); bool isContentDirectory = fieldType.IsSubclassOf(typeof(ContentDirectory)); bool isSubcontentHolder = fi.GetCustomAttributes <SubcontentAttribute>().Any(); if (isContentDirectory) { var entry = _Reflect(fi.FieldType, inContent); entry.Name = fi.Name; entry.Attributes = fi.GetCustomAttributes(false); //warning: this may affect how the directory is interpreted, so maybe it should do this ret.Children.Add(entry); } else if (isContentType) { //content can have subcontent holders, so we need to recurse into that var entry = _Reflect(fieldType, true); entry.Name = fi.Name; entry.Attributes = fi.GetCustomAttributes(false); ret.Children.Add(entry); } else if (isSubcontentHolder) { //subcontent must be under content; moreover it must be legal for that content type (todo) if (!inContent) { throw new Exception("Structural error: subcontent outside of content"); //todo: just make an informative log message //continue; } var entry = _Reflect(fieldType, true); entry.Name = fi.Name; entry.Attributes = fi.GetCustomAttributes(false); ret.Children.Add(entry); } else { if (inContent) { } else if (iAmDirectory) { } else { throw new Exception("Non-content found in content manifest type"); } } } return(ret); }
internal void DoBindManifest(ContentManifestEntry manifest) { BindManifest(manifest); }