Hello
We are trying to run a setup method when a package is imported, we found out that methods with InitializeOnLoadMethod are called when a package is imported for the first time but not when it is updated Just to clarify our use case, we are using a private scoped registry, we want to create/update a custom gradle template on the Assets/Plugins folder every time a package is imported/updated.
Is there a proper way to call a method when a package is imported and/or updated?
Hi @caioteixeira5 !
There are no existing callbacks to know when a package is installed/updated or removed
But you can effectively used a InitializeOnLoadMethod
fct to know which packages are installed.
Btw, this fct will be called at each domain reload (install/update/remove of a package but also entering play-mode)
Hereās a code snippet (works with Unity 2019.1.0b9) you may starts with:
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEditor;
using UnityEditor.PackageManager;
public static class InstalledPackages
{
[InitializeOnLoadMethod]
private static void InitializeOnLoad()
{
var listRequest = Client.List(true);
while (!listRequest.IsCompleted)
Thread.Sleep(100);
if (listRequest.Error != null)
{
Debug.Log("Error: " + listRequest.Error.message);
return;
}
var packages = listRequest.Result;
var text = new StringBuilder("Packages:\n");
foreach (var package in packages)
{
if (package.source == PackageSource.Registry)
text.AppendLine($"{package.name}: {package.version} [{package.resolvedPath}]");
}
Debug.Log(text.ToString());
}
}
Regards,
Manu73
Thank you for the quick answer!
I tested it in both Unity 2018.3 and 2019.1, but it seems to have the same behavior that I had before. This method is not called when a package is updated.
I actually have a piece of new information, this issue only happens when using a private registry. When I update an official package the method is called.
Hi @caioteixeira5 !
Sorry for this inconvenience.
I locally use Verdaccio and scoped registry in Packman and I was able to update my package published in my local registry.
The only difference I think, itās that my package contains scripts that I have updated.
Domain reload (which will call InitializeOnLoadMethod) will only occur if there are changes in scripts.
Does your package only contains assets (aka non scripts)?
Also, in Unity General preferences, do you have Auto-Refresh checked?
Manu73
Hi @caioteixeira5 !
Iāve updated the code snippet to take care of assets only change:
using System.Linq;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEditor;
using UnityEditor.PackageManager;
public class InstalledPackages : AssetPostprocessor
{
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
var inPackages = importedAssets.Any(path => path.StartsWith("Packages/")) ||
deletedAssets.Any(path => path.StartsWith("Packages/")) ||
movedAssets.Any(path => path.StartsWith("Packages/")) ||
movedFromAssetPaths.Any(path => path.StartsWith("Packages/"));
if (inPackages)
{
InitializeOnLoad();
}
}
[InitializeOnLoadMethod]
private static void InitializeOnLoad()
{
var listRequest = Client.List(true);
while (!listRequest.IsCompleted)
Thread.Sleep(100);
if (listRequest.Error != null)
{
Debug.Log("Error: " + listRequest.Error.message);
return;
}
var packages = listRequest.Result;
var text = new StringBuilder("Packages:\n");
foreach (var package in packages)
{
if (package.source == PackageSource.Registry)
text.AppendLine($"{package.name}: {package.version} [{package.resolvedPath}]");
}
Debug.Log(text.ToString());
}
}
You can adapt this code to only look in specific Packages.
Thank you!
It is working now
I am also using Verdaccio, my package has both assets and scripts. I am not sure why exactly this was not working, but modifying a hardcoded string on a script was not triggering a recompile for some reason.
Youāre welcome.
Hi @manu73 ! Is there any way to get a system like this working despite other scripts throwing errors? I tried both asmdefs and DLLs to no avail Trigger installer script on load despite other assemblies throwing errors?
What I want to do is create an āinstallerā that automatically triggers and allows the user to install the missing dependencies. If my current approach doesnāt work, how would you recommend I do it instead?
Thank you!
Hey @QFSW did you make any progress on this topic? I am looking to do the same but if there are compiler errors Unity grinds to a halt, even if there are scripts which can execute.
Thanks!
Unfortunately not
I would also love functionality like a callback as well when a custom package is installed/updated. Just adding my tiny voice to this thread.
@manu73 thanks for the code Iāll try that out.
Update: The code doesnāt seem to detect custom packages, only unity packages
Update2: figured it out. Deleting the if check for the package source found all package registries. By the way, the name is the full com.x.package name if youāre checking for a particular package. Thanks for the code!
Is it not possible to use PackageRegistrationEventArgs to detect when a Package is installed/updated/removed? I see this thread started in 2019 - could this API be relatively new? I havenāt tried it yet - Iām currently still investigating what I will need to do and found this thread from google.
@flintmech : PackageRegistryEventArgs is supported since 2020.3 (available since 2020.2). It is used by
PackageManager.Events.registeredPackages which is supported since 2020.3 (available since 2020.2) and PackageManager.Events.registeringPackages with the same availability, the former (following my own observations, no guarantee) apparently invoked when a package is being installed (i.e. after installation has finished), the latter when uninstalling a package (obviously before uninstalling).
So yes, it is (relatively) new (at least younger than the thread), but this is the way to go for recent editors.
Hi, I need a callback for when a Sample is downloaded. Any chance thereās an event for that as well? I havenāt been able to find it.
Of course, I could use the InitializeOnLoad trick but that seems hacky.