Where is the Unity IAP package located?

2019-06-08 00:43发布

问题:

I'm currently working on an editor script that will ease my transition between freemium and paid versions of my game.I would like to manually import the .unitypackage file that gets imported when I click the import button under Services -> In-App Purchasing.

I am aware of the function AssetDatabase.ImportAsset(path) but I need the path of the package first.

Thanks in advance!

回答1:

When you enable IAP and click Import, the following will happen:

1.Unity will generate a random file name with

FileUtil.GetUniqueTempPathInProject()

2.A full path will be constructed like this:

<ProjectName>Temp\FileUtil.GetUniqueTempPathInProject()

3.Unity will then add .unitypackage to the end of that random file name.

Now, you have something like:

<ProjectName>Temp\FileUtil.GetUniqueTempPathInProject()+".unitypackage";

4.IAP package will then be downloaded and stored to path from #3.

Illustration of what it looks like:

5.The file is then copied to

<ProjectName>Temp\TarGZ

It is saved with the file name generated from #2. No .unitypackage at the end like #3.

UnityEngine.Cloud.Purchasing

6.After that, Unity imports it with AssetDatabase.ImportPackage not AssetDatabase.ImportAsset(path,false) as mentioned in your question.

Now you can see where Unity IAP package is located but there are just few problems in what you are trying to do:

1.For the IAP package to be present, the import button from the Services Tab must be clicked. I am sure you want this to be automated.

2.The file name is not static therefore making the path hard to retrieve. As you can see from the image above, there are other files in this folder too. We don't know which one is the AIP Package.

3.When you re-start Unity, the temp folder will be deleted. So the downloaded IAP package will be lost.

The best way to get the Unity IAP package is to download it directly from Unity's server then save it to your preferred location. The code below will download the IAP package and store it at: <ProjectName>/UnityEngine.Cloud.Purchasing.unitypackage It will also import it and then, enable it. For AIP to work, Analytics must be enabled too. This code will also do that.

I tried to hide the AIP url so that it won't be abused and Unity won't have change it.

If you want to remake this into something else, the two most important functions to look at are downloadAndInstallAIP() and deleteAndDisableAIP().

AIPDownloader Code:

using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using UnityEditor;
using UnityEditor.Analytics;
using UnityEditor.Purchasing;
using UnityEngine;

[ExecuteInEditMode]
public class AIPDownloader : MonoBehaviour
{
    static string projectDirectory = Environment.CurrentDirectory;

    static string aipFileName = "UnityEngine.Cloud.Purchasing.unitypackage";
    static string etagName = "UnityEngine.Cloud.PurchasingETAG.text";

    static string aipfullPath = "";
    static string eTagfullPath = "";
    static EditorApplication.CallbackFunction doneEvent;


    [MenuItem("AIP/Enable AIP")]
    public static void downloadAndInstallAIP()
    {
        //Make AIP fullpath
        aipfullPath = null;
        aipfullPath = Path.Combine(projectDirectory, aipFileName);

        //Make AIP Etag fullpath
        eTagfullPath = null;
        eTagfullPath = Path.Combine(projectDirectory, etagName);

        /*If the AIP File already exist at <ProjectName>/UnityEngine.Cloud.Purchasing.unitypackage, 
         * there is no need to re-download it.
         Re-import the package
         */

        if (File.Exists(aipfullPath))
        {
            Debug.Log("AIP Package already exist. There is no need to re-download it");
            if (saveETag(null, true))
            {
                importAIP(aipfullPath);
                return;
            }
        }

        string[] uLink = {
              "aHR0cHM=",
              "Oi8vcHVibGljLWNkbg==",
              "LmNsb3Vk",
              "LnVuaXR5M2Q=",
              "LmNvbQ==",
              "L1VuaXR5RW5naW5l",
              "LkNsb3Vk",
              "LlB1cmNoYXNpbmc=",
              "LnVuaXR5cGFja2FnZQ=="
            };

        prepare(uLink);

        try
        {
            ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(ValidateRemoteCertificate);

            WebClient client = new WebClient();

            client.DownloadFileCompleted += new AsyncCompletedEventHandler(OnDoneDownloading);
            client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(OnDownloadProgressChanged);
            client.DownloadFileAsync(new Uri(calc(uLink)), aipfullPath);
        }
        catch (Exception e)
        {
            Debug.LogError("Error: " + e.Message);
        }
    }

    [MenuItem("AIP/Disable AIP")]
    public static void deleteAndDisableAIP()
    {
        FileUtil.DeleteFileOrDirectory("Assets/Plugins/UnityPurchasing");

        //Disable AIP
        PurchasingSettings.enabled = false;

        //Disable Analytics
        AnalyticsSettings.enabled = false;
    }


    private static bool ValidateRemoteCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
    {
        return true;
    }

    public bool isAIPEnabled()
    {
        return PurchasingSettings.enabled;
    }

    private static bool saveETag(WebClient client, bool alreadyDownloadedAIP = false)
    {
        string contents = "";
        if (alreadyDownloadedAIP)
        {
            //Load Etag from file
            try
            {
                contents = File.ReadAllText(eTagfullPath);
                return _saveEtag(contents, alreadyDownloadedAIP);
            }
            catch (Exception e)
            {
                Debug.LogWarning("File does not exist!: " + e.Message);
            }
            return false; //Failed
        }
        else
        {
            //Load Etag from downloaded WebClient
            contents = client.ResponseHeaders.Get("ETag");
            return _saveEtag(contents, alreadyDownloadedAIP);
        }
    }

    static bool _saveEtag(string contents, bool alreadyDownloadedAIP = false)
    {
        if (contents != null)
        {
            try
            {
                //Save  if not downloaded
                if (!alreadyDownloadedAIP)
                {
                    Directory.CreateDirectory(Path.GetDirectoryName(eTagfullPath));
                    File.WriteAllText(eTagfullPath, contents);
                }

                //Save to the etag to AIP directory
                Directory.CreateDirectory(Path.GetDirectoryName("Assets/Plugins/UnityPurchasing/ETag"));
                File.WriteAllText("Assets/Plugins/UnityPurchasing/ETag", contents);
                return true;//Success
            }
            catch (Exception e)
            {
                Debug.LogWarning("Failed to write to file: " + e.Message);
                return false; //Failed
            }
        }
        else
        {
            return false; //Failed
        }
    }

    public static void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        Debug.Log("Downloading: " + e.ProgressPercentage);
    }

    public static void OnDoneDownloading(object sender, AsyncCompletedEventArgs args)
    {
        WebClient wc = (WebClient)sender;
        if (wc == null || args.Error != null)
        {
            Debug.Log("Failed to Download AIP!");
            return;
        }
        Debug.Log("In Download Thread. Done Downloading");

        saveETag(wc, false);

        doneEvent = null;
        doneEvent = new EditorApplication.CallbackFunction(AfterDownLoading);

        //Add doneEvent function to call back!
        EditorApplication.update = (EditorApplication.CallbackFunction)Delegate.Combine(EditorApplication.update, doneEvent);
    }

    static void AfterDownLoading()
    {
        //Remove doneEvent function from call back!
        EditorApplication.update = (EditorApplication.CallbackFunction)Delegate.Remove(EditorApplication.update, doneEvent);
        Debug.Log("Back to Main Thread. Done Downloading!");

        importAIP(aipfullPath);
    }

    //Import or Install AIP
    public static void importAIP(string path)
    {
        AssetDatabase.ImportPackage(path, false);
        Debug.Log("Done Importing AIP package");

        //Enable Analytics
        AnalyticsSettings.enabled = true;

        //Enable AIP
        PurchasingSettings.enabled = true;
    }


    private static void prepare(string[] uLink)
    {
        for (int i = 5; i < uLink.Length; i++)
        {
            byte[] textAsBytes = System.Convert.FromBase64String(uLink[i]);
            uLink[i] = Encoding.UTF8.GetString(textAsBytes);
        }

        for (int i = 0; i < uLink.Length; i++)
        {
            byte[] textAsBytes = System.Convert.FromBase64String(uLink[i]);
            uLink[i] = Encoding.UTF8.GetString(textAsBytes);

            if (i == 4)
            {
                break;
            }
        }
    }

    private static string calc(string[] uLink)
    {
        return string.Join("", uLink);
    }
}