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

Add -additionaldir command-line option #471

Merged
merged 12 commits into from
Oct 5, 2024

Conversation

Jayman2000
Copy link
Contributor

@Jayman2000 Jayman2000 commented Jun 27, 2024

Pull Request Type

  • GitHub Workflow changes
  • Documentation or Wiki changes
  • Build and Dependency changes
  • Runtime changes
    • Render changes
    • Audio changes
    • Input changes
    • Network changes
    • Other changes

Description

This PR adds a command-line argument named -additionaldir. -additionaldir allows users to store game assets in multiple different directories. For example, if a user installs Descent 3 using a package manager, then they’ll be able to do this:

Descent3 -setdir <path-to-proprietary-assets> -additionaldir <path-to-assets-installed-by-package-manager>

Related Issues

This is related to #373 and #534, but it doesn’t fully implement either of those feature requests.

Checklist

  • I have tested my changes locally and verified that they work as intended.
  • I have documented any new or modified functionality.
  • I have reviewed the changes to ensure they do not introduce any unnecessary complexity or duplicate code.
  • I understand that by submitting this pull request, I am agreeing to license my contributions under the project's license.

Additional Comments

This PR is meant to be a first step. If it gets accpeted, then I’ll open PRs to do the following:

  • Add a CMake option called DEFAULT_ADDITIONAL_DIRS. This way, package maintainers will be able to create Descent3 executables that automatically find the other files that get installed by their Descent 3 package.
  • Add documentation for -additionaldir and how it compares to -setdir.
  • Allow Descent 3 to load .pld files from -additionaldir directories.

@winterheart
Copy link
Collaborator

Hi. I'm working on cfile / ddio refactoring and some of your changes already done in my branch. I'd suggest to wait it since after that path handling and general filesystem operations will be much simpler.

Still, there some comments on general idea (as I see it, regarding to #373 and similar issues). We have read-only and writable paths. Read-only are some preconfigured on build-time paths (like /usr/share/Descent3 in Linux or values from Registry in Windows from preinstalled D3) and defined by configuration/command line options. Writable paths are paths that known to belong to current user (like $HOME/.config, $HOME/.local in Linux, and C:\users\USER\AppData in Windows). All of these paths together forms search path list, that should be searched from writable to read-only. Descent3 should use relative path notation for resource lookup, and cfile / ddio will handle all work to search and open requested file. There no current dir in search for reason. Unless we going to make "portable" mode for game, Descent3 should use proposed scheme to write user-data (cache/temp files included) to writeable paths that belongs to user. Current dir may not belong to user, so we should avoid overuse it.

@Jayman2000
Copy link
Contributor Author

Hi. I'm working on cfile / ddio refactoring and some of your changes already done in my branch. I'd suggest to wait it since after that path handling and general filesystem operations will be much simpler.

So you’re saying that I should rebase this PR on main once your PR gets merged?

Still, there some comments on general idea (as I see it, regarding to #373 and similar issues). We have read-only and writable paths. Read-only are some preconfigured on build-time paths (like /usr/share/Descent3 in Linux or values from Registry in Windows from preinstalled D3) and defined by configuration/command line options. Writable paths are paths that known to belong to current user (like $HOME/.config, $HOME/.local in Linux, and C:\users\USER\AppData in Windows). All of these paths together forms search path list, that should be searched from writable to read-only. Descent3 should use relative path notation for resource lookup, and cfile / ddio will handle all work to search and open requested file. There no current dir in search for reason. Unless we going to make "portable" mode for game, Descent3 should use proposed scheme to write user-data (cache/temp files included) to writeable paths that belongs to user. Current dir may not belong to user, so we should avoid overuse it.

I agree. This PR implements all of what you said, except for these things:

  • This PR makes it so that there’s only one writable path.
  • There’s no way to specify additional/read-only paths at compile time yet. Like I mentioned before, I’ll do that in a later PR.
  • This PR doesn’t add any code that looks in the Windows registry.
  • At the moment, the default value for the writable directory is still the current working directory. A future PR can make it default to something else.

@Jayman2000
Copy link
Contributor Author

I just pushed a new version of this PR. Here are the changes:

  • It’s been rebased onto d3659bb (Merge pull request fix path creation in gamesave.cpp #499 from vlohacks/main, 2024-07-22). I started this rebase a while ago, and me a long time to finish it. That’s the only reason why this PR hasn’t been rebased onto something newer yet.
  • I had to move the Base_directory variable from Descent3/init.cpp into a its own new module. I had to move it because of the tests added in 1a3c012 (Refactor cf_FindRealFileNameCaseInsensitive() function, 2024-07-02). Those tests were for the cf_FindRealFileNameCaseInsensitive() function. This PR has always deleted that function and replaced it with a new one. Here’s the problem: that new function needs access to the Base_directory variable. For the regular game, accessing Base_directory is fine because Base_directory is delcared in Descent3/init.cpp. For tests, accessing Base_directory is a problem. Tests don’t currently link to Descent3/init.cpp, and I found it really difficult to make them link to Descent3/init.cpp. That’s why I created a separate module to hold the Base_directory variable.
  • On the bright side, I was able to use that separate module in order to keep all of the new functions that this PR adds in one single place. While the new module started as a workaround, I actually like it better this way. It makes things more compartmentalized.

Here are some things that I could use some help with:

  • I ended up deleting the new implementation of cf_FindRealFileNameCaseInsensitive() that was added in 1a3c012 (Refactor cf_FindRealFileNameCaseInsensitive() function, 2024-07-02). One of the commits from this PR replaces cf_FindRealFileNameCaseInsensitive() with a new function named gd_LocatePath(). I haven’t run any benchmarks, but I think that my gd_LocatePath() is probably slower than cf_FindRealFileNameCaseInsensitive(). I don’t really understand the code that was in cf_FindRealFileNameCaseInsensitive(), but it would be nice if that code could somehow be reused in the implementation of gd_LocatePath().
  • One of the commits introduces a regression on Windows. Specifically, if you try to start a multiplayer server, then the game will page fault.

@Jayman2000
Copy link
Contributor Author

Jayman2000 commented Aug 20, 2024

I just pushed another new version of this PR. Here are the changes:

  • It’s been rebased onto 3cb1e89 10ce4e4 (Merge pull request build on OpenBSD via Linux pathways #542 from rfht/main, 2024-08-20).
  • I fixed the uninitialized memory access on Windows.
  • I took some of the changes from the “Add -aditionaldir option” commit and put them in their own commit named “Add start parameter to FindArg()”. I think that this makes the commit history cleaner, and it also gave me an opportunity to write a new commit message where I justify some of my changes.

I could still use some help with the whole cf_FindRealFileNameCaseInsensitive() thing.

@Jayman2000 Jayman2000 force-pushed the additionaldir-arg branch 4 times, most recently from 7dc8ad8 to 66eb9ce Compare August 22, 2024 21:13
@Lgt2x
Copy link
Member

Lgt2x commented Aug 27, 2024

Tell us if/when you need feedback or help with anything :)

@Jayman2000
Copy link
Contributor Author

Tell us if/when you need feedback or help with anything :)

Like I mentioned earlier, I could use some help with the cf_FindRealFileNameCaseInsensitive() thing. I know that @winterheart put in a significant amount of effort into making cf_FindRealFileNameCaseInsensitive() faster. At the moment, this PR deletes cf_FindRealFileNameCaseInsensitive() and replaces it with gd_LocatePath(). It would be nice if that faster code could somehow be incorporated into gd_LocatePath(), but I don’t know how to do that because I don’t really understand the code that’s in cf_FindRealFileNameCaseInsensitive().

@winterheart
Copy link
Collaborator

I would like to correct some points of PR. It's rather big feature request.

Let's try to implement feature in one of already exist submodule. As it's more file access and discovery thing, I suggest to use cfile. We don't have much writable paths in to be defined (actually is one - location in user's home dir), so we can inject proper path in cfopen() with checking "w" flag option. For reading we can use list of paths and hog-files specified in not yet implemented cf_Init() function. In this way cfopen() should always accepts relative paths - "d3.hog", "missions/d3.msn", "movies/intro.mve" etc.

I thinking about using some cache path resolver that adds already resolved paths into LRU cache or similar by lowercased relative path filename. This cache can be used to keep tracking already opened resources and avoiding excessive case-insensitive look-ups, like cfile's Libraries variable does for entries of hog-files.

Regarding cf_FindRealFileNameCaseInsensitive - let's keep it under cfile submodule as is, with suggested design alterations it may be used with minimal changes.

@Jayman2000
Copy link
Contributor Author

I would like to correct some points of PR. It's rather big feature request.

Finally, someone is reading my code!


Before I start implementing your suggestions, I want to make sure that I fully understand them.

Let's try to implement feature in one of already exist submodule. As it's more file access and discovery thing, I suggest to use cfile.

When I move all of the stuff from the gamedata module to the cfile module, should the “Move Base_directory into separate module” commit be changed so that it moves Base_directory into the cfile module?

We don't have much writable paths in to be defined (actually is one - location in user's home dir), so we can inject proper path in cfopen() with checking "w" flag option. For reading we can use list of paths and hog-files specified in not yet implemented cf_Init() function. In this way cfopen() should always accepts relative paths - "d3.hog", "missions/d3.msn", "movies/intro.mve" etc.

I think that you’re saying two things here:

  1. cfopen() should do something like this:

    CFILE *cfopen(const std::filesystem::path &relative_path, const char *mode) {
        //
        std::filesystem::path full_path;
        if (mode[0] == 'w') {
            // cf_GetWritableBaseDirectory is the new name for gd_GetWritableBaseDirectory.
            full_path = cf_GetWritableBaseDirectory() / relative_path;
        } else if (mode[0] == 'r') {
            // cf_LocatePath is the new name for gd_LocatePath.
            full_path = cf_LocatePath(relative_path);
        }
        //
    }
  2. gd_Init() should be moved to the cfile module and renamed to cf_Init().

Am I understanding correctly?

I thinking about using some cache path resolver that adds already resolved paths into LRU cache or similar by lowercased relative path filename. This cache can be used to keep tracking already opened resources and avoiding excessive case-insensitive look-ups, like cfile's Libraries variable does for entries of hog-files.

So you’re saying that you might open another PR that optimizes Descent 3’s case insensitive path code further?

Regarding cf_FindRealFileNameCaseInsensitive - let's keep it under cfile submodule as is, with suggested design alterations it may be used with minimal changes.

Yeah, but how am I going to make those minimal changes? Like I said previously:

It would be nice if that faster code could somehow be incorporated into gd_LocatePath(), but I don’t know how to do that because I don’t really understand the code that’s in cf_FindRealFileNameCaseInsensitive().

@winterheart
Copy link
Collaborator

  1. and 2. are correct. Only thing is that we should get away sometime from Base_directory to list of base directories (readonly and writable).

I'm planning heavily rewrite cfile / ddio modules (related to file operations), I still see some optimization points here to improve overall performance. Path cache is one of them. Currently we are about on halfway of this refactoring :).

Main issue of case insensitive discovering is that can't be optimized much. On each level of directories directory iterator loses so much time, making whole traversal incredibly slow. Optimization of cf_FindRealFileNameCaseInsensitive() is based in fact that Base_directory is already known to us, and we only have to resolve one-two sublevels of directory path. So that why I'm stressing that we should use relative paths on resource loading, this is one of ways to optimize case-insensitive mess.

@Jayman2000
Copy link
Contributor Author

Jayman2000 commented Sep 3, 2024

  1. and 2. are correct.

OK. In that case, I’ll move everything to the cfile module like you asked, but I’m not going to make the requested cfopen() change. It’s just too much work to hunt down and change every absolute-path cfopen() call, especially since I already have a working implementation of -additionaldir that doesn’t depend on that change. I say that it’s too much work because I’m fed up with all of the extra stuff that I’ve had to do in order to implement -additionaldir. For example, I had to rebase this PR because you decided that your other PR should come before this one. As part of that rebase, I had to create a new module and move all of my code out of an existing module into that new module. I now have to move all of my code out of that new module and back into an existing module. As part of that previous rebase, I also had to change every instance of example_path.string().c_str() to example_path.u8string().c_str() in order to match the code that you introduced in that other PR. I also had file, argue for and fix this issue in order to make sure that adding additional calls to the u8string() method doesn’t introduce additional regressions.

Those are just some examples. There’s a lot of other stuff that I’ve had to deal with in order to finish this PR. The thought of having to hunt down and change every absolute-path cfopen() call in this PR makes me want to start crying and vomiting, especially when I look back at how unsupportive the Descent 3 development community has been. I do think that the cfopen() change is worth making, but I think that it should be made in a separate PR.


Only thing is that we should get away sometime from Base_directory to list of base directories (readonly and writable).

It sounds like you’re saying that you agree with the changes in this PR. Specifically, it sounds like you agree with the parts of this PR that replace char Base_directory[_MAX_PATH] with std::vector<std::filesystem::path> Base_directories. It sounds like you also agree with parts of this PR that cause the path to the writable base directory and the paths to all of the read-only base directories to be stored in the Base_directories variable. That being said, I don’t think that you successfully answered this question:

When I move all of the stuff from the gamedata module to the cfile module, should the “Move Base_directory into separate module” commit be changed so that it moves Base_directory into the cfile module?


I'm planning heavily rewrite cfile / ddio modules (related to file operations), I still see some optimization points here to improve overall performance. Path cache is one of them. Currently we are about on halfway of this refactoring :).

Main issue of case insensitive discovering is that can't be optimized much. On each level of directories directory iterator loses so much time, making whole traversal incredibly slow.

I don’t think that you understood this question that I asked:

So you’re saying that you might open another PR that optimizes Descent 3’s case insensitive path code further?

Here’s my motivation for asking that question. Sometimes, you mention things that could be done but haven’t been done yet. Here are some examples:

When someone comments on my PR, I expect the comments to fall into one of four categories: approvals, disapprovals, questions about the PR or suggestions for how the PR can be improved. Some of your comments don’t fall into any of those categories so I don’t know what to do with them. In the past, I tried asking you how one of those comments affects my PR, but you never responded.


Optimization of cf_FindRealFileNameCaseInsensitive() is based in fact that Base_directory is already known to us, and we only have to resolve one-two sublevels of directory path.

This is really helpful. Part of the reason why I didn’t understand cf_FindRealFileNameCaseInsensitive() is because I expected it to handle an arbitrary number of subdirectories. The code makes a lot more sense now that I know that it’s not supposed to handle an arbitrary number of subdirectories on purpose.


So that why I'm stressing that we should use relative paths on resource loading, this is one of ways to optimize case-insensitive mess.

If that’s the case, then I think that you’ll like the changes in this PR’s “[WIP] Consolidate case-sensitive filesystem functions” commit.

@Jayman2000 Jayman2000 force-pushed the additionaldir-arg branch 2 times, most recently from cece3f5 to c08e82a Compare September 12, 2024 16:14
@Jayman2000
Copy link
Contributor Author

I pushed a new version of this PR that resolves merge conflicts. I haven’t gotten rid of the gamedata module yet. I’ll start work on moving everything from gamedata to cfile once I get an answer to my question about the “Move Base_directory into separate module” commit.

@winterheart
Copy link
Collaborator

I pushed a new version of this PR that resolves merge conflicts. I haven’t gotten rid of the gamedata module yet. I’ll start work on moving everything from gamedata to cfile once I get an answer to my question about the “Move Base_directory into separate module” commit.

Base_directory (or list of them) should be in cfile and should be accessed (when possible) via submodule's functions. Direct access to variable should be avoidable.

@Jayman2000
Copy link
Contributor Author

Base_directory (or list of them) should be in cfile[…]

OK. I just pushed a new version of this PR that restores the code that was in cf_FindRealFileNameCaseInsensitive(), moves everything that was in gamedata to cfile, fixes the merge conflicts, replaces the mprintf() calls with LOG_WHATEVER and makes a few improvements to comments and commit messages.

[…]and should be accessed (when possible) via submodule's functions. Direct access to variable should be avoidable.

I feel like this PR already did that by the time that you left that comment. When you left that comment, this PR added functions named gd_LocatePath(), gd_LocateMultiplePaths() and gd_GetWritableBaseDirectory(), and this PR always accessed Base_directories through those functions (with only one exception). The new version of this PR does the same thing, but those functions have been renamed to cf_LocatePath(), cf_LocateMultiplePaths() and cf_GetWritableBaseDirectory().

What do you think? Do you think that the way that this PR uses those functions counts as avoiding direct access to the Base_directories variable?

@Jayman2000 Jayman2000 marked this pull request as ready for review September 22, 2024 13:48
Copy link
Collaborator

@winterheart winterheart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested PR and generally it's good. Please resolve request changes and merge conflicts before we can retest it and finally merge to main branch.

Descent3/init.cpp Outdated Show resolved Hide resolved
Descent3/multi_dll_mgr.cpp Outdated Show resolved Hide resolved
cfile/cfile.cpp Outdated Show resolved Hide resolved
cfile/cfile.cpp Outdated Show resolved Hide resolved
#endif

std::vector<std::filesystem::path> cf_LocatePathMultiplePathsHelper(std::filesystem::path relative_path, bool stop_after_first_result) {
ASSERT(("realative_path should be a relative path.", relative_path.is_relative()));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't have to use ASSERTs here since you just can resolve relative paths with std::filesystem::absolute().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t understand how I could replace the ASSERTs with code that uses std::filesystem::absolute(). Could you post a diff showing what you would do here?

Descent3/init.cpp Outdated Show resolved Hide resolved
Descent3/multi_dll_mgr.cpp Show resolved Hide resolved
ddio/sdlcontroller.cpp Outdated Show resolved Hide resolved
netcon/includes/con_dll.h Outdated Show resolved Hide resolved
netcon/includes/mdllinit.h Outdated Show resolved Hide resolved
Before this change, API.vp[5] was cast to a pilot pointer and then cast
to a char pointer immediately afterwards. Nothing gets done to API.vp[5]
while it’s a pilot pointer, so we can remove the pilot pointer cast
without changing any behavior.
The main motivation behind this commit is to make it easier to create a
future commit. That future commit will modify code in CheckHogFile(). If
I don’t deduplicate the code now, then I’ll have to edit the same lines
of code in multiple different places.
The main motivation behind this commit is to make it easier to create a
future commit. That future commit will change how CheckHogFile() finds
the path for d3.mn3 and d3_2.mn3. Simplifying CheckHogFile() now
decreases the amount of code that needs to be changed in that future
commit.
Before this change, mve_Init() took two arguments. Neither of the
arguments were ever used. This commit simplifies some code by removing
those unused arguments.

The main motivation behind this commit is to make it easier to make a
future commit. One of the arguments that this commit removes was named
dir. dir was a path to the movies/ directory. The future commit will
make it so that there can be more than one movies/ directory. Removing
dir means that I won’t have to mess with dir when I create that future
commit.
Before this change, the Base_directory variable would get accessed
before we set its value. This seems to be OK. I’m not sure about the
details, but it seems like C++ guarantees that Base_directory will be
filled with zeros when it’s created.

That being said, the code that was modified by this commit used to be
misleading. The code would copy the contents of the Base_directory
variable into another variable named path. If you read the code, you
would think that path would be set to something along the lines of
"C:\\Games\\Descent3" or "/home/username/D3-open-source", but in
reality, path would be set to "". This change makes it clear that path
is guaranteed to be set to "".

The main motivation behind this commit is to make it easier to create a
future commit. That future commit will replace Base_directory with a new
variable named Base_directories. Base_directories will have a different
data type that does not get filled with zeros by default. In other
words, we need to set the new Base_directories variable before we use
it. Unfortunately, neither the current Base_directory variable nor the
future Base_directories variable will get set in time. Both of them will
be set after path variable gets set. This commit allows me to not worry
about that detail when I create the future Base_directories commit.
Before this change, Base_directory was a char array. In general, it’s
better to use an std::filesystem::path to represent paths. Here’s why:

• char arrays have a limited size. If a user creates a file or directory
that has a very long path, then it might not be possible to store that
path in a given char array. You can try to make the char array long
enough to store the longest possible path, but future operating systems
may increase that limit. With std::filesystem::paths, you don’t have
to worry about path length limits.

• char arrays cannot necessarily represent all paths. We try our best to
use UTF-8 for all char arrays [1], but unfortunately, it’s possible to
create a path that cannot be represent using UTF-8 [2]. With an
std::filesystem::paths, stuff like that is less likely to happen because
std::filesystem::path::value_type is platform-specific.

• char arrays don’t have any built-in mechanisms for manipulating paths.
Before this commit, the code would work around this problem in two
different ways:

  1. It would use Descent 3–specific functions that implement path
  operations on char arrays/pointers (for example, ddio_MakePath()).
  Once all paths use std::filesystem::path, we’ll be able to simplify
  the code by removing those functions.

  2. It would temporarily convert Base_directory into an
  std::filesystem::path. This commit simplifies the code by removing
  those temporary conversions because they are no longer necessary.

[1]: 7cd7902 (Merge pull request DescentDevelopers#494 from Jayman2000/encoding-improvements, 2024-07-20)
[2]: <rust-lang/rust#12056>
The main motivation behind this commit is to make it easier to create a
future commit. That future commit will will take multiple different
functions from throughout the codebase and replace them with a new
function named cf_LocatePath().

One of the functions that will get replaced is
cf_FindRealFileNameCaseInsensitive(). There are tests for
cf_FindRealFileNameCaseInsensitive() in cfile/tests/cfile_tests.cpp.
When I make that future commit, I will have to change the tests in
cfile/test/cfile_tests.cpp so that they test the cf_LocatePath()
function instead of the cf_FindRealFileNameCaseInsensitive() function.

There is an important difference between cf_LocatePath() and
cf_FindRealFileNameCaseInsensitive(). cf_LocatePath() depends on the
Base_directory variable. cf_FindRealFileNameCaseInsensitive() does not.
In order to update the tests so that they use cf_LocatePath(), I need to
make sure that the tests have access to the Base_directory variable.

Before this change, the Base_directory variable was declared in
Descent3/init.cpp. That meant that if a program wanted to access
Base_directory, then it would have to link to Descent3/init.cpp. In
other words, we would have to link to Descent3/init.cpp when compiling
the program that currently tests cf_FindRealFileNameCaseInsensitive()
but will test cf_LocatePath() in the future. I tried making that program
link to Descent3/init.cpp, but I gave up after a wile. Descent3/init.cpp
depends on a lot of other things in the codebase.

In order to make it easier to create that future commit, this commit
moves the Base_directory variable into the cfile module. When I create
that future commit, I won’t have to mess with anything linking related
because the cfile tests already link to the cfile module. Additionally,
this change will make compiling that test program more efficient.
There’s not need for the compiler to look at the entirety of
Descent3/init.cpp just because we need a single variable from it.
The main motivation behind this commit is to make it easier to create a
future commit. That futures commit will rename the
cf_FindRealFileNameCaseInsensitive() function and change its code
slightly. I was struggling to create that future commit because I found
the code in cf_FindRealFileNameCaseInsensitive() difficult to
understand. This change will make it easier to create that future commit
by making the code in cf_FindRealFileNameCaseInsensitive() easier to
understand.
Descent 3 is case-insensitive. It doesn’t matter if a file is named
“ppics.hog” or “PPPICS.HOG”. Descent 3 will load the file regardless. In
order to accomplish this, Descent 3 has to have special code for
case-sensitive filesystems. That code must take a fake case-insensitive
path and turn it into a real case-sensitive path.

Before this change, there was multiple chunks of code that helped turn
fake case-insensitive paths into real case-sensitive paths. There was
cf_FindRealFileNameCaseInsenstive(), mve_FindMovieFileRealName() and a
chunk of code in open_file_in_directory() that only exists if __LINUX__
is defined. This removes each of those pieces of code and replaces them
with a new cf_LocatePath() function.

Using the new cf_LocatePath() function has two main advantages over the
old way of doing things. First, having a single function is simpler than
having three different pieces of code. Second, the new cf_LocatePath()
function will make it easier to create a future commit. That future
commit will make Descent 3 look for files in more than just the -setdir
directory. Having a single function that’s responsible for determining
the true path of a file will make it much easier to create that future
commit.
Before this change, there were two functions in Descent3/menu.cpp that
had parameters named base_directory. base_directory wasn’t a very good
name for those variables. We have another variable declared in
cfile/cfile.cpp named Base_directory. Confusingly, the Base_directory
variable and the base_directory parameters both referred to different
things.

For example, let’s say that a user installed Descent 3 into
/home/username/D3-open-source. In that case, the Base_directory variable
would be set to /home/username/D3-open-source and the base_directory
parameters would be set to /home/username/D3-open-source/missions.

To make the code easier to understand, this commit renames both of the
base_directory parameters to “missions_directory”.
Before this change, the FindArg() function was well suited for finding
command-line options that only appear once. It’s very resonable for
FindArg() to only support arguments that appear one time because Descent
3 doesn’t have any command-line options that should be specified
multiple times. For example, it would be pretty pointless to do
something like this:

  Descent3 -useexedir -useexedir

or something like this:

  Descent3 -aspect 1.0 -aspect 1.6

It does, however, sometimes makes sense to repeat command-line options for
other applications. For example, you can specify -e multiple times when
running grep [1]:

  grep -e pattern1 -e pattern2 file.txt

The main motivation behind this change is to make it easier to create a
future commit. That future commit will add a command-line option named
“-additionaldir”. -additionaldir will be similar to grep’s -e flag. In
other words, it will make sense to do this:

  Descent3 -additionaldir /home/user/dir1 -additionaldir /home/user/dir2

Adding a start parameter to FindArg() now will make it easier for that
future commit parse the multiple occurrences of -additionaldir.

Unfortunately, there is one drawback to this change. In Descent3/args.h,
I gave the start parameter a default value of 1. Giving the start
parameter a default value allowed me to add the start parameter without
having to change most of the calls to the FindArg() function. There was
one situation where I had to change how FindArg was called, though.
DLLFindArg is a pointer to the FindArg() function. You cannot give
function pointers default arguments [2]. As a result,
FindArg("-someargument") works, but DLLFindArg("-someargument") does
not. Instead, you have to write DLLFindArg("-someargument", 1) which is
a little bit annoying.

[1]: <https://www.gnu.org/software/grep/manual/html_node/Matching-Control.html>
[2]: <https://stackoverflow.com/a/9760710/7593853>
Before this change, Descent 3 would look for all of its game data files
in a single directory. This change allows users to spread out Descent
3’s game data over multiple directories.

Building Descent 3 produces multiple files that can be freely
redistributed (Descent3, d3-linux.hog, online/Direct TCP~IP.d3c, etc.).
Running Descent 3 requires those files and several additional files that
cannot be freely redistributed. Before this change, the files that were
redistributable had to be in the same directory as the files that were
not redistributable. This change makes it so that they can be in
separate directories.

The main motivation behind this change is to allow people to package
Descent 3 for Linux in a reasonable manner. For the most part, binary
packages for Descent 3 will contain all of the freely redistributable
components. Package managers will copy those components into system
directories that are owned by root and that users probably shouldn’t
edit manually. Users will then create a new directory and copy the game
data from their copy of Descent 3 into that new directory. Users will
then be able to run:

  Descent3 -setdir <path-to-proprietary-files> -additionaldir <path-to-open-source-files>

The -additionaldir option can also be used to support more complicated
scenarios. For example, if the user is using Debian’s
game-data-packager [1], then they would do something like this:

  Descent3 -setdir <path-to-writable-directory> -additionaldir <path-to-gdp-directory> -additionaldir <path-to-open-source-files>

The -additionaldir option can also be used to load a mod that replaces
.hog files:

  Descent3 -setdir <path-to-base-game-data> -additionaldir <path-to-mod-files>

[1]: <DescentDevelopers#373 (comment)>
@winterheart winterheart self-requested a review October 5, 2024 01:22
@winterheart
Copy link
Collaborator

Tested on Linux and Windows, and generally changes works as intended. Thanks for patience and contribution.

@winterheart winterheart merged commit afd5a49 into DescentDevelopers:main Oct 5, 2024
10 checks passed
@Jayman2000 Jayman2000 deleted the additionaldir-arg branch October 5, 2024 17:14
@Lgt2x
Copy link
Member

Lgt2x commented Oct 6, 2024

Thanks for this improvement, big step forward for file management!

Jayman2000 added a commit to Jayman2000/nixpkgs-pr that referenced this pull request Nov 13, 2024
By default, Descent 3 will only look for game files in the current
working directory. In order to function properly, Descent 3 needs to
look for some of its files in the Nix store. This commit creates a
wrapper around the main Descent3 executable that automatically looks in
the correct path in the Nix store.

Hopefully, this wrapper will only exist temporarily. I have an
unfinished Descent 3 pull request that adds a DEFAULT_ADDITIONAL_DIRS
CMake option [1]. Once I can finish that pull request
(DescentDevelopers/Descent3#623 needs to get merged first), we’ll be
able to get rid of this wrapper and use DEFAULT_ADDITIONAL_DIRS instead.
Additionally, if DescentDevelopers/Descent3#628 gets merged first, then
that pull request will also probably make this wrapper unnecessary.

[1]: <DescentDevelopers/Descent3#471>
Jayman2000 added a commit to Jayman2000/nixpkgs-pr that referenced this pull request Nov 14, 2024
By default, Descent 3 will only look for game files in the current
working directory. In order to function properly, Descent 3 needs to
look for some of its files in the Nix store. This commit creates a
wrapper around the main Descent3 executable that automatically looks in
the correct path in the Nix store.

Hopefully, this wrapper will only exist temporarily. I have an
unfinished Descent 3 pull request that adds a DEFAULT_ADDITIONAL_DIRS
CMake option [1]. Once I can finish that pull request
(DescentDevelopers/Descent3#623 needs to get merged first), we’ll be
able to get rid of this wrapper and use DEFAULT_ADDITIONAL_DIRS instead.
Additionally, if DescentDevelopers/Descent3#628 gets merged first, then
that pull request will also probably make this wrapper unnecessary.

[1]: <DescentDevelopers/Descent3#471>
Jayman2000 added a commit to Jayman2000/nixpkgs-pr that referenced this pull request Nov 15, 2024
By default, Descent 3 will only look for game files in the current
working directory. In order to function properly, Descent 3 needs to
look for some of its files in the Nix store. This commit creates a
wrapper around the main Descent3 executable that automatically looks in
the correct path in the Nix store.

Hopefully, this wrapper will only exist temporarily. I have an
unfinished Descent 3 pull request that adds a DEFAULT_ADDITIONAL_DIRS
CMake option [1]. Once I can finish that pull request
(DescentDevelopers/Descent3#623 needs to get merged first), we’ll be
able to get rid of this wrapper and use DEFAULT_ADDITIONAL_DIRS instead.
Additionally, if DescentDevelopers/Descent3#628 gets merged first, then
that pull request will also probably make this wrapper unnecessary.

[1]: <DescentDevelopers/Descent3#471>
mkg20001 pushed a commit to mkg20001/nixpkgs that referenced this pull request Dec 17, 2024
By default, Descent 3 will only look for game files in the current
working directory. In order to function properly, Descent 3 needs to
look for some of its files in the Nix store. This commit creates a
wrapper around the main Descent3 executable that automatically looks in
the correct path in the Nix store.

Hopefully, this wrapper will only exist temporarily. I have an
unfinished Descent 3 pull request that adds a DEFAULT_ADDITIONAL_DIRS
CMake option [1]. Once I can finish that pull request
(DescentDevelopers/Descent3#623 needs to get merged first), we’ll be
able to get rid of this wrapper and use DEFAULT_ADDITIONAL_DIRS instead.
Additionally, if DescentDevelopers/Descent3#628 gets merged first, then
that pull request will also probably make this wrapper unnecessary.

[1]: <DescentDevelopers/Descent3#471>
mkg20001 pushed a commit to mkg20001/nixpkgs that referenced this pull request Dec 17, 2024
By default, Descent 3 will only look for game files in the current
working directory. In order to function properly, Descent 3 needs to
look for some of its files in the Nix store. This commit creates a
wrapper around the main Descent3 executable that automatically looks in
the correct path in the Nix store.

Hopefully, this wrapper will only exist temporarily. I have an
unfinished Descent 3 pull request that adds a DEFAULT_ADDITIONAL_DIRS
CMake option [1]. Once I can finish that pull request
(DescentDevelopers/Descent3#623 needs to get merged first), we’ll be
able to get rid of this wrapper and use DEFAULT_ADDITIONAL_DIRS instead.
Additionally, if DescentDevelopers/Descent3#628 gets merged first, then
that pull request will also probably make this wrapper unnecessary.

[1]: <DescentDevelopers/Descent3#471>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants