Search Unity

Download Obb File from Own Server

Discussion in 'Android' started by stephenc, Oct 7, 2013.

  1. stephenc

    stephenc

    Joined:
    Oct 7, 2013
    Posts:
    7
    I'm using split packages for my Android app, so I have an APK and a seperate OBB file.

    If the obb file is present it works fine, but if it isn't present I want to be able to download it from my own server. I can download it using www but when I've downloaded it I can't figure out how to read the contents and / or install it in the correct folder.

    Unity documentation mentions how you can host OBB files yourself, but it doesn't say how you install them. Has anyone done this?
     
  2. stephenc

    stephenc

    Joined:
    Oct 7, 2013
    Posts:
    7
    My code looks like this:

    Code (csharp):
    1.  
    2.  
    3. protected IEnumerator LoadObb(string mainPath)
    4.     {
    5.         Debug.Log("Load obb from " + mainPath);
    6.  
    7.         Debug.Log("Get file from: " + mainPath);
    8.  
    9.         WWW www = null;
    10.         try
    11.         {
    12.             www = WWW.LoadFromCacheOrDownload(mainPath, 0);
    13.         }
    14.         catch (Exception e)
    15.         {
    16.             Debug.Log("Exception downloading obb: " + e);
    17.         }
    18.        
    19.  
    20.         // Wait for download to complete
    21.         while (www == null || !www.isDone)
    22.         {
    23.             yield return new WaitForSeconds(0.2f);
    24.  
    25.             Debug.Log("Downloading obb ");
    26.  
    27.             if (www != null)
    28.             {
    29.                 Debug.Log("Progress: " + www.progress);
    30.             }
    31.         }
    32.  
    33.         if (www.error != null)
    34.         {
    35.             Debug.Log("wwww error " + www.error);
    36.         }
    37.         else
    38.         {
    39.             Debug.Log("obb download complete");
    40.  
    41.             //TODO: Get this working
    42.             Debug.Log("About to create directory");
    43.             System.IO.Directory.CreateDirectory(GooglePlayDownloader.GetExpansionFilePath());
    44.  
    45.             Debug.Log("About to get bytes");
    46.             var bytes = www.bytes;
    47.  
    48.             if (bytes != null)
    49.             {
    50.                 Debug.Log("Bytes = " + bytes.Length);
    51.             }
    52.  
    53.             Debug.Log("About to write file");
    54.             if (bytes != null)
    55.             {
    56.                 File.WriteAllBytes(GooglePlayDownloader.GetPathForSavingOBB(), bytes);
    57.             }
    58.         }
    59.  
    60.  
    61.         yield return new WaitForSeconds(0.5f);
    62.         Application.LoadLevel("Start");
    63.     }
    64.  
    65.  

    Everything seems to work up until I try to access www.bytes

    At that point I see this message, and bytes are null:

    "WWWCached data can only be accessed using the assetBundle property!"


    How else can I save the obb file?
     
    SummitJain likes this.
  3. jvil

    jvil

    Joined:
    Jul 13, 2012
    Posts:
    263
  4. stephenc

    stephenc

    Joined:
    Oct 7, 2013
    Posts:
    7
    Thanks - I almost have it working.

    It's loading the file and saving it to the correct directory now, but unity doesn't pick up the obb file until the next restart. Is there a way to force a refresh?
     
  5. kjuanlu

    kjuanlu

    Joined:
    Dec 4, 2011
    Posts:
    100
    I have the same problem, after downloading the obb file form a server, and putting in the correct folder, the game can´t load scenes, but if I restart the game, it loads scenes.

    It seems we have to change the path from main apk to obb, but I don´t konw how to do it manually.

    Do you have the solution?

    Regards
     
    MaydayWorks likes this.
  6. alex_nwu

    alex_nwu

    Joined:
    Apr 16, 2014
    Posts:
    13
    Was anyone able to solve this problem? I'm in the same place where I download the obb file but am unable to move to the next scene until I restart my game.
     
    MaydayWorks likes this.
  7. Gizmoi

    Gizmoi

    Joined:
    Jan 9, 2013
    Posts:
    327
    I realised this is an old thread, but did anyone fix this?
    I download the obb from my own server. Then load it using WWW.LoadFromCacheOrDownload, then try and load the next scene. I also tried loading a file from Resources to check if the obb had downloaded. It never works until I reboot the app, then it works fine.
     
  8. kjuanlu

    kjuanlu

    Joined:
    Dec 4, 2011
    Posts:
    100

    This is the original thread: http://forum.unity3d.com/threads/more-google-obb-drama.135224/


    We have a solution, we call a "dummyActivity" after loadFromCacheOrDownload:

    Code (csharp):
    1.  
    2. public static void resetPath()
    3.     {
    4.         AndroidJavaClass resetClass = new AndroidJavaClass("com.pixelratio.resetapplicationpath.DummyActivity");
    5.         resetClass.CallStatic("static__reload");
    6.  
    7.     }
    8.  
    The jar file with the dummy activity: https://dl.dropboxusercontent.com/u/51228949/resetappliationpath.jar


    This is the content:
    Code (csharp):
    1.  
    2. package com.pixelratio.resetapplicationpath;
    3.  
    4. import android.app.Activity;
    5. import android.content.Intent;
    6. import android.os.Bundle;
    7. import com.unity3d.player.UnityPlayer;
    8.  
    9. public class DummyActivity
    10.   extends Activity
    11. {
    12.   protected void onCreate(Bundle savedInstanceState)
    13.   {
    14.     super.onCreate(savedInstanceState);
    15.     UnityPlayer.UnitySendMessage("obbManager", "__loadMainLevel", "");
    16.     finish();
    17.   }
    18.   public static void static__reload()
    19.   {
    20.     UnityPlayer.currentActivity.runOnUiThread(new Runnable()
    21.     {
    22.       public void run()
    23.       {
    24.         Intent intent = new Intent(UnityPlayer.currentActivity.getApplicationContext(), DummyActivity.class);
    25.    
    26.         UnityPlayer.currentActivity.startActivity(intent);
    27.       }
    28.     });
    29.   }
    30. }
    31. [/co
     
  9. Gizmoi

    Gizmoi

    Joined:
    Jan 9, 2013
    Posts:
    327
    Thank you very much!
    Although I'm curious what you mean by Application.dataPath will indicate if it's mounted?
    Will Application.dataPath be null or empty if the OBB is not loaded?
     
  10. kjuanlu

    kjuanlu

    Joined:
    Dec 4, 2011
    Posts:
    100
    I don't know, but I have this function in my old code:

    (please note that is an old code writed in javascript.... XD )

    Code (csharp):
    1.  
    2. function checkPath(){
    3.  
    4.  
    5.     while(true){
    6.        
    7.           Debug.Log(Application.dataPath);
    8.           yield WaitForSeconds(0.5);
    9.        }
    10.  
    11. }
    12.  
    It's called before resetPath() function (it's only for debug purpose). If you view the jar file, it sends the message "__loadMainLevel", it waits 2 seconds and load the first scene on the obb file.

    Here is the complete script:

    Code (csharp):
    1.  
    2. #pragma strict
    3.  
    4. private var expPath : String;
    5. private var serverUrl : String = "https://dl.dropboxusercontent.com/u.......squest.obb";
    6.  
    7.  
    8. public var loadingBar : Transform;
    9.  
    10. public var screen01 : GameObject;
    11. public var screen02 : GameObject;
    12. public var loadingObject : GameObject;
    13. public var loadingObject2 : GameObject;
    14. public var textObject : GameObject;
    15.  
    16. public var loadFromOwnServer : boolean = false;
    17.  
    18.  
    19. public var  background :Texture2D;
    20.  
    21. function OnGUI(){
    22.         if( loadFromOwnServer == false)
    23.             GUI.DrawTexture(new Rect(0,0,Screen.width,Screen.height),background, ScaleMode.ScaleToFit,true,0);
    24.  
    25. }
    26.  
    27. function Awake(){
    28.  
    29.     if( loadFromOwnServer == false) this.GetComponent(Camera).enabled = true;
    30.     else{
    31.         screen01.active = false;
    32.         screen02.active = false;
    33.         loadingObject.active = false;
    34.         loadingObject2.active = false;
    35.         textObject.active = false;
    36.     }
    37. }
    38.  
    39. function Start () {
    40.  
    41.  
    42.  
    43.     if(Application.platform != RuntimePlatform.Android){ //Editor loading
    44.  
    45.         yield WaitForSeconds(5);
    46.      
    47.      
    48.         Application.LoadLevel("Main_GUI");
    49.         return;
    50.     }
    51.  
    52.  
    53.  
    54.     var obbState : int = checkOBBFile();
    55.     if(obbState== 1)
    56.             Application.LoadLevel("Main_GUI");
    57.          
    58.     else if(obbState == -1){
    59.             if( loadFromOwnServer == false) DownloadFromGooglePlay();
    60.             if( loadFromOwnServer == true)  DownloadFromOwnServer();
    61.     }
    62.          
    63.     else if(obbState == -2)
    64.             Debug.Log("External Storage is not available");
    65.  
    66.  
    67.  
    68.  
    69.  
    70.  
    71.  
    72.  
    73. }
    74.  
    75.  
    76.  
    77.  
    78.  
    79.  
    80.  
    81.  
    82.  
    83.  
    84.  
    85. function DownloadFromGooglePlay() {
    86.  
    87.     /*screen01.active = true;
    88.     screen02.active = true;
    89.     loadingObject.active = true;
    90.     loadingObject2.active = true;
    91.     textObject.active = true;*/
    92.      
    93.     GooglePlayDownloader.FetchOBB();
    94.  
    95.     var mainPath : String;
    96.     do{
    97.    
    98.         yield WaitForSeconds(0.5);
    99.         mainPath = GooglePlayDownloader.GetMainOBBPath(expPath);
    100.         Debug.Log("Waiting mainPath "+mainPath);
    101.          
    102.     }
    103.     while(mainPath == null);
    104.    
    105.  
    106.    
    107.  
    108.     var uri : String  = "file://" + mainPath;
    109.     Debug.Log("downloading " + uri);
    110.     var www: WWW = WWW.LoadFromCacheOrDownload(uri , 0);      
    111.  
    112.     //Wait for download to complete
    113.     while(www.isDone == false){
    114.  
    115.         //textObject.GetComponent(TextMesh).text = "Downloading... " + (www.progress*52000) + "/52000Kb";
    116.  
    117.         loadingBar.localScale.z = www.progress;
    118.         yield;
    119.                          
    120.              
    121.     }
    122.  
    123.     if (www.error != null) {
    124.         Debug.Log (www.error);
    125.         return;
    126.     }
    127.     else{
    128.  
    129.         Application.LoadLevel("Main_GUI");
    130.     }
    131.      
    132.      
    133.  
    134.  
    135. }
    136.  
    137.  
    138.  
    139.  
    140.  
    141.  
    142.  
    143.  
    144.  
    145.  
    146.  
    147.  
    148.  
    149. function DownloadFromOwnServer () {
    150.  
    151.  
    152.     screen01.active = true;
    153.     screen02.active = true;
    154.     loadingObject.active = true;
    155.     loadingObject2.active = true;
    156.     textObject.active = true;
    157.  
    158.    
    159.    
    160.    
    161.  
    162.     var uri : String  = serverUrl;
    163.     Debug.Log("downloading " + uri);
    164.     //var www: WWW = WWW.LoadFromCacheOrDownload(uri , 0);      
    165.     var www: WWW = new WWW(uri);  
    166.     //Wait for download to complete
    167.     while(www.isDone == false){
    168.  
    169.         textObject.GetComponent(TextMesh).text = "Downloading... " + (www.progress*52032) + "/55032Kb";
    170.         loadingBar.localScale.z = www.progress;
    171.         yield;              
    172.     }
    173.  
    174.     if (www.error != null) {
    175.         Debug.Log (www.error);
    176.         return;
    177.     }
    178.     else{
    179.  
    180.         textObject.GetComponent(TextMesh).text = "Loading... ";
    181.  
    182.         //Put file in the correct directory
    183.         Debug.Log("About to create directory "+expPath);
    184.         System.IO.Directory.CreateDirectory(expPath);
    185.      
    186.         var futureFile : String = GooglePlayDownloader.GetMainOBBPath_ifNoExist(expPath);
    187.         Debug.Log("About to create file " + futureFile);
    188.      
    189.         var bytes = www.bytes;
    190.         if (bytes != null){
    191.             System.IO.File.WriteAllBytes(futureFile, bytes);
    192.             Debug.Log("file created" + futureFile);
    193.          }
    194.        
    195.          /////////////////////
    196.          ////////////////////
    197.      
    198.        
    199.      
    200.        GooglePlayDownloader.resetPath();
    201.        checkPath();
    202.      
    203.    
    204.      
    205.  
    206.      
    207.     }
    208.      
    209.      
    210.    
    211.  
    212. }
    213.  
    214. function __loadMainLevel(param : String){
    215.  
    216.     yield WaitForSeconds(2);
    217.     Application.LoadLevel("Main_GUI");
    218. }
    219.  
    220.  
    221.  
    222.  
    223.  
    224.  
    225.  
    226.  
    227.  
    228.  
    229.  
    230.  
    231.  
    232.  
    233.  
    234.  
    235. function checkOBBFile() : int{
    236.  
    237.  
    238.     expPath = GooglePlayDownloader.GetExpansionFilePath();
    239.     if (expPath == null){
    240.  
    241.         //Debug.Log("External Storage is not available");
    242.         return -2;
    243.          
    244.     }
    245.  
    246.     else {
    247.  
    248.         var mainPath : String = GooglePlayDownloader.GetMainOBBPath(expPath);
    249.         var patchPath : String = GooglePlayDownloader.GetPatchOBBPath(expPath);
    250.      
    251.         Debug.Log("expPath " + expPath);
    252.         Debug.Log("Main " + mainPath);
    253.         //Debug.Log("Main_ " + mainPath.Substring(expPath.Length));
    254.  
    255.      
    256.         if(mainPath != null) {  
    257.          
    258.             Debug.Log("Main " + mainPath);
    259.             return 1;
    260.         }
    261.      
    262.         else{
    263.          
    264.             Debug.Log("mainPath not available");
    265.             return -1;
    266.         }
    267.     }
    268.      
    269.      
    270.          
    271. }
    272.  
    273.  
    274.  
    275. function checkPath(){
    276.  
    277.  
    278.     while(true){
    279.        
    280.           Debug.Log(Application.dataPath);
    281.           yield WaitForSeconds(0.5);
    282.        }
    283.  
    284. }
    285.  
    And this is the GooglePlayDownloader.cs with some changes:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.IO;
    5. using System;
    6.  
    7. public class GooglePlayDownloader
    8. {
    9.     private static AndroidJavaClass detectAndroidJNI;
    10.     public static bool RunningOnAndroid()
    11.     {
    12.         if (detectAndroidJNI == null)
    13.             detectAndroidJNI = new AndroidJavaClass("android.os.Build");
    14.         return detectAndroidJNI.GetRawClass() != IntPtr.Zero;
    15.     }
    16.  
    17.     private static AndroidJavaClass Environment;
    18.     private const string Environment_MEDIA_MOUNTED = "mounted";
    19.  
    20.     static GooglePlayDownloader()
    21.     {
    22.         if (!RunningOnAndroid())
    23.             return;
    24.  
    25.         Environment = new AndroidJavaClass("android.os.Environment");
    26.      
    27.         using (AndroidJavaClass dl_service = new AndroidJavaClass("com.unity3d.plugin.downloader.UnityDownloaderService"))
    28.         {
    29.        // stuff for LVL -- MODIFY FOR YOUR APPLICATION!
    30.             dl_service.SetStatic("BASE64_PUBLIC_KEY", "MIIBIjANBgkqhkiG9w0BAQ.....EleWfyliTpVzUUwvPY00/Mc6WaYa34Inju0TB6VeUA6b64pa2uHv08KEsAGoDwIDAQAB");
    31.        // used by the preference obfuscater
    32.             dl_service.SetStatic("SALT", new byte[]{1, 43, 256-12, 256-1, 54, 98, 256-100, 256-12, 43, 2, 256-8, 256-4, 9, 5, 256-106, 256-108, 256-33, 45, 256-1, 84});
    33.         }
    34.     }
    35.  
    36.     public static string GetExpansionFilePath()
    37.     {
    38.         populateOBBData();
    39.  
    40.         if (Environment.CallStatic<string>("getExternalStorageState") != Environment_MEDIA_MOUNTED)
    41.             return null;
    42.          
    43.         const string obbPath = "Android/obb";
    44.          
    45.         using (AndroidJavaObject externalStorageDirectory = Environment.CallStatic<AndroidJavaObject>("getExternalStorageDirectory"))
    46.         {
    47.             string root = externalStorageDirectory.Call<string>("getPath");
    48.             return String.Format("{0}/{1}/{2}", root, obbPath, obb_package);
    49.         }
    50.     }
    51.     public static string GetMainOBBPath(string expansionFilePath)
    52.     {
    53.         populateOBBData();
    54.  
    55.         if (expansionFilePath == null)
    56.             return null;
    57.         string main = String.Format("{0}/main.{1}.{2}.obb", expansionFilePath, obb_version, obb_package);
    58.         if (!File.Exists(main))
    59.             return null;
    60.         return main;
    61.     }
    62.  
    63.         public static string GetMainOBBPath_ifNoExist(string expansionFilePath)
    64.     {
    65.         populateOBBData();
    66.  
    67.         if (expansionFilePath == null)
    68.             return null;
    69.         string main = String.Format("{0}/main.{1}.{2}.obb", expansionFilePath, obb_version, obb_package);
    70.         //if (!File.Exists(main))
    71.         //    return null;
    72.         return main;
    73.     }
    74.  
    75.     public static string GetPatchOBBPath(string expansionFilePath)
    76.     {
    77.         populateOBBData();
    78.  
    79.         if (expansionFilePath == null)
    80.             return null;
    81.         string main = String.Format("{0}/patch.{1}.{2}.obb", expansionFilePath, obb_version, obb_package);
    82.         if (!File.Exists(main))
    83.             return null;
    84.         return main;
    85.     }
    86.  
    87.  
    88.     public static void FetchOBB()
    89.     {
    90.         using (AndroidJavaClass unity_player = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
    91.         {
    92.             AndroidJavaObject current_activity = unity_player.GetStatic<AndroidJavaObject>("currentActivity");
    93.  
    94.             AndroidJavaObject intent = new AndroidJavaObject("android.content.Intent",
    95.                                                             current_activity,
    96.                                                             new AndroidJavaClass("com.unity3d.plugin.downloader.UnityDownloaderActivity"));
    97.  
    98.             int Intent_FLAG_ACTIVITY_NO_ANIMATION = 0x10000;
    99.             intent.Call<AndroidJavaObject>("addFlags", Intent_FLAG_ACTIVITY_NO_ANIMATION);
    100.             intent.Call<AndroidJavaObject>("putExtra", "unityplayer.Activity",
    101.                                                         current_activity.Call<AndroidJavaObject>("getClass").Call<string>("getName"));
    102.             current_activity.Call("startActivity", intent);
    103.  
    104.             if (AndroidJNI.ExceptionOccurred() != System.IntPtr.Zero)
    105.             {
    106.                 Debug.LogError("Exception occurred while attempting to start DownloaderActivity - is the AndroidManifest.xml incorrect?");
    107.                 AndroidJNI.ExceptionDescribe();
    108.                 AndroidJNI.ExceptionClear();
    109.             }
    110.         }
    111.     }
    112.  
    113.     public static void resetPath()
    114.     {
    115.         AndroidJavaClass resetClass = new AndroidJavaClass("com.pixelratio.resetapplicationpath.DummyActivity");
    116.         resetClass.CallStatic("static__reload");
    117.  
    118.     }
    119.  
    120.     // This code will reuse the package version from the .apk when looking for the .obb
    121.     // Modify as appropriate
    122.     private static string obb_package;
    123.     private static int obb_version = 0;
    124.     private static void populateOBBData()
    125.     {
    126.         if (obb_version != 0)
    127.             return;
    128.         using (AndroidJavaClass unity_player = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
    129.         {
    130.             AndroidJavaObject current_activity = unity_player.GetStatic<AndroidJavaObject>("currentActivity");
    131.             obb_package = current_activity.Call<string>("getPackageName");
    132.             AndroidJavaObject package_info = current_activity.Call<AndroidJavaObject>("getPackageManager").Call<AndroidJavaObject>("getPackageInfo", obb_package, 0);
    133.             obb_version = package_info.Get<int>("versionCode");
    134.         }
    135.     }
    136. }
     
  11. Gizmoi

    Gizmoi

    Joined:
    Jan 9, 2013
    Posts:
    327
    Well I got it working, although I used OnApplicationPause to check when the Android activity had switched back and that seemed to work fine. Thanks for the help :)
     
    SummitJain and kjuanlu like this.
  12. SummitJain

    SummitJain

    Joined:
    May 30, 2014
    Posts:
    12
    @Gizmoi: I reached exactly where you were but i am unable to swtich activity..can you please help.

    Many thanks in advance. Once Done i will share the entire code for people who gets stuck like me.
     
  13. Gizmoi

    Gizmoi

    Joined:
    Jan 9, 2013
    Posts:
    327
    This is the code we use to switch to a DummyActivity I made in Android Studio.
    Code (CSharp):
    1. private static void RunDummyActivity()
    2. {
    3.     using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
    4.     {
    5.         var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
    6.  
    7.         var dummyActivity = new AndroidJavaClass("com.threesixty.dummy.DummyActivity");
    8.         dummyActivity.CallStatic("launch", activity);
    9.     }
    10. }

    The DummyActivity looks like this:
    Code (Java):
    1. public class DummyActivity extends Activity {
    2.  
    3.     @Override
    4.     protected void onCreate(Bundle savedInstanceState) {
    5.         super.onCreate(savedInstanceState);
    6.         finish();
    7.     }
    8.  
    9.     public static void launch(Activity activity) {
    10.         Intent intent = new Intent(activity, DummyActivity.class);
    11.         activity.startActivity(intent);
    12.     }
    13. }

    This is the code we use to check that the app has returned back to Unity.
    Code (CSharp):
    1. private void OnApplicationPause(bool paused)
    2. {
    3.     Debug.Log("Application Paused: " + paused);
    4.     if (paused == false && m_WaitingForResume == true)
    5.     {
    6.         OnComplete();
    7.     }
    8. }

    Hope that helps.
     
  14. mobiusmedia

    mobiusmedia

    Joined:
    Aug 14, 2017
    Posts:
    19
    @Gizmoi did you have to add this activity to the manifest file for this to work? I am getting the following error:

    Code (CSharp):
    1. AndroidJavaException: android.content.ActivityNotFoundException: Unable to find explicit activity class {com.mycompany.mygame/com.mycompany.lib.DummyActivity}; have you declared this activity in your AndroidManifest.
     
  15. mobiusmedia

    mobiusmedia

    Joined:
    Aug 14, 2017
    Posts:
    19
    I managed to get this working (as in launching the other activity) by adding the activity to the overridden manifest file. However, this does not fix the issue. The screen simply goes black and stays black and does not load the scene from the OBB :(
     
  16. SherwynRodrigues

    SherwynRodrigues

    Joined:
    Aug 14, 2021
    Posts:
    3
    hiiii

    I am also trying to download and install obb from self server but all the plugins are absolute and depricated. Could you please send the code of how you are downloading anf how do you exactly use it. please.
    it would be very helpful.
    Thank You