Soft dependencies for resources

I want to suggest adding soft dependencies for resources. Soft dependencies work just like normal dependencies, but they are not required to start a resource. Soft dependencies are actually quite common in software, for instance in Magento and, more closely related to game plugins, the Bukkit plugin.yml.

To implement soft dependencies I want to add a new manifest key soft_dependency (and the plural soft_dependencies). These dependencies would only contain resources, as runtime constraints don’t make a lot of sense to have as optional dependencies.

The way that I think the soft dependencies should be handled is as follows:

  1. Try to start a resource.
  2. First handle the hard dependencies indicated in dependency or dependencies as was done before. If one is missing the resource would not start and soft dependencies don’t need to be checked either.
  3. Then check the soft dependencies by going through each one just like normal dependencies.
    • If the resource exists, we try to start it.
    • If not, we print that the soft dependency is missing and, instead of cancelling the loading of the resource, we continue to the next soft dependency.
    • If the soft dependency fails to start, we print that the soft dependency failed to start and, just like with missing soft dependencies, move on to the next soft dependency.
  4. After all dependencies are processed we start the actual resource, just like we did before.

Now there are some important caveats for the implementation.

  1. The soft dependencies do not get stored in resourceDependencies, because if the resource fails to start we treat it as a missing resource. There is no point in waiting for the soft dependency to start properly as our own resource will work fine without it (albeit missing some features).
  2. Soft dependencies also don’t get stored in resourceDependants. While I was thinking it might be beneficial for soft dependants to stop once a soft dependency stops, the soft dependency should not dictate what our resource does. Since the resource runs fine without the soft dependency, it should also run fine when it stops. Leaving it up to the resource author to handle stopped resources. (i.e. listening to onResourceStopped and unloading whatever you wanted to do with that resource or checking the resource state before making use of it)

But why would we want this? Well let’s take a simple example. Say I have a resource that has a location where players can interact with an NPC. By default you would walk up to the NPC and press E to start a mission or do some other action. But a lot of server owners nowadays prefer to use a targeting library like ox_target. They would enable the configuration variable to make use of the targeting system (if you have that), and based on this your script would register the targeting options for the NPC through the export ox_target provides, instead of creating a loop with distance checks and everything needed otherwise. But oh no! Ox_target is set up to start way after your script runs, and now there is an error saying there is no such export available in ox_target. Server owners might not understand this and create help topics etc.

So to prevent this, you add ox_target as a dependency to your resource. This ensures that it starts before your resource and the export is available. But now there is a server that does not like targeting, and does not have ox_target installed. Your script won’t run on this server, because ox_target is a hard dependency. So to solve this, you provide documentation to tell server owners to comment out the ox_target dependency if they don’t want to use that (or code it in if they do). As Linden said here: Using logic for resource dependencies - #6 by Linden

Obviously, this is not a nice solution. You’re relying on people who might not know anything about programming to change the manifest for your resource. Countless issues could happen (like missing quotes or whatever), and we don’t want that. So that’s where the soft dependency comes in. Mark ox_target as a soft dependency, and the resource loader will make sure it is started before your resource, but only if it exists.

The example might not fully apply for the use case the way I describe it, but the main idea is that your resource might benefit from having access to another resource (in this case ox_target) to provide additional functionality.

So why can’t we just use GetResourceState(resourceName) == "started"? Well, the soft dependency might not be started yet. Using the soft dependency implementation I propose ensures that they are started, after which you can safely use exports (and events). Keep in mind that you still need to use GetResourceState (in case of exports) to validate that the soft dependency was actually present and started.

Now similar suggestions have been made in Resource dependency constraints and Using logic for resource dependencies, since these suggestions are somewhat similar I wanted to link these for reference.

Especially Linden’s suggestion is quite similar to this one, except using special notation in the dependencies instead of a new manifest key. However, to me it makes more sense to have a separate manifest key. This is also what is used for client_scripts and server_scripts. Similarly, a server_dependency (and server_dependencies) manifest entry could be added as I definitely see a use case for that, just not the point of this suggestion.

I implemented my proposal for soft dependencies and I’ve opened a PR to the github repository. You can find this here: Soft dependency support for resources by r3ps4J · Pull Request #2920 · citizenfx/fivem · GitHub

1 Like

For anyone who wants to play around with this functionality early, I’ve made an experimental resource to mimick this behaviour. You can find this resource in this repository: GitHub - r3ps4J/fivem-experiments

To download it head over to the releases section and download experiment_soft_dependencies.zip.