Triggering a build via BuildPipeline.BuildPlayer doesn't build asset bundles

Hello,

I have a build script that I use in conjunction with Jenkins build server which uses a Build Script that calls BuildPipeline.BuildPlayer(…) to trigger builds. I’m noticing that the Addressables system doesn’t build any asset bundles when making a build in this way.

If I use the standard Build settings window and do builds that way, then the asset bundles do get built.

Is this a missing feature still to come, or do I need to use another API call to trigger the builds?

2 Likes

Look at scripts in AddressablesBuildScriptHook.cs

In short I think you need

AddressableAssetSettingsDefaultObject.Settings.ActivePlayerDataBuilder.BuildData<AddressablesPlayerBuildResult>(AddressablesBuildDataBuilderContext)

before BuildPipeline.BuildPlayer

using UnityEngine;
using UnityEngine.ResourceManagement;
using UnityEngine.AddressableAssets;
using System;
using UnityEngine.Experimental.UIElements;
using System.Collections.Generic;
using System.IO;

namespace UnityEditor.AddressableAssets
{
    /// <summary>
    /// Entry point to set callbacks for builds.
    /// </summary>
    public static class BuildScript
    {
        /// <summary>
        /// Global delegate for handling the result of AddressableAssets builds.  This will get called for player builds and when entering play mode.
        /// </summary>
        public static Action<AddressableAssetBuildResult> buildCompleted;
    }

    internal static class AddressablesBuildScriptHooks
    {
        [InitializeOnLoadMethod]
        static void Init()
        {
            BuildPlayerWindow.RegisterBuildPlayerHandler(BuildPlayer);
            EditorApplication.playModeStateChanged += OnEditorPlayModeChanged;
        }

        static void BuildPlayer(BuildPlayerOptions ops)
        {
            var settings = AddressableAssetSettingsDefaultObject.Settings;
            if (settings == null)
            {
                if (BuildScript.buildCompleted != null)
                    BuildScript.buildCompleted(new AddressableAssetBuildResult() { Duration = 0, Error = "AddressableAssetSettings not found." });
                BuildPipeline.BuildPlayer(ops);
            }
            else
            {
                if (settings.ActivePlayerDataBuilder == null)
                {
                    var err = "Active build script is null.";
                    Debug.LogError(err);

                    if (BuildScript.buildCompleted != null)
                        BuildScript.buildCompleted(new AddressableAssetBuildResult() { Duration = 0, Error = err });
                    return;
                }

                if (!settings.ActivePlayerDataBuilder.CanBuildData<AddressablesPlayerBuildResult>())
                {
                    var err = string.Format("Active build script {0} cannot build AddressablesPlayerBuildResult.", settings.ActivePlayerDataBuilder);
                    Debug.LogError(err);
                    if (BuildScript.buildCompleted != null)
                        BuildScript.buildCompleted(new AddressableAssetBuildResult() { Duration = 0, Error = err });
                    return;
                }

                var context = new AddressablesBuildDataBuilderContext(settings, ops.targetGroup, ops.target, (ops.options & BuildOptions.Development) != BuildOptions.None, ((ops.options & BuildOptions.ConnectWithProfiler) != BuildOptions.None) && ProjectConfigData.postProfilerEvents, settings.PlayerBuildVersion);
                var res = settings.ActivePlayerDataBuilder.BuildData<AddressablesPlayerBuildResult>(context);
                BuildPipeline.BuildPlayer(ops);
                if (!string.IsNullOrEmpty(res.ContentStateDataPath))
                {    
                    var newPath = ContentUpdateScript.GetContentStateDataPath(false);
                    if (File.Exists(newPath))
                          File.Delete(newPath);
                    File.Copy(res.ContentStateDataPath, newPath);
                }

                if (BuildScript.buildCompleted != null)
                    BuildScript.buildCompleted(res);
                settings.DataBuilderCompleted(settings.ActivePlayerDataBuilder, res);
            }
        }

        private static void OnEditorPlayModeChanged(PlayModeStateChange state)
        {
            if (state == PlayModeStateChange.ExitingEditMode)
            {
                var settings = AddressableAssetSettingsDefaultObject.Settings;
                if (settings == null)
                    return;
                if (settings.ActivePlayModeDataBuilder == null)
                {
                    var err = "Active play mode build script is null.";
                    Debug.LogError(err);

                    if (BuildScript.buildCompleted != null)
                        BuildScript.buildCompleted(new AddressableAssetBuildResult() { Duration = 0, Error = err });
                    return;
                }

                if (!settings.ActivePlayerDataBuilder.CanBuildData<AddressablesPlayModeBuildResult>())
                {
                    var err = string.Format("Active build script {0} cannot build AddressablesPlayModeBuildResult.", settings.ActivePlayModeDataBuilder);
                    Debug.LogError(err);
                    if (BuildScript.buildCompleted != null)
                        BuildScript.buildCompleted(new AddressableAssetBuildResult() { Duration = 0, Error = err });
                    return;
                }

                SceneManagerState.Record();
                var res = settings.ActivePlayModeDataBuilder.BuildData<AddressablesPlayModeBuildResult>(new AddressablesBuildDataBuilderContext(settings));
                SceneManagerState.AddScenesForPlayMode(res.ScenesToAdd);
                if (BuildScript.buildCompleted != null)
                    BuildScript.buildCompleted(res);
                settings.DataBuilderCompleted(settings.ActivePlayerDataBuilder, res);
            }
            else if (state == PlayModeStateChange.EnteredEditMode)
            {
                var settings = AddressableAssetSettingsDefaultObject.Settings;
                if(settings != null && settings.ActivePlayerDataBuilder != null && settings.ActivePlayerDataBuilder.CanBuildData<AddressablesPlayModeBuildResult>())
                    SceneManagerState.Restore();
            }
        }
    }
}
2 Likes

@5argon that doesn’t work for me. Anyone managed to get this working?

The script above works for me, except that sprites contained in SpriteAtlases don’t work for me when I build from script rather than from the menu. Is there a way of seeing the exact steps unity is taking when we click “Build”?

Anyone have an idea about this?

To reproduce from an empty project simply create a prefab with a Sprite Renderer component that contains a reference to a sprite that is contained in a Sprite Atlas.

Now add this prefab to an addressable group and instantiate it from a script.

When you build the game for WebGL from batch mode using the script above the sprite atlas won’t be built into the addressables correctly and the sprite won’t load in game.

In the next release, you can call AddressableAssetSettingsDefaultObject.Settings.BuildPlayerContent(). This will use the build target and group and the development flag from EditorUserBuildSettings. I will look into why there may be issues with Sprites. Also, the automatic building of bundles when a player is built (or entering play mode) has been removed. We determined that it makes more sense to have that as a separate step since it can take a long time. In the editor play mode, Fast & Virtual build scripts will still do some processing but the PackMode will rely on already built bundles.

1 Like

I’m trying to get this working now with TeamCity. Just like arvzg, if I build from the editor, everything works. As soon as I use BuildPipeline.BuildPlayer nothing works.

I have tried to manually get it to build addressables before I build the player and am able to get it to populate the StreamingAssets folder, but I must still be missing something.

Any ideas?

EDIT: I have tried using the same script I am using for Android with StandaloneWindows64 as the target and it actually works. I’m not sure what I’m doing wrong

Hi, I’m new in Addressables. I want to do build with TeamCity and i wonder about the problems mentioned above all? are they solved now?