예제 #1
0
        private async Task LoadAddonAsync(string path, Action <AddonLoadData> onLoadedMethod)
        {
            AddonLoadContext context = new AddonLoadContext();

            context.Path             = path;
            context.ResourcePriority = ResourcePriority.Addon;

            context.AddonManager     = this;
            context.OnLoadedCallback = onLoadedMethod;

            context.Manifest = ReadAddonManifest(path);

            if (context.Manifest.IgnoreSingleFileErrors)
            {
                context.AbortOnSingleFileFailure = false;
            }

            LoadMainAssembly(context);
            CreateAddonBase(context);

            await context.AddonBase.LoadAddonAsync(context);

            //if we get here, loading has succeeded
            LoadedAddons.Add(context.Manifest.Name, context);
        }
예제 #2
0
        private void CreateAddonBase(AddonLoadContext context)
        {
            if (context.MainAssembly != null)
            {
                var types = context.MainAssembly.GetTypes();

                //scan for addonbase derived type, instantiate
                var abType = types.FirstOrDefault(t => t.IsSubclassOf(typeof(AddonBase)));

                if (abType != null)
                {
                    context.AddonBaseType = abType;
                }
                else
                {
                    context.AddonBaseType = typeof(AddonBase); //if there is no addonbase derived type just use addonbase
                }
            }
            else
            {
                context.AddonBaseType = typeof(AddonBase);
            }

            context.AddonBase = (AddonBase)Activator.CreateInstance(context.AddonBaseType);
        }
예제 #3
0
        public virtual async Task LoadAddonAsync(AddonLoadContext context)
        {
            InitialLoadContext = context;
            LocalResourcePath  = context.MountPath;
            await context.AddonManager.LoadAssembliesAsync(context);

            await context.AddonManager.LoadResourcesFromPathAsync(context);

            context.AddonManager.RegisterLoadedScenes(context);
            RunOnLoadedCallback(context);
        }
예제 #4
0
        private void LoadMainAssembly(AddonLoadContext context)
        {
            if (!string.IsNullOrEmpty(context.Manifest.MainAssembly)) //we do allow addons with no assembly
            {
                string dllPath  = Path.Combine(context.Path, "managed", context.Manifest.MainAssembly + ".dll");
                var    assembly = Assembly.LoadFile(dllPath);
                context.MainAssembly = assembly;
                context.LoadedAssemblies.Add(assembly);

                if (ConfigState.Instance.UseVerboseLogging)
                {
                    Debug.Log($"[AddonManager] Loaded assembly {assembly.FullName}");
                }
            }
        }
예제 #5
0
        public async Task LoadStreamingAssetsAsync(Action <AddonLoadData> onLoadedMethod)
        {
            if (!(CoreParams.LoadAddons && CanLoadAddons))
            {
                return;
            }

            var context = new AddonLoadContext();

            //set various things in context
            context.Path                     = CoreParams.StreamingAssetsPath;
            context.AddonManager             = this;
            context.MountPathOverride        = "Streaming";
            context.ResourcePriority         = ResourcePriority.Streaming;
            context.AbortOnSingleFileFailure = false;

            await LoadResourcesFromPathAsync(context);

            onLoadedMethod(new AddonLoadData(context.LoadedAssemblies, context.LoadedResources));
        }
예제 #6
0
        public async Task LoadAssembliesAsync(AddonLoadContext context)
        {
            string dirPath = Path.Combine(context.Path, "managed");

            if (Directory.Exists(dirPath))
            {
                var dirEnumerable = Directory.EnumerateFiles(dirPath);
                foreach (var file in dirEnumerable)
                {
                    try
                    {
                        if (!Path.GetFileNameWithoutExtension(file).Equals(context.Manifest.MainAssembly) && Path.GetExtension(file).Equals(".dll", StringComparison.OrdinalIgnoreCase))
                        {
                            //okay to load assembly!
                            var assembly = Assembly.LoadFile(file);
                            context.LoadedAssemblies.Add(assembly);

                            if (ConfigState.Instance.UseVerboseLogging)
                            {
                                Debug.Log($"[AddonManager] Loaded assembly {assembly.FullName}");
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.LogError($"[AddonManager] Failed to load assembly \"{file}\" ({e.GetType().Name})");
                        if (ConfigState.Instance.UseVerboseLogging)
                        {
                            Debug.LogException(e);
                        }
                        if (context.AbortOnSingleFileFailure)
                        {
                            throw e;
                        }
                    }

                    await Task.Yield(); //don't try to load every dll in one frame
                }
            }
        }
예제 #7
0
        //TODO resilience, or should we just throw?

        public async Task LoadResourcesFromPathAsync(AddonLoadContext context)
        {
            //load elocal, then expand
            //load assetbundles, then loose files

            string elocalTargetPath = context.MountPath;
            string elocalPath       = Path.Combine(context.Path, "elocal");
            string elocalBundlePath = Path.Combine(context.Path, "elocal.assetbundle");

            await tryLoadResourceFromAssetBundleAsync(elocalBundlePath, elocalTargetPath);
            await LoadResourcesInFolderRecurseAsync(context, elocalPath, elocalTargetPath);

            string expandTargetPath = ""; //I think this is actually correct
            string expandPath       = Path.Combine(context.Path, "expand");
            string expandBundlePath = Path.Combine(context.Path, "expand.assetbundle");

            await tryLoadResourceFromAssetBundleAsync(expandBundlePath, expandTargetPath);
            await LoadResourcesInFolderRecurseAsync(context, expandPath, expandTargetPath);

            async Task tryLoadResourceFromAssetBundleAsync(string bundlePath, string targetPath)
            {
                try
                {
                    await LoadResourcesFromAssetBundleAsync(context, bundlePath, targetPath);
                }
                catch (Exception e)
                {
                    Debug.LogError($"Failed to load assetbundle \"{bundlePath}\" ({e.GetType().Name})");
                    if (ConfigState.Instance.UseVerboseLogging)
                    {
                        Debug.LogException(e);
                    }
                    if (context.AbortOnSingleFileFailure)
                    {
                        throw e;
                    }
                }
            }
        }
예제 #8
0
        private async Task LoadResourcesFromAssetBundleAsync(AddonLoadContext context, string bundlePath, string targetPath)
        {
            if (!File.Exists(bundlePath))
            {
                return;
            }

            if (ConfigState.Instance.UseVerboseLogging)
            {
                Debug.Log($"loading assetbundle \"{bundlePath}\" to \"{targetPath}\"");
            }

            //load from AssetBundles
            var bundleLoadRequest = AssetBundle.LoadFromFileAsync(bundlePath);

            while (!bundleLoadRequest.isDone)
            {
                await Task.Yield(); //ugly but should work
            }
            var assetBundle = bundleLoadRequest.assetBundle;

            if (assetBundle == null)
            {
                Debug.LogError($"[AddonManager] failed to load assetbundle \"{bundlePath}\"");
                throw new FileLoadException("Failed to load assetbundle"); //TODO change this to a more appropriate exception
            }

            var scenes = assetBundle.GetAllScenePaths();

            if (ConfigState.Instance.UseVerboseLogging && scenes != null && scenes.Length > 0)
            {
                Debug.Log(scenes.ToNiceString());
            }

            if (scenes != null && scenes.Length > 0)
            {
                context.LoadedScenes.AddRange(scenes);
            }

            var names = assetBundle.GetAllAssetNames();

            if (ConfigState.Instance.UseVerboseLogging && names != null && names.Length > 0)
            {
                Debug.Log(names.ToNiceString());
            }

            if (names != null && names.Length > 0)
            {
                bool useAssetBundlePaths = false;
                if (context.Manifest != null && context.Manifest.UseAssetBundlePaths)
                {
                    Debug.LogWarning($"[AddonManager] Addon {context.Manifest.Name} is set to use full asset bundle paths, which is considered experimental!");
                    //throw new NotImplementedException("Full asset bundle paths are not yet supported"); //we can actually add this with minimal difficulty

                    //so we'll just use the full paths from the asset bundle minus the "assets/" part and tack that on after the targetPrefix
                    //which will mount elocal.assetbundle at Addons/<package name> and expand.assetbundle at the root
                    //and really you should either use two pathed assetbundles or a lot of flat assetbundles, not multiple pathed assetbundles

                    useAssetBundlePaths = true;
                }

                string targetPrefix = targetPath.Replace('\\', '/');
                if (!targetPrefix.EndsWith("/", StringComparison.Ordinal))
                {
                    targetPrefix = targetPrefix + "/";
                }

                foreach (var name in names)
                {
                    string objectName       = useAssetBundlePaths ? GetObjectPartialPathFromBundledName(name) : GetObjectNameFromBundledName(name);
                    string objectTargetPath = targetPrefix + objectName;

                    var rh = await CCBase.ResourceManager.AddResourceFromAssetBundleAsync(objectTargetPath, name, assetBundle, context.ResourcePriority);

                    context.LoadedResources.Add(objectTargetPath, rh);
                }
            }
        }
예제 #9
0
        private async Task LoadResourcesInFolderRecurseAsync(AddonLoadContext context, string folderPath, string targetPath)
        {
            if (!Directory.Exists(folderPath))
            {
                return;
            }

            //Debug.Log($"load things in \"{folderPath}\" to \"{targetPath}\"");

            //handle both assetbundles and loose resources

            var files = Directory.EnumerateFiles(folderPath);

            foreach (var file in files)
            {
                string fileTargetPath = getTargetPath(file);

                try
                {
                    if (Path.GetExtension(file).Equals(".assetbundle", StringComparison.OrdinalIgnoreCase))
                    {
                        await LoadResourcesFromAssetBundleAsync(context, file, fileTargetPath);
                    }
                    else
                    {
                        var rh = await CCBase.ResourceManager.AddResourceFromFileAsync(fileTargetPath, file, context.ResourcePriority);

                        context.LoadedResources.Add(fileTargetPath, rh);
                    }
                }
                catch (Exception e)
                {
                    Debug.LogError($"Failed to load \"{file}\" ({e.GetType().Name})");
                    if (ConfigState.Instance.UseVerboseLogging)
                    {
                        Debug.LogException(e);
                    }
                    if (context.AbortOnSingleFileFailure)
                    {
                        throw e;
                    }
                }
            }

            var subdirs = Directory.EnumerateDirectories(folderPath);

            foreach (var subdir in subdirs)
            {
                string subdirTargetPath = getTargetPath(subdir);

                await LoadResourcesInFolderRecurseAsync(context, subdir, subdirTargetPath);
            }

            string getTargetPath(string objectPath)
            {
                string fullBasePath      = Path.GetFullPath(folderPath);
                string fullObjectPath    = Path.GetFullPath(objectPath);
                string partialObjectPath = fullObjectPath.Substring(fullBasePath.Length); //watch the off-by-ones!
                string newTargetPath     = targetPath + partialObjectPath;

                if (newTargetPath.StartsWith("/", StringComparison.OrdinalIgnoreCase) || newTargetPath.StartsWith("\\", StringComparison.OrdinalIgnoreCase))
                {
                    newTargetPath = newTargetPath.Substring(1);
                }

                if (Path.HasExtension(newTargetPath))
                {
                    newTargetPath = Path.ChangeExtension(newTargetPath, null);
                }

                newTargetPath = newTargetPath.Replace('\\', '/');

                //Debug.Log($"{targetPath} + {partialObjectPath} = {newTargetPath}");

                return(newTargetPath);
            }
        }
예제 #10
0
 public void RegisterLoadedScenes(AddonLoadContext context)
 {
     LoadedScenes.UnionWith(context.LoadedScenes);
 }
예제 #11
0
 protected void RunOnLoadedCallback(AddonLoadContext context)
 {
     context.OnLoadedCallback(new AddonLoadData(context.LoadedAssemblies, context.LoadedResources));
 }
예제 #12
0
 protected async Task LoadResources(AddonLoadContext context)
 {
     await context.AddonManager.LoadResourcesFromPathAsync(context);
 }
예제 #13
0
 protected async Task LoadAssemblies(AddonLoadContext context)
 {
     await context.AddonManager.LoadAssembliesAsync(context);
 }