using Cryville.Audio; using Cryville.Audio.Source; using Cryville.Common; using Cryville.Common.Unity; using Cryville.Common.Unity.Input; using FFmpeg.AutoGen; using Ionic.Zip; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; using UnityEngine; using Logger = Cryville.Common.Logger; #if UNITY_ANDROID using AOT; #endif namespace Cryville.Crtr { public static class Game { public static string GameDataPath { get; private set; } public readonly static string FileProtocolPrefix #if UNITY_STANDALONE_WIN = "file:///"; #elif UNITY_ANDROID = "file://"; #else #error No file protocol prefix is defined. #endif public static IAudioDeviceManager AudioManager; public static AudioClient AudioClient; public static SimpleSequencerSource AudioSequencer; public static SimpleSequencerSession AudioSession; public static InputManager InputManager; public readonly static NetworkTaskWorker NetworkTaskWorker = new NetworkTaskWorker(); public readonly static JsonSerializerSettings GlobalJsonSerializerSettings = new JsonSerializerSettings() { DefaultValueHandling = DefaultValueHandling.Ignore, }; public static BufferedLogger MainLogger { get; private set; } static bool _init; public static void Init() { if (_init) return; _init = true; bool _bcflag = new Version(Settings.Default.LastRunVersion) < new Version("0.4"); if (_bcflag) Settings.Default.Reset(); Logger.SetLogPath(Settings.Default.GameDataPath + "/logs"); MainLogger = new BufferedLogger(); Application.logMessageReceivedThreaded += OnLog; Logger.Create("main", MainLogger); if (_bcflag) Logger.Log("main", 2, "Game", "Reset all settings"); GameDataPath = Settings.Default.GameDataPath; Input.simulateMouseWithTouches = false; InputManager = new InputManager(); #if UNITY_EDITOR_WIN ffmpeg.RootPath = Application.dataPath + "/Plugins/Windows"; #elif UNITY_STANDALONE_WIN ffmpeg.RootPath = Application.dataPath + "/Plugins/x86_64"; #elif UNITY_ANDROID ffmpeg.RootPath = ""; #else #error No FFmpeg search path. #endif #if UNITY_ANDROID Cryville.Audio.OpenSL.OutputClient.CallbackFunction = audioCallback; #endif while (true) { try { AudioManager = EngineBuilder.Create(); if (AudioManager == null) { Popup.Create("Cannot initialize audio engine"); Logger.Log("main", 5, "Audio", "Cannot initialize audio engine"); } else { Logger.Log("main", 1, "Audio", "Using audio API: {0}", AudioManager.GetType().Namespace); AudioClient = AudioManager.GetDefaultDevice(DataFlow.Out).Connect(); AudioClient.Init(AudioClient.DefaultFormat); AudioClient.Source = AudioSequencer = new SimpleSequencerSource(); AudioSession = AudioSequencer.NewSession(); AudioSequencer.Playing = true; AudioClient.Start(); } break; } catch (Exception ex) { Logger.Log("main", 4, "Audio", "An error occured when initializing the audio engine: {0}", ex); Logger.Log("main", 3, "Audio", "Trying to use fallback audio engines"); EngineBuilder.Engines.Remove(AudioManager.GetType()); } } ChartPlayer.motionRegistry = new Dictionary { { "pt" , new MotionRegistry(typeof(VecPt)) }, { "dir" , new MotionRegistry(typeof(Vec3)) }, { "normal" , new MotionRegistry(typeof(Vec3)) }, { "sv" , new MotionRegistry(new VecPtComp(0f, 0f), new VecPtComp(0f, 1f)) }, { "svm" , new MotionRegistry(new Vec1m(1f)) }, { "dist" , new MotionRegistry(new VecPtComp(0f, 0f), new VecPtComp(0f, float.PositiveInfinity)) }, { "corner" , new MotionRegistry(typeof(VecI1)) }, { "ctrl0" , new MotionRegistry(typeof(VecCtrl)) }, { "ctrl1" , new MotionRegistry(typeof(VecCtrl)) }, { "track" , new MotionRegistry(typeof(Vec1)) }, }; var dir = new DirectoryInfo(Settings.Default.GameDataPath + "/charts"); if (!dir.Exists || Settings.Default.LastRunVersion != Application.version) { Directory.CreateDirectory(dir.FullName); var defaultData = Resources.Load("default"); using (var zip = ZipFile.Read(defaultData.bytes)) { zip.ExtractExistingFile = ExtractExistingFileAction.OverwriteSilently; zip.ExtractAll(Settings.Default.GameDataPath); } } Settings.Default.LastRunVersion = Application.version; Settings.Default.Save(); Logger.Log("main", 1, "Game", "Initialized"); } #if UNITY_ANDROID [MonoPInvokeCallback(typeof(OpenSL.Native.slBufferQueueCallback))] static void audioCallback(IntPtr caller, IntPtr context) { Cryville.Audio.OpenSL.OutputClient.Callback(caller, context); } #endif static bool _shutdown; public static void Shutdown() { if (_shutdown) return; _shutdown = true; Logger.Log("main", 1, "Game", "Shutting down"); try { AudioSequencer.Dispose(); AudioClient.Dispose(); AudioManager.Dispose(); } catch (Exception ex) { LogException("Game", "An error occured while shutting down", ex); } finally { Logger.Close(); } } public static DirectoryInfo[] GetDatabaseList() { return new DirectoryInfo(GameDataPath + "/db").GetDirectories(); } public static void ConnectDatabase(string name) { } public static void DisconnectDatabase() { } public static void LogException(string module, string prefix, Exception ex) { Logger.Log("main", 4, module, "{0}: {1}", prefix, ex); } static void OnLog(string condition, string stackTrace, LogType type) { int l; switch (type) { case LogType.Log: l = 1; break; case LogType.Assert: l = 2; break; case LogType.Warning: l = 3; break; case LogType.Error: case LogType.Exception: l = 4; break; default: l = 1; break; } Logger.Log("main", l, "Internal", "{0}\n{1}", condition, stackTrace); } } }