Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with Multiple Mgcb's #4977

Closed
dazinator opened this issue Jul 2, 2016 · 18 comments
Closed

Problem with Multiple Mgcb's #4977

dazinator opened this issue Jul 2, 2016 · 18 comments

Comments

@dazinator
Copy link
Contributor

dazinator commented Jul 2, 2016

Hello,
I am attempting to split out some content so I can use 2 seperate ContentManager instances.

I assumed if I referenced two seperate mgcb files from my project, that I could get them to output their content to different directories when I build my project.

When creating a ContentManager you need to give it a root directory, so I assumed I could create one for each content directory where my mgcb content is output:

 var contentOne = new ContentManager(this.Services, "Content\\ScreenManagement");
 var contentTwo = new ContentManager(this.Services, "Content\\Models");

The problem is, when I build the project, the assets from the multilple referenced mgcbs are always output directly to the content folder under py projects output directory. Furthermore, the 1st output is cleared when the 2nd mgcb is built, and so I actually only end up with the content of the 2nd mgcb file in by output directory.

Here is how it looks (note I added the mgcb files using Add as Link

image

It doesn't seem to matter which folder in my project that I add the reference to the mgcb too, when I build they always seem to output to the outputDir/Content folder for my project.

This means I don't end up with seperate root paths to create my ContentManager instances for, and also the build clearing the content output by the first mgcb when it builds the second one is obvioulsy a bit problematic!

Any ideas?

@Somft
Copy link

Somft commented Jul 4, 2016

Why do you use two different .mgcb files?

@dazinator
Copy link
Contributor Author

dazinator commented Jul 4, 2016

I was experimenting with the idea of being able to have seperate .mgcb files for a variety of reasons, here is some of my thinking:

If I am creating a re-usable library (I'd like to use for multiple different game projects) - let's say it a screen management system - i.e it displays windows, and the library needs to load some content like a background for the windows, and some fonts etc.

If distribute the library with its own mgcb file, then it keeps all the content related to the library easy to locate. If I want to remove the screen management library from my project at some point in the future, I know I can delete the mgcb quite easily, and all of its related content is gone. I could probably get this benefit also with a single mgcb file, by creating a folder name for the window system, and putting all related content under that. However the seperate mgcb approach is also quite useful in that the consumer of the library doesn't need to worry about adding / setting up content to use the library in the first place, they just have to reference the mgcb file. Ofcourse they can customise the content in the mgcb file still if desired.

Such a re-usable library could be distributed via a NuGet package, containing the assembly and a custom install.ps1 script that adds a reference to the mgcb file within the package to the project when you install the NuGet package.

I noticed also that ContentManager has the ability to unload all of it's content, freeing up resources. I was thinking about how this could be useful. My conclusion was that it would be useful to me if I had seperate ContentManager instances containing seperate content, because then I could unload content in a modular way. The scenarios where I think i would want to unload content in my game all fell into a unloading it in a modular way, never unloading all content globally.

@theZMan
Copy link
Contributor

theZMan commented Jul 5, 2016

I noticed also that ContentManager has the ability to unload all of it's
content, freeing up resources. I was thinking about how this could be
useful. My conclusion was that it would be useful to me if I had seperate
ContentManager instances containing seperate content, because then I
could unload content in a modular way.

This is a very common scenario and pretty much how any large game used to
have to manage memory on the limited Xbox 360. For example for Guncraft we
had 3 'alwaysloaded' 'ui' 'ingame' - we switched the ui and ingame content
as needed. I also know of games that have per level content manager

@mrhelmut
Copy link
Contributor

mrhelmut commented Jul 5, 2016

I used to work with multiple ContentManager as well, but I ended up extending it with tags (i.e. Load() accepts optional tags, as well as Unload(), to unload only a specific assets category).

@tomspilman
Copy link
Member

To be clear... MonoGame supports having multiple ContentManager instances just fine.

The issue here in our .targets file:

https://github.com/mono/MonoGame/blob/develop/MonoGame.Framework.Content.Pipeline/MonoGame.Content.Builder.targets

It assumes a single content root no matter the number of .mgcb files. Instead it should be depending on the content root defined within the .mgcb... but the problem is that the .target wouldn't then know were to place the final content.

This process just needs a refactor.

@tomspilman tomspilman added this to the 3.7 Release milestone Jul 5, 2016
@dazinator
Copy link
Contributor Author

dazinator commented Jul 5, 2016

@tomspilman I'd be up for having a go with this, i am quite familiar with msbuild, but i'm new to monogame so would need guidance in particular areas.

It assumes a single content root no matter the number of .mgcb files. Instead it should be depending on the content root defined within the .mgcb...

Is there a way to read the Output Folder setting defined within an mgcb file? I had a look at mgcb.exe but couldn't see anything obvious to read that value out.

image

Assuming we can get to that setting, would the following be basically what we are after:

For each MonoGameContentReference item, read the Output Folder from mgcb file. Use this a relative path from the `$(ProjectDir) to produce:

  1. ContentRootDirectory
  2. ParentOutputDir
  3. ParentIntermediateDir

Formulate the mgcb.exe command with those arguments, and make the call.

This would mean making multiple mgcb.exe calls, once per MonoGameContentReference?

I'm probably not best placed to pick this work up but I will give it a go in anyone elses absence!

@mrhelmut
Copy link
Contributor

mrhelmut commented Jul 6, 2016

I don't think that the mgcb output folder can be used for this, it can be set to something unrelated to content root and obj folder. I believe a dedicated parameter would be better if we'd like to make it clean, or maybe inferring the root based on the .mgcb relative path to ProjectDir.

@tomspilman
Copy link
Member

or maybe inferring the root based on the .mgcb relative path to ProjectDir.

I think @mrhelmut is right here... we can't use mgcb output folder for many different reasons.

It would be easier and clearer to infer the content folder as @mrhelmut mentions.

@dazinator
Copy link
Contributor Author

dazinator commented Jul 6, 2016

Ah ok. All we need is for the content from different mgcb's to end up in different directories rather than all in the same /Content folder. So we could just use /Content/{mgcb name)/ instead. This would be a breaking change though as people would have to adjust their existing content root path in their game classes appropriately.. How about that?

@mrhelmut
Copy link
Contributor

mrhelmut commented Jul 7, 2016

What I was thinking about, is to use the .mgcb's path to define where xnb's are copied, e.g.:

/Content/x.mgcb    =>   root would be   /Content
/Content/Sub/y.mgcb   =>   root would be   /Content/Sub
/AnotherContent/z.mgcb   =>  root would be    /AnotherContent

I believe this would feel natural to users because virtually anything you put in a project folder (being xnb's or non-pipeline files) would end up in the destination you would expect from your project setup in a similar way to the "copy to output" build action.

@dazinator
Copy link
Contributor Author

@mrhelmut - got ya! Yep that makes sense to me.

@dazinator
Copy link
Contributor Author

dazinator commented Jul 8, 2016

Ok, so I have the solution open, and I am making changes to the .targets file. Would appreciate any guidance on the best way is to test it? Am thinking I should just manually create a new monogame project, swap the targets file out in it's .csproj, add some mgcb's to it, and build it - in other words test it all manually? I am assuming there are no automated tests for the project system?

@tomspilman
Copy link
Member

in other words test it all manually? I am assuming there are no automated tests for the project system?

Yeah... that is really the only option.

We don't have any sort of unit testing of the template projects. I would love to fix that some day, but so far no one has stepped up with a solution to that.

dazinator added a commit to dazinator/MonoGame that referenced this issue Jul 8, 2016
Modified msbuild targets file, so that when mgcb's are built, their build output is copied to a directory in the output folder that is relative to the path of the mgcb within the project. This allows content to more easily be isolated by moving mgcb's to seperate prokected folders.
@dazinator
Copy link
Contributor Author

dazinator commented Jul 8, 2016

No problem, will test manually. PR is on its way!

@dazinator
Copy link
Contributor Author

dazinator commented Jul 8, 2016

PR submitted, which makes the following come true:

/Content/x.mgcb => root would be /Content
/Content/Sub/y.mgcb => root would be /Content/Sub
/AnotherContent/z.mgcb => root would be /AnotherContent

So the xnb's will now appear in an output folder that is relative to the mgcb it was produced from.

However am left with some questions now about loading content at runtime.

Given the structure above, what happens if when your game runs you create a new ContentManager instance looking at /Content - does it load all of the xnb's under that directory which would also include /Content/Sub/ folder?

What happens if you create two ContentManager instances, one pointing at /Content and the other at /Content/Sub. Can you unload either of them independently without affecting the other?

@KonajuGames
Copy link
Contributor

Can you unload either of them independently without affecting the other?

Absolutely. They don't affect each other, but you may end up loading the same asset twice, once in one ContentManager instance and again in the other ContentManager instance if you loaded "Sub/Asset" in one and "Asset" in the other. They don't know about each other, so the same asset is loaded twice.

It all comes down to how you plan your assets. It's your choice if you want nested ContentManagers like that.

@dazinator
Copy link
Contributor Author

Cool - sounds good to me then ;)

@dazinator
Copy link
Contributor Author

PR was submitted for this: #4997

@dazinator dazinator mentioned this issue Aug 23, 2017
nkast pushed a commit to nkast/MonoGame that referenced this issue Jun 26, 2018
* Fixes MonoGame#4977
Modified msbuild targets file, so that when mgcb's are built, their build output is copied to a directory in the output folder that is relative to the path of the mgcb within the project. This allows content to more easily be isolated by moving mgcb's to seperate prokected folders.

* Removed a comment line I had accidentally left in.

* Fixed issue where xnb's relative directory was not preserved when output to the bin folder.

* Now also supports linked mgcb files.

* Removed whitespace.
Fixed a problem with a missing condition, causing more output than necessary.

* Removed additional backslahes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants