Trying out MonoAGS (AGS 5 candidate) : journal

Started by Monsieur OUXX, Wed 02/05/2018 14:56:42

Previous topic - Next topic

tzachs

Quote from: cat on Fri 11/05/2018 09:10:43
AGSEngine.Desktop has a reference to Autofac, while AGSEngine has Autofac as nuget dependency.
Ah, AGSEngine.Desktop was still using the old project format while AGSEngine was converted to the new format, so it's the same bug I mentioned before, I believe. I fixed it in this branch- can you try pulling from that branch and see if that solves it for you? I'll push to master once you confirm, otherwise I'll try to reproduce during the weekend when I have more time.
Btw, for posterity, this is the dotnet bug I'm referring to, which creates problems when combining old and new project formats in project references.

Quote from: Monsieur OUXX on Thu 10/05/2018 22:49:04
Because some of the projects have evolved since then and you need to add references to Autofacs and System.ValueTuple (and possibly Guilabs.Undo). These are not mentionned in the docs.
You shouldn't need to add those as references to your game project (unless you use those directly), that was a result of that bug too (I think).

Quote from: cat on Fri 11/05/2018 09:10:43
The question is: Why did I have to do this in my new solution with the git submodule but not in the original checkout of MonoAGS? Because there I magically have the packages folder already and the references work.
I think the difference is that in MonoAGS, the engine projects and the demo project are configured to be compiled to the same folder. So the demo game happened to work because the engine project copied the dependency to its own folder which happened to be the demo folder too.
In your setup, however, I'm guessing both projects are configured to be compiled to different folders so it didn't work. I might change the demo quest to compile to a different folder so that I would encounter this bug sooner if it reoccurs.

Quote from: Monsieur OUXX on Fri 11/05/2018 09:44:09
I'm getting a headache trying to do extend some classes functionalities without modifying directly MonoAGS's code. The reason is that tzachs used "private" everywhere instead of "protected"! Is there a specific reason? I thought that nowadays, "protected" was more common than the very punishing "private".
Because of that, I cannot derive any class from the engine's classes. Or if I do, I have to rewrite all the helper methods exactly as they are in the parent class instead of just calling them.
PS: if you read closely all the good practices about "protected" vs "private", people say "member variables should always be private, not protected"... but then they say "it's ok if accessors are protected". So I'd say : make everything but member variables protected.
Having everything protected and not private will encourage people to extend via inheritance, which I (and many others) don't like. Inheritance should be used sparingly, I believe, as a lot of time it acts as an anti pattern. This is why MonoAGS tries to encourage composition over inheritance.
Note that in Java when no access modifier is declared the default is protected (same as what you want and I don't). The c# designers had the same opinion as me, that this is a mistake, and so in c# the default modifier is private.

That said, I'm not ruling out inheritance completely, so if you can explain which member you wanted to see as protected and why (i.e what are you trying to do exactly), we can discuss specific use-cases.

Crimson Wizard

#101
Quote from: tzachs on Fri 11/05/2018 16:06:34
That said, I'm not ruling out inheritance completely, so if you can explain which member you wanted to see as protected and why (i.e what are you trying to do exactly), we can discuss specific use-cases.

Imo that is question #1: what is the actual purpose of changes; because particular modification may conflict with the concept of the engine, and there could be other existing or planned methods to achieve same result.

For that reason I've been asking Monsieur OUXX what he is trying to do several times, but he does not want to tell :(.

PS. Sorry, for complaining. I understand that it is not right to demand the answer, but really, if we are discussing the development and usage of the engine here, this explanation would make things so much easier.

cat

#102
Quote from: tzachs on Fri 11/05/2018 16:06:34
I fixed it in this branch- can you try pulling from that branch and see if that solves it for you?
Awesome, it works! Thank you so much!

Quote from: tzachs on Fri 11/05/2018 16:06:34
Having everything protected and not private will encourage people to extend via inheritance, which I (and many others) don't like. Inheritance should be used sparingly, I believe, as a lot of time it acts as an anti pattern. This is why MonoAGS tries to encourage composition over inheritance.
We used to say about a college: "Some people use inheritance as a weapon" ;)

Monsieur OUXX

#103
Quote from: Crimson Wizard on Fri 11/05/2018 16:21:03
For that reason I've been asking Monsieur OUXX what he is trying to do several times, but he does not want to tell :(.
Sorry I felt like my plan was crystal clear since I keep repeating what I want to do (with poor words). I want to add an extra method to ResourcesLoader that loads resources recursively. It scans all subfolders aof the Assets folder, and it also checks if there is an XML file in it.
Since all the classes involded have all their helper functions private, then (even with composition) I have to pretty much reprogram the whole system alongside the original one, by duplicating 95% of its code and changing just 5%. Don't you think it's a pity?

You can see how I've done it here : https://github.com/jeancallisti/fakeags/blob/master/FakeAGS/FakeAGS/Engine/FakeAGSResourcePack.cs
(follow the call stack of LoadAllDefinitionsRecursively )
 

tzachs

#104
Ah, ok, I missed the fact that you want to load recursively.
So, if I understand correctly, you want this method: "public List<IResource> LoadResources(string folder)", to be changed to "public List<IResource> LoadResources(string folder, bool isRecursive)". Right?
Yes, I think that can be valuable.
You haven't mentioned though which member you want to see protected and how it would help you to write the recursive scanning.

I'm also not sure what you mean by "it also checks if there is an xml file in it". Is this about the filename or about verifying the contents? Do you want a boolean true/false about a folder or the list of xml files in that folder?
If it's about the filename, then you can do:
Code: csharp

var resources = loader.LoadResources(myFolder);

bool hasXml = resources.Any(r => r.ID.EndsWith(".xml"));
//or:
List<IResource> xmlResources = resources.Where(r => r.ID.EndsWith(".xml")).ToList();

And if it's about validating the contents, then you can create your validation code and filter based on that:
Code: csharp

private bool isValidXml(IResource resource)
{
   //read the stream and validate.
}

var resources = loader.LoadResources(myFolder);

bool hasXml = resources.Any(isValidXml);
//or:
List<IResource> xmlResources = resources.Where(isValidXml).ToList();


EDIT: Ah, hold on, I see you added an example, let me take a look.

Ok, I understand what you want to do now: load recursively only files matching a specific pattern.
I still don't know what member do you want to be protected to help you write that.

Crimson Wizard

#105
Quote from: Monsieur OUXX on Fri 11/05/2018 17:03:58I want to add an extra method to ResourcesLoader that loads resources recursively. It scans all subfolders aof the Assets folder, and it also checks if there is an XML file in it.

Ohhhhh! Yes, I also missed the idea to load them recursively. Now I understand this. :)


Idk if this suggestion holds any value, but an alternate approach / temporary workaround may be to have a "master" xml file that would hold a list of paths to other xmls. In other words, this will be explicit vs implicit asset loading.


EDIT
@tzachs, BTW, do you think it may make sense to provide a "file tree" interface to iterate asset entries, instead of call "load" for them? (Not sure if it actually always loads for real though).
In other words, instead of (or along with) IResourceLoader, have something like IResourceTree with dictionary of all assets and possibility to request their loading.

For instance, in AGS the engine has a full list of all assets in the package (not loaded to memory, just a list of their names and some info). It's not available in script API, but could be exposed there also in theory.

Hmm, I'd imagine this would need "Rescan" function, and listen for disk changes, because files on disk may get modified. Well, ones in package may change also, if its something like zip archive.

Monsieur OUXX

Quote from: Crimson Wizard on Fri 11/05/2018 18:11:52
an alternate approach / temporary workaround may be to have a "master" xml file that would hold a list of paths to other xmls
Game designers have no time to lose ;) Implicit all the way! Yaaaay! ;)


Quote from: Crimson Wizard on Fri 11/05/2018 18:11:52
@tzachs, if resources are actually always loaded for real when calling "Load"
I wasn't sure either, so I digged and I actually think they are. So my approach of doing some sort of "pre-load" by just searching the definition files (xml files) might come handy for memory management too. In other words: I know that there's something there on disk and I know what it is from the XML file, but I can load or unload it at will.



Quote from: Crimson Wizard on Fri 11/05/2018 18:11:52
@tzachs, BTW, do you think it may make sense to provide a "file tree" interface to iterate asset entries, instead of call "load" for them?
I actually just did that with my AssetDefsFactory.Process(wildcard). Internally it's not a tree but that's the idea.


You can see more from my repo by paying attention to that bit :
Code: ags


                List<IAssetDef> gameAssetsDefs = new AssetDefsFactory(game, resources).Process("*/Game/*");
                var roomsAssetsDefs = new AssetDefsFactory(game, resources).Process("*/Rooms/*");

                string customRes = gameAssetsDefs.First().GetValue("CustomResolution");
                Debug.WriteLine($"Debug : Custom resolution is : '{customRes}'");

(well, for the game settings it doesn't load anything deeper than root level, but for rooms it will)

Please note though that this code is horrible and slow. But it is an embryo of those concepts.
 

tzachs

Quote from: Crimson Wizard on Fri 11/05/2018 18:11:52
@tzachs, BTW, do you think it may make sense to provide a "file tree" interface to iterate asset entries, instead of call "load" for them? (Not sure if it actually always loads for real though).
In other words, instead of (or along with) IResourceLoader, have something like IResourceTree with dictionary of all assets and possibility to request their loading.
Yes, it definitely makes sense. I've added an issue for this here. We'll need to think of how the API will look and how it will be integrated with the current API, and how to integrate the "re-scan" with hot reloading.
I'll try to think about it more (and look at Monsieur OUXX's implementation) after finishing up my current work.

Monsieur OUXX

By the way CW I made a mistake when I said that all the resources are loaded. What happens is that all the files get open, as Streams. But not necessarily read. Each resource as a handle to a file. I still don't know how it's supposed to be handled cleanly outside of the built-in resources like Fonts, etc. For now I'm leaving the streams open when reading my XML files.
 

Monsieur OUXX

#109
Something is bugging me because I wrote nice code based on ResourcePacks to load the XML file that defines the title, width and depth of the game... But then I realized that game.Factories.Resources is not populated until after calling game.Start (to which you must pass the settings).

You need the resources to get the settings to call game.Start, and you need to call game.Start to get the resources to get the settings. >:(

Oh well. At least my construct works for everything else but the early initialization.
 

tzachs

You can change the title and window size after the game started (but not the virtual resolution, sorry about that): game.Settings.Title = "My title";

Monsieur OUXX

Right now I'm struggling understanding how the rooms work! Again I'm baging my head against the wall because of all the asbstraction.

Can you confirm that this is the lifecycle of a room :

Code: ags

CustomRoom customRoom = new CustomRoom(player) //by the way wouldn't it be more efficient to provide "game" straight away?
Task<IRoom> roomTask = customRoom.LoadAsync(game)
--->      IRoom room = factory.Room.GetRoom(_roomId, 20f, 310f, 190f, 10f); //Creates some dummy room object?
         //...fill all the room's attributes

waitForRoom(game, roomTask )
--->     game.State.Rooms.Add(room)


And then at any time later in the game, whenever something happens that might have broken a pointer to a room (like restoring a saved game) :
Code: ags

_updatedRoomReference = Rooms.Find(_game.State, _oldRoomReference); //This just looks up the room with the same ID as the old reference


 

tzachs

Yes, that's the lifecycle of a room.

Quote
//by the way wouldn't it be more efficient to provide "game" straight away?
I wouldn't say more efficient, but more aesthetically pleasant, yes.

Quote
And then at any time later in the game, whenever something happens that might have broken a pointer to a room (like restoring a saved game) :
Right, only restoring a saved game is the only thing that can break that pointer.

Crimson Wizard

#113
In the past I wanted to suggest to split concept of "room item" and loaded room, this way game could track available rooms, and keep some of them unloaded for a reason.

In theory it is possible to write your own manager that does that (loads and disposes all room objects, for example).

But then there is a problem of saving room state in between room unloading and reloading, and changing something in the room which is not loaded (what bugs users of AGS currently). So, that's not trivial.

Probably unloading only graphic and audio resources may be a better choice. (But then again, there could be dynamically modified sprites, so... all this requires a good design)

tzachs

I'm not following: the concept of room item and loaded room are already split. You can get a room from the factory with zero state and add stuff later to it, and you can add stuff with or without graphics/audio loaded.
We're missing the built in loading/unloading resource strategies we mentioned before (which one of them will be "load room resources when moving to room"), but other than that, I'm not sure what's missing.

Crimson Wizard

#115
Quote from: tzachs on Sat 12/05/2018 19:17:39
I'm not following: the concept of room item and loaded room are already split. You can get a room from the factory with zero state and add stuff later to it, and you can add stuff with or without graphics/audio loaded.
We're missing the built in loading/unloading resource strategies we mentioned before (which one of them will be "load room resources when moving to room"), but other than that, I'm not sure what's missing.

My meaning was that these concepts are not split in the engine API. There is only one IRoom type, which has no definition of its state. There is no way to tell if the room is a bare object just from the factory, or fully initialized room. User must track this on their own.

I am currently not sure if engine may or should be capable of that, and how to organize this, that is why I decided to not formally suggest that yet.

tzachs

Ah, ok, got you now. Yeah, I'm also not sure if we actually need this.

Monsieur OUXX

I'm currently writing that loading/unloading.
For now I don't care if the state of the room is not kept in-between visits. I'll start with a simple behavior.
How do you unload things?
 

tzachs

To remove an entity from a room: room.Objects.Remove(myEntity);
Or if it's an area: room.Areas.Remove(myEntity);
To remove an entity from the GUI list: state.UI.Remove(myEntity);

After removing the entity from room/gui: myEntity.Dispose();

Monsieur OUXX

#119
OK, thanks.

Based on these sources, does anyone understand why the word "Loading" does not appear?

FakeAGSTestGame/Assets/Rooms/Splashscreen/RoomScript.cs
FakeAGS/Rooms/FakeAGSLoadingScreen.cs


 

SMF spam blocked by CleanTalk