2023-01-22 16:38:23 +01:00
using System ;
using System.Collections.Generic ;
2024-08-03 22:24:42 +02:00
using System.IO ;
using System.Reflection ;
2023-07-30 01:20:46 +02:00
using System.Threading.Tasks ;
2023-01-22 16:38:23 +01:00
using ABI.CCK.Components ;
using UnityEditor ;
using UnityEditor.SceneManagement ;
using UnityEngine ;
using UnityEngine.Events ;
using UnityEngine.SceneManagement ;
2024-08-03 22:24:42 +02:00
using UnityEngine.XR ;
using Object = UnityEngine . Object ;
2023-07-30 01:20:46 +02:00
using Random = System . Random ;
2023-01-22 16:38:23 +01:00
namespace ABI.CCK.Scripts.Editor
{
public class CCK_BuildUtility
{
2024-08-03 22:24:42 +02:00
#region Variables
2023-01-22 16:38:23 +01:00
public static PreAvatarBundleEvent PreAvatarBundleEvent = new PreAvatarBundleEvent ( ) ;
public static PrePropBundleEvent PrePropBundleEvent = new PrePropBundleEvent ( ) ;
2024-08-03 22:24:42 +02:00
public static PreWorldBundleEvent PreWorldBundleEvent = new PreWorldBundleEvent ( ) ;
2023-07-30 01:20:46 +02:00
public static string upload_id = "" ;
2024-08-03 22:24:42 +02:00
#endregion
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
#region Avatar Upload
2023-07-30 01:20:46 +02:00
public static async Task BuildAndUploadAvatar ( GameObject avatarObject )
2023-01-22 16:38:23 +01:00
{
2024-08-03 22:24:42 +02:00
ClearLog ( ) ;
EnforceVRSetting ( ) ;
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
CVRAssetInfo assetInfo = avatarObject . GetComponent < CVRAssetInfo > ( ) ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
await InitializeAPIAndSetObjectId ( assetInfo , "cck/generate/avatar" ) ;
SetAssetInfoDirty ( assetInfo ) ;
GameObject instantiatedAvatar = InstantiateAndUnpackPrefab ( avatarObject ) ;
if ( ! HandlePreBuildEvent ( PreAvatarBundleEvent . Invoke , instantiatedAvatar ) )
2023-07-30 01:20:46 +02:00
{
2024-08-03 22:24:42 +02:00
Object . DestroyImmediate ( instantiatedAvatar ) ;
return ;
2023-07-30 01:20:46 +02:00
}
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
CCK_Tools . CleanEditorOnlyGameObjects ( instantiatedAvatar ) ;
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
PrefabUtility . SaveAsPrefabAsset ( instantiatedAvatar ,
! Application . unityVersion . Contains ( "2021" )
? "Assets/ABI.CCK/Resources/Cache/_CVRAvatar.prefab"
: $"Assets/ABI.CCK/Resources/Cache/CVRAvatar_{assetInfo.objectId}_{assetInfo.randomNum}.prefab" ) ;
2023-01-22 16:38:23 +01:00
AssetBundleBuild assetBundleBuild = new AssetBundleBuild ( ) ;
2024-08-03 22:24:42 +02:00
assetBundleBuild . assetNames = ! Application . unityVersion . Contains ( "2021" )
? new [ ] { "Assets/ABI.CCK/Resources/Cache/_CVRAvatar.prefab" }
: new [ ]
{
$"Assets/ABI.CCK/Resources/Cache/CVRAvatar_{assetInfo.objectId}_{assetInfo.randomNum}.prefab"
} ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
upload_id = assetInfo . objectId ;
2023-07-30 01:20:46 +02:00
EditorPrefs . SetString ( "m_ABI_uploadId" , upload_id ) ;
2024-08-03 22:24:42 +02:00
EditorPrefs . SetString ( "m_ABI_uploadRand" , assetInfo . randomNum ) ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
assetBundleBuild . assetBundleName = $"cvravatar_{assetInfo.objectId}_{assetInfo.randomNum}.cvravatar" ;
2023-01-22 16:38:23 +01:00
BuildPipeline . BuildAssetBundles ( Application . persistentDataPath , new [ ] { assetBundleBuild } ,
2023-07-30 01:20:46 +02:00
BuildAssetBundleOptions . ChunkBasedCompression , EditorUserBuildSettings . activeBuildTarget ) ;
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
Object . DestroyImmediate ( instantiatedAvatar ) ;
if ( ! File . Exists ( $"{Application.persistentDataPath}/cvravatar_{assetInfo.objectId}_{assetInfo.randomNum}.cvravatar" ) )
{
Debug . LogError ( "Error during bundling\nThere has been an error during the bundling process. Please check your console for errors." ) ;
EditorUtility . DisplayDialog ( "Error during bundling" , "There has been an error during the bundling process. Please check your console for errors." , "OK" ) ;
return ;
}
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
AssetDatabase . Refresh ( ) ;
2023-01-22 16:38:23 +01:00
EditorPrefs . SetBool ( "m_ABI_isBuilding" , true ) ;
EditorApplication . isPlaying = true ;
}
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
#endregion
#region Spawnable Upload
public static async Task BuildAndUploadSpawnable ( GameObject spawnableObject )
{
ClearLog ( ) ;
EnforceVRSetting ( ) ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
CVRAssetInfo assetInfo = spawnableObject . GetComponent < CVRAssetInfo > ( ) ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
CVRSpawnable spawnable = spawnableObject . GetComponent < CVRSpawnable > ( ) ;
spawnable . spawnableType = CVRSpawnable . SpawnableType . StandaloneSpawnable ;
EditorUtility . SetDirty ( spawnable ) ;
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
await InitializeAPIAndSetObjectId ( assetInfo , "cck/generate/spawnable" ) ;
SetAssetInfoDirty ( assetInfo ) ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
GameObject instantiatedSpawnable = InstantiateAndUnpackPrefab ( spawnableObject ) ;
if ( ! HandlePreBuildEvent ( PrePropBundleEvent . Invoke , instantiatedSpawnable ) )
2023-07-30 01:20:46 +02:00
{
2024-08-03 22:24:42 +02:00
Object . DestroyImmediate ( instantiatedSpawnable ) ;
return ;
2023-01-22 16:38:23 +01:00
}
2024-08-03 22:24:42 +02:00
CCK_Tools . CleanEditorOnlyGameObjects ( instantiatedSpawnable ) ;
PrefabUtility . SaveAsPrefabAsset ( instantiatedSpawnable ,
! Application . unityVersion . Contains ( "2021" )
? "Assets/ABI.CCK/Resources/Cache/_CVRSpawnable.prefab"
: $"Assets/ABI.CCK/Resources/Cache/CVRSpawnable_{assetInfo.objectId}_{assetInfo.randomNum}.prefab" ) ;
2023-01-22 16:38:23 +01:00
AssetBundleBuild assetBundleBuild = new AssetBundleBuild ( ) ;
2024-08-03 22:24:42 +02:00
assetBundleBuild . assetNames = ! Application . unityVersion . Contains ( "2021" )
? new [ ] { "Assets/ABI.CCK/Resources/Cache/_CVRSpawnable.prefab" }
: new [ ]
{
$"Assets/ABI.CCK/Resources/Cache/CVRSpawnable_{assetInfo.objectId}_{assetInfo.randomNum}.prefab"
} ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
upload_id = assetInfo . objectId ;
2023-07-30 01:20:46 +02:00
EditorPrefs . SetString ( "m_ABI_uploadId" , upload_id ) ;
2024-08-03 22:24:42 +02:00
EditorPrefs . SetString ( "m_ABI_uploadRand" , assetInfo . randomNum ) ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
assetBundleBuild . assetBundleName = $"cvrspawnable_{assetInfo.objectId}_{assetInfo.randomNum}.cvrprop" ;
2023-01-22 16:38:23 +01:00
BuildPipeline . BuildAssetBundles ( Application . persistentDataPath , new [ ] { assetBundleBuild } ,
2023-07-30 01:20:46 +02:00
BuildAssetBundleOptions . ChunkBasedCompression , EditorUserBuildSettings . activeBuildTarget ) ;
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
Object . DestroyImmediate ( instantiatedSpawnable ) ;
if ( ! File . Exists ( $"{Application.persistentDataPath}/cvrspawnable_{assetInfo.objectId}_{assetInfo.randomNum}.cvrprop" ) )
{
Debug . LogError ( "Error during bundling\nThere has been an error during the bundling process. Please check your console for errors." ) ;
EditorUtility . DisplayDialog ( "Error during bundling" , "There has been an error during the bundling process. Please check your console for errors." , "OK" ) ;
return ;
}
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
AssetDatabase . Refresh ( ) ;
2023-01-22 16:38:23 +01:00
EditorPrefs . SetBool ( "m_ABI_isBuilding" , true ) ;
EditorApplication . isPlaying = true ;
}
2024-08-03 22:24:42 +02:00
#endregion
#region World Upload
2023-07-30 01:20:46 +02:00
public static async Task BuildAndUploadMapAsset ( Scene scene , GameObject descriptor )
2023-01-22 16:38:23 +01:00
{
2024-08-03 22:24:42 +02:00
ClearLog ( ) ;
EnforceVRSetting ( ) ;
2023-01-22 16:38:23 +01:00
SetupNetworkUUIDs ( ) ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
CVRAssetInfo assetInfo = descriptor . GetComponent < CVRAssetInfo > ( ) ;
2023-07-30 01:20:46 +02:00
2024-08-03 22:24:42 +02:00
await InitializeAPIAndSetObjectId ( assetInfo , "cck/generate/world" ) ;
SetAssetInfoDirty ( assetInfo ) ;
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
if ( ! HandlePreBuildEvent ( PreWorldBundleEvent . Invoke , scene ) )
return ;
2023-07-30 01:20:46 +02:00
2023-01-22 16:38:23 +01:00
PrefabUtility . SaveAsPrefabAsset ( descriptor , "Assets/ABI.CCK/Resources/Cache/_CVRWorld.prefab" ) ;
AssetBundleBuild assetBundleBuild = new AssetBundleBuild ( ) ;
assetBundleBuild . assetNames = new [ ] { scene . path } ;
assetBundleBuild . assetBundleName = "bundle.cvrworld" ;
BuildPipeline . BuildAssetBundles ( Application . persistentDataPath , new [ ] { assetBundleBuild } ,
2023-07-30 01:20:46 +02:00
BuildAssetBundleOptions . ChunkBasedCompression , EditorUserBuildSettings . activeBuildTarget ) ;
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
if ( ! File . Exists ( $"{Application.persistentDataPath}/bundle.cvrworld" ) )
{
Debug . LogError ( "Error during bundling\nThere has been an error during the bundling process. Please check your console for errors." ) ;
EditorUtility . DisplayDialog ( "Error during bundling" , "There has been an error during the bundling process. Please check your console for errors." , "OK" ) ;
return ;
}
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
AssetDatabase . Refresh ( ) ;
2023-01-22 16:38:23 +01:00
EditorPrefs . SetBool ( "m_ABI_isBuilding" , true ) ;
EditorApplication . isPlaying = true ;
}
2024-08-03 22:24:42 +02:00
#endregion
#region Private Methods
private static async Task InitializeAPIAndSetObjectId ( CVRAssetInfo assetInfo , string apiEndpoint )
{
if ( string . IsNullOrEmpty ( assetInfo . objectId ) )
{
#if UNITY_EDITOR
APIConnection . Initialize ( EditorPrefs . GetString ( "m_ABI_Username" ) , EditorPrefs . GetString ( "m_ABI_Key" ) ) ;
#endif
APIConnection . BaseResponse < APIConnection . GenerateResponse > response = await APIConnection . MakeRequest < APIConnection . GenerateResponse > ( apiEndpoint , put : true ) ;
if ( response ! = null & & response . Data ! = null )
assetInfo . objectId = response . Data . Id . ToString ( ) ;
else
Debug . LogError ( $"[CCK:BuildUtility] New Guid could not be generated" ) ;
}
Random rnd = new Random ( ) ;
assetInfo . randomNum = rnd . Next ( 11111111 , 99999999 ) . ToString ( ) ;
}
private static void SetAssetInfoDirty ( Component assetInfo )
{
EditorUtility . SetDirty ( assetInfo ) ;
EditorSceneManager . MarkSceneDirty ( assetInfo . gameObject . scene ) ;
EditorSceneManager . SaveScene ( assetInfo . gameObject . scene ) ;
}
private static GameObject InstantiateAndUnpackPrefab ( GameObject original )
{
GameObject instantiated = Object . Instantiate ( original ) ;
if ( PrefabUtility . IsPartOfNonAssetPrefabInstance ( instantiated ) & & PrefabUtility . IsOutermostPrefabInstanceRoot ( instantiated ) )
PrefabUtility . UnpackPrefabInstance ( instantiated , PrefabUnpackMode . Completely , InteractionMode . AutomatedAction ) ;
// Why would you do this?
if ( instantiated . CompareTag ( "EditorOnly" ) )
instantiated . tag = "Untagged" ;
return instantiated ;
}
private static bool HandlePreBuildEvent ( Action < GameObject > preBuildEvent , GameObject instantiatedObject )
{
try
{
preBuildEvent . Invoke ( instantiatedObject ) ;
return true ;
}
catch ( Exception ex )
{
Debug . LogError ( $"[CCK:BuildUtility] Error occurred during PreBuildEvent: {ex.Message}" ) ;
return false ;
}
}
private static bool HandlePreBuildEvent ( Action < Scene > preBuildEvent , Scene scene )
{
try
{
preBuildEvent . Invoke ( scene ) ;
return true ;
}
catch ( Exception ex )
{
Debug . LogError ( $"[CCK:BuildUtility] Error occurred during PreBuildEvent: {ex.Message}" ) ;
return false ;
}
}
private static void SetupNetworkUUIDs ( )
2023-01-22 16:38:23 +01:00
{
CVRInteractable [ ] interactables = Resources . FindObjectsOfTypeAll < CVRInteractable > ( ) ;
CVRObjectSync [ ] objectSyncs = Resources . FindObjectsOfTypeAll < CVRObjectSync > ( ) ;
CVRVideoPlayer [ ] videoPlayers = Resources . FindObjectsOfTypeAll < CVRVideoPlayer > ( ) ;
CVRSpawnable [ ] spawnables = Resources . FindObjectsOfTypeAll < CVRSpawnable > ( ) ;
GameInstanceController [ ] gameInstances = Resources . FindObjectsOfTypeAll < GameInstanceController > ( ) ;
GunController [ ] gunControllers = Resources . FindObjectsOfTypeAll < GunController > ( ) ;
List < string > UsedGuids = new List < string > ( ) ;
foreach ( var interactable in interactables )
{
foreach ( var action in interactable . actions )
{
string guid ;
do
{
guid = Guid . NewGuid ( ) . ToString ( ) ;
} while ( ! string . IsNullOrEmpty ( UsedGuids . Find ( match = > match = = guid ) ) ) ;
UsedGuids . Add ( guid ) ;
action . guid = guid ;
}
}
foreach ( var objectSync in objectSyncs )
{
string guid ;
do
{
guid = Guid . NewGuid ( ) . ToString ( ) ;
} while ( ! string . IsNullOrEmpty ( UsedGuids . Find ( match = > match = = guid ) ) ) ;
UsedGuids . Add ( guid ) ;
var newserializedObject = new SerializedObject ( objectSync ) ;
newserializedObject . Update ( ) ;
SerializedProperty _guidProperty = newserializedObject . FindProperty ( "guid" ) ;
_guidProperty . stringValue = guid ;
newserializedObject . ApplyModifiedProperties ( ) ;
}
foreach ( var player in videoPlayers )
{
Guid res ;
if ( player . playerId = = null | | ! Guid . TryParse ( player . playerId , out res ) )
{
string guid ;
do
{
guid = Guid . NewGuid ( ) . ToString ( ) ;
} while ( ! string . IsNullOrEmpty ( UsedGuids . Find ( match = > match = = guid ) ) ) ;
UsedGuids . Add ( guid ) ;
player . playerId = guid ;
}
}
foreach ( var spawnable in spawnables )
{
string guid ;
do
{
guid = Guid . NewGuid ( ) . ToString ( ) ;
} while ( ! string . IsNullOrEmpty ( UsedGuids . Find ( match = > match = = guid ) ) ) ;
if ( spawnable . preGeneratedInstanceId = = "" )
{
UsedGuids . Add ( guid ) ;
spawnable . preGeneratedInstanceId = "ws~" + guid ;
}
else
{
UsedGuids . Add ( spawnable . preGeneratedInstanceId ) ;
}
spawnable . spawnableType = CVRSpawnable . SpawnableType . WorldSpawnable ;
}
int i = 0 ;
foreach ( GameInstanceController gameInstance in gameInstances )
{
if ( gameInstance . teams . Count = = 0 )
{
var team = new Team ( ) ;
team . name = "Team" ;
team . color = Color . red ;
team . playerLimit = 16 ;
team . index = i ;
gameInstance . teams . Add ( team ) ;
i + + ;
}
else
{
foreach ( var team in gameInstance . teams )
{
team . index = i ;
i + + ;
}
}
}
foreach ( var gunController in gunControllers )
{
string guid ;
do
{
guid = Guid . NewGuid ( ) . ToString ( ) ;
} while ( ! string . IsNullOrEmpty ( UsedGuids . Find ( match = > match = = guid ) ) ) ;
UsedGuids . Add ( guid ) ;
var newserializedObject = new SerializedObject ( gunController ) ;
newserializedObject . Update ( ) ;
SerializedProperty _guidProperty = newserializedObject . FindProperty ( "referenceID" ) ;
_guidProperty . stringValue = guid ;
newserializedObject . ApplyModifiedProperties ( ) ;
}
}
2024-08-03 22:24:42 +02:00
private static void ClearLog ( )
{
var assembly = Assembly . GetAssembly ( typeof ( UnityEditor . Editor ) ) ;
var type = assembly . GetType ( "UnityEditor.LogEntries" ) ;
var method = type . GetMethod ( "Clear" ) ;
method . Invoke ( new object ( ) , null ) ;
}
private static void EnforceVRSetting ( )
{
#if UNITY_2021_1_OR_NEWER
PlayerSettings . virtualRealitySupported = true ;
PlayerSettings . stereoRenderingPath = StereoRenderingPath . Instancing ;
XRSettings . enabled = false ;
#else
PlayerSettings . virtualRealitySupported = true ;
PlayerSettings . SetVirtualRealitySDKs ( BuildTargetGroup . Standalone , new string [ ] { "None" , "Oculus" , "OpenVR" , "MockHMD" } ) ;
PlayerSettings . stereoRenderingPath = StereoRenderingPath . SinglePass ;
#endif
}
#endregion
2023-01-22 16:38:23 +01:00
}
2024-08-03 22:24:42 +02:00
#region PreBundleEvents
[Serializable]
public class PreAvatarBundleEvent : UnityEvent < GameObject > { }
2023-01-22 16:38:23 +01:00
2024-08-03 22:24:42 +02:00
[Serializable]
public class PrePropBundleEvent : UnityEvent < GameObject > { }
[Serializable]
public class PreWorldBundleEvent : UnityEvent < Scene > { }
#endregion
}