-
Notifications
You must be signed in to change notification settings - Fork 62
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
Loadout Items, Library Items, and Downloads #1336
Comments
Something to note is that the App actually mostly only cares about the so called How these The proposal above aims to move our architecture from a simple I think that the Apart from UI and Game specific features, Mod Updates are the main feature of the App that would likely be influenced for the better by a more accurate mapping of loadout entities into architecture. |
@halgari we should use this issue to plan out the core types we'll be using, as well as how the new source generator would be used here. I'm thinking that a Loadout Item should only consist of a name, a reference to a Loadout, and an optional reference to a "parent"/group which itself is just another Loadout Item: interface ILoadoutItem
{
EntityId ItemId;
LoadoutId LoadoutId;
Optional<EntityId> ParentItemId;
} We should also have a marker attribute like // better name welcomed
interface ILoadoutItemWithFiles : ILoadoutItem
{
// optionally can have methods like GetFiles() or ToFileTree(), but this shouldn't really have any properties.
} Additional metadata like the version should be in separate interfaces as well. I'd almost say we want separate entities for metadata entirely, similar to how we'd do "layers". Just some ideas, and as you hopefully notice, not a single mention of "mod". |
Notes after chatting with @captainsandypants and @Al12rs. Will write a separate comment about the "My Mods" page. DownloadsFirst off, the data for our downloads should be better organized and simplified. The new designs for the UI heavily make use of the Nexus Mods mod pages. Files that we "download", be that archives, executables, or other, would get metadata attached to them, matching the data available at the "origin". All of this data is also not local to a Loadout, but global: (NOTE: erDiagram
DownloadedFile {
string FileName
ulong Size
Hash Hash
DateTime DateDownloaded
}
NexusModsFile {
string Name
string Version
int FileId
NexusModsPage Page
}
NexusModsPage {
string Name
Image Screenshot
int ModId
}
LocalFile {
string Path
}
DownloadedFile ||--|| LocalFile : includes
DownloadedFile ||--|| NexusModsFile : includes
NexusModsFile }|--|| NexusModsPage : references
Archive {
DownloadedFile Origin
}
ArchiveFile {
stirng FileName
ulong Size
Hash Hash
Archive Parent
}
NestedArchive
DownloadedFile ||--|| Archive : references
Archive ||--|{ ArchiveFile : references
Archive ||--|| NestedArchive : includes
ArchiveFile ||--|| NestedArchive : includes
If a user adds a file from disk, we'd create a new If the downloaded file is an archive, we run it through the extractor and create new This model can also be expanded with a erDiagram
NexusModsCollection {
NexusModsFile[] Files
}
NexusModsCollection ||--|| DownloadedFile : includes
NexusModsCollection ||--|{ NexusModsFile : references
The new "Mods Library" page designed by @captainsandypants will display |
I'm still not happy with some of the names, like I'm thinking about some potential alternatives like |
@Al12rs what about |
I like that more than everything else so far, I think that could work. We can rename the |
|
Updated relationships would look like this: erDiagram
LibraryItem {
string Name
}
LibraryFile {
ulong Size
Hash Hash
string FileName
}
NexusModsLibraryFile {
NexusModsFileMetadata FileMetadata
}
NexusModsFileMetadata {
string Name
string Version
int FileId
NexusModsModPageMetadata Page
}
NexusModsModPageMetadata {
string Name
Image Screenshot
int ModId
}
LocalFile {
string Path
}
LibraryFile ||--|| LibraryItem : includes
NexusModsLibraryFile ||--|| LibraryFile : includes
LocalFile ||--|| LibraryFile : includes
NexusModsLibraryFile ||--|| NexusModsFileMetadata : references
NexusModsFileMetadata }|--|| NexusModsModPageMetadata : references
LibraryArchive {
LibraryArchiveFileEntry[] Children
}
LibraryArchiveFileEntry {
stirng FileName
ulong Size
Hash Hash
LibraryArchive Parent
}
NestedArchive
LibraryArchive ||--|| LibraryFile : includes
LibraryArchiveFileEntry }|--|| LibraryArchive : references
NestedArchive ||--|| LibraryArchive : includes
NestedArchive ||--|| LibraryArchiveFileEntry : includes
|
Cool! Nice work! Yeah I think this is pretty understandable. |
I'm not sure what @halgari can we do double includes like this, or use a different approach? |
Ah, I had missed the relationship with I don't think |
We can have a double includes in the data model, but it's not exposed in a clear way via the source generators. What it would involve is writing our own "TryGetAsXX" and then doing a "reinterpret cast" into the other type. This is pretty much the way the code works internally, and it's about 10 lines of code. |
Notes from meeting with @Al12rs about download states. We've realized that the actual data we want to put into the DB, in regard to downloads, is very limited: erDiagram
LibraryFile
NexusModsLibraryFile {
NexusModsFileMetadata FileMetadata
}
NexusModsLibraryFile ||--|| LibraryFile : includes
NexusModsFileMetadata {
string Name
string Version
int FileId
NexusModsModPage Page
}
NexusModsModPageMetadata {
string Name
int ModId
}
NexusModsLibraryFile ||--|| NexusModsFileMetadata : references
NexusModsFileMetadata }|--|| NexusModsModPageMetadata : references
PersistedState {
PersistedStatus Status
OptionalSize TotalSize
}
NXMDownloadState {
NexusModsFileMetadata FileMetadata
}
NXMDownloadState ||--|| PersistedState : includes
NXMDownloadState ||--|| NexusModsFileMetadata : references
The downloads are currently storing data in the DB and in a "sidecar" file. We've come up with a much better and cleaner design for downloads that puts all the live data into these sidecar files. This data is going to be represented using a
The new approach has the NXM handler create a Once the download is finished, the library service will check if the file is an archive, if that's the case, it will create an extraction activity and pass it to the extractor. When the extractor returns, we create an analyze activity to get the hashes and sizes. The library service calls Finally, the library service will create |
Superseded by #1763. |
Ever since the beginning of the project, a Loadout contained "Mods":
NexusMods.App/src/Abstractions/NexusMods.Abstractions.Loadouts/Loadout.cs
Lines 20 to 23 in 6354819
Which the new DB and PR #1302, this changed to "Mods" specifying the Loadout they are in:
NexusMods.App/src/Abstractions/NexusMods.Abstractions.Loadouts/Mods/Mod.cs
Lines 132 to 136 in afcab96
NexusMods.App/src/Abstractions/NexusMods.Abstractions.Loadouts/Loadout.cs
Lines 123 to 126 in afcab96
We now have a reversed relationship between Loadouts and "Mods". Instead of getting a Loadout and then having access to its "Mods", we can now just get every Entity that is a Mod and has a
LoadoutId
property that matches the value of the Loadout we're trying to look up.This issue is about removing the
Mod
model, and replacing it with a genericLoadoutItem
model.We've been trying to shoehorn everything into our
Mod
model. This is best illustrated by looking at ourModCategory
enum that describes what theMod
is:NexusMods.App/src/Abstractions/NexusMods.Abstractions.Loadouts/Mods/ModCategory.cs
Lines 3 to 13 in afcab96
Translating this into English, we're saying that a
Mod
can be "Game Files", it can be "Saves", it can be a "Mod"...The abstraction that we have, of Loadouts containing Mods, is woefully inadequate. The effects of this can already be seeing by reading #1195 and #1146. We don't even have two Mods installed and already our system is not doing the things we want it to do.
Other mod managers also have an issue with shoehorning everything into a "Mod". In Vortex (@insomnious), a Nexus Mods Collection is represented as a Mod, in Mod Organizer 2 (@Al12rs) a "Mod" is literally just a subdirectory in the
mods
directory.Continuing to use "Mods" to represent the content of a Loadout will just lead to more and more issues, that'll require weird hacks, and that'll incur a lot of tech debt.
However, which the new DB, we can go down a different path. Loadouts won't be containing "Mods" anymore. Instead, we'll have Loadout Items that reference a Loadout. A Loadout Item can be anything: Game Files, Configs, SMAPI Mods, Collections. Since Entities in the new DB are just collections of attributes that we gave a type to make it work in C#, each individual system can just query for the specific attributes it needs.
The apply process wouldn't go through all "Mods" to get the file trees, it would just query all Entities that reference the current Loadout that has a "File Bucket" attribute or similar.
While this is primarily a backend change, doing this allows the UI to be much richer. We could show all Loadout Items like this:
Here we have "Game Files" because a Loadout contains those, we have SMAPI, and then we have an "Item Group" called "Raised Garden Beds". This item group contains three SMAPI Mods as well as a "Config" item that we created when the user launched the game, and it created a config file for the SMAPI Mod. Our new model would allow all of these to be different types of Items that have relationships between one another.
Whether we show such a view is up to the designers, but what this new model allows us to do is make the "Loadout Mods" view easier as well:
Instead of using stuff like the
ModCategory
enum to filter out "Mods" we don't want to show in this view, we'll use the Loaodut Items and special attributes to figure out what we want to show.Collections would also be easier to implement with this, since it'll just be another Item that gets referenced by all the Items it contains.
Overall, going from "Mods" to Loadout Items will make our data model much easier and simpler, as well as allow us to build better UIs. We won't be able to escape the term "Mod" when making a "Mod Manager" but we won't be forced to use it in our backend.
As a small postfix to this post, a "Mod" is just a list of instructions on how to modify something that we humans gave a name to. Outside of modding games, mods exists for cars and other real things as well.
The text was updated successfully, but these errors were encountered: