diff --git a/.gitignore b/.gitignore index 9d97c3b..482ede6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,7 @@ -# Created by https://www.toptal.com/developers/gitignore/api/unity -# Edit at https://www.toptal.com/developers/gitignore?templates=unity - -### Unity ### # This .gitignore file should be placed at the root of your Unity project directory # # Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore +# /[Ll]ibrary/ /[Tt]emp/ /[Oo]bj/ @@ -74,5 +71,6 @@ crashlytics-build.properties /[Aa]ssets/[Ss]treamingAssets/aa.meta /[Aa]ssets/[Ss]treamingAssets/aa/* -# End of https://www.toptal.com/developers/gitignore/api/unity - +# Additional Rivet items +**/.DS_Store +/ProjectSettings diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..ddb6ff8 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "visualstudiotoolsforunity.vstuc" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..da60e25 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,10 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to Unity", + "type": "vstuc", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1be22fd --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,60 @@ +{ + "files.exclude": { + "**/.DS_Store": true, + "**/.git": true, + "**/.vs": true, + "**/.gitmodules": true, + "**/.vsconfig": true, + "**/*.booproj": true, + "**/*.pidb": true, + "**/*.suo": true, + "**/*.user": true, + "**/*.userprefs": true, + "**/*.unityproj": true, + "**/*.dll": true, + "**/*.exe": true, + "**/*.pdf": true, + "**/*.mid": true, + "**/*.midi": true, + "**/*.wav": true, + "**/*.gif": true, + "**/*.ico": true, + "**/*.jpg": true, + "**/*.jpeg": true, + "**/*.png": true, + "**/*.psd": true, + "**/*.tga": true, + "**/*.tif": true, + "**/*.tiff": true, + "**/*.3ds": true, + "**/*.3DS": true, + "**/*.fbx": true, + "**/*.FBX": true, + "**/*.lxo": true, + "**/*.LXO": true, + "**/*.ma": true, + "**/*.MA": true, + "**/*.obj": true, + "**/*.OBJ": true, + "**/*.asset": true, + "**/*.cubemap": true, + "**/*.flare": true, + "**/*.mat": true, + "**/*.meta": true, + "**/*.prefab": true, + "**/*.unity": true, + "build/": true, + "Build/": true, + "Library/": true, + "library/": true, + "obj/": true, + "Obj/": true, + "Logs/": true, + "logs/": true, + "ProjectSettings/": true, + "UserSettings/": true, + "temp/": true, + "Temp/": true + }, + "dotnet.defaultSolution": "plugin-unity.sln" +} \ No newline at end of file diff --git a/Assets/Rivet.meta b/Assets/Rivet.meta new file mode 100644 index 0000000..6f55048 --- /dev/null +++ b/Assets/Rivet.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 340c7fefc29a24a948e0af4516b94e7f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Dockerfile b/Assets/Rivet/Dockerfile new file mode 100644 index 0000000..7165280 --- /dev/null +++ b/Assets/Rivet/Dockerfile @@ -0,0 +1,24 @@ +# MARK: Runner +FROM ubuntu:22.04 +WORKDIR /app + +# Install necessary libraries for Unity +RUN apt-get update && apt-get install -y \ + ca-certificates \ + && update-ca-certificates \ + && rm -rf /var/lib/apt/lists/* \ + && useradd -ms /bin/bash rivet + +# Copy the precompiled Unity server files +COPY ./Builds/Game /app/Game +# COPY ./Builds/Game_BurstDebugInformation_DoNotShip /app/Game_BurstDebugInformation_DoNotShip +# COPY ./Builds/Game_Data /app/Game_Data +# COPY ./Builds/UnityPlayer.so /app/UnityPlayer.so + +# Change to user rivet +USER rivet + +ENV UNITY_SERVER=1 + +# Run the server +ENTRYPOINT ["./Game", "-batchmode", "-nographics"] diff --git a/Assets/Rivet/Dockerfile.meta b/Assets/Rivet/Dockerfile.meta new file mode 100644 index 0000000..1a0b7a3 --- /dev/null +++ b/Assets/Rivet/Dockerfile.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 13b71fb9c7cd14e2294b40b89dcef233 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor.meta b/Assets/Rivet/Editor.meta new file mode 100644 index 0000000..4e5583c --- /dev/null +++ b/Assets/Rivet/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5043f2f84714f4e3ca72b93f6a18c333 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor/BuildPipeline.cs b/Assets/Rivet/Editor/BuildPipeline.cs new file mode 100644 index 0000000..24037da --- /dev/null +++ b/Assets/Rivet/Editor/BuildPipeline.cs @@ -0,0 +1,24 @@ +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; +using UnityEngine; + +public class BuildScript : IPreprocessBuildWithReport, IPostprocessBuildWithReport +{ + public int callbackOrder { get { return 0; } } + + public void OnPreprocessBuild(BuildReport report) + { + // Create the asset file before the build + RivetSettings data = ScriptableObject.CreateInstance(); + // data.SomeData = "Some data from the plugin"; + AssetDatabase.CreateAsset(data, "Assets/rivet_export.asset"); + AssetDatabase.SaveAssets(); + } + + public void OnPostprocessBuild(BuildReport report) + { + // Delete the asset file after the build + AssetDatabase.DeleteAsset("Assets/rivet_export.asset"); + } +} \ No newline at end of file diff --git a/Assets/Rivet/Editor/BuildPipeline.cs.meta b/Assets/Rivet/Editor/BuildPipeline.cs.meta new file mode 100644 index 0000000..e0c7a96 --- /dev/null +++ b/Assets/Rivet/Editor/BuildPipeline.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 31fc2096397c426c68832f49183d6abf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor/CoroutineUtility.cs b/Assets/Rivet/Editor/CoroutineUtility.cs new file mode 100644 index 0000000..f1c854c --- /dev/null +++ b/Assets/Rivet/Editor/CoroutineUtility.cs @@ -0,0 +1,25 @@ +using UnityEngine; +using System.Collections; + +public class CoroutineUtility : MonoBehaviour +{ + public static CoroutineUtility instance; + + private void Awake() + { + if (instance == null) + { + instance = this; + DontDestroyOnLoad(gameObject); + } + else + { + Destroy(gameObject); + } + } + + public Coroutine StartGlobalCoroutine(IEnumerator coroutine) + { + return StartCoroutine(coroutine); + } +} \ No newline at end of file diff --git a/Assets/Rivet/Editor/CoroutineUtility.cs.meta b/Assets/Rivet/Editor/CoroutineUtility.cs.meta new file mode 100644 index 0000000..0a4d63a --- /dev/null +++ b/Assets/Rivet/Editor/CoroutineUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b601aa65406b41a490ceafaf4bd7764 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor/EditorCoroutine.cs b/Assets/Rivet/Editor/EditorCoroutine.cs new file mode 100644 index 0000000..47a7c17 --- /dev/null +++ b/Assets/Rivet/Editor/EditorCoroutine.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections; +using UnityEditor; + +public class EditorCoroutine +{ + public static EditorCoroutine Start(IEnumerator routine) + { + EditorCoroutine coroutine = new EditorCoroutine(routine); + coroutine.Start(); + return coroutine; + } + + readonly IEnumerator routine; + EditorCoroutine(IEnumerator routine) + { + this.routine = routine; + } + + void Start() + { + EditorApplication.update += Update; + } + public void Stop() + { + EditorApplication.update -= Update; + } + + void Update() + { + if (!routine.MoveNext()) + { + Stop(); + } + } +} \ No newline at end of file diff --git a/Assets/Rivet/Editor/EditorCoroutine.cs.meta b/Assets/Rivet/Editor/EditorCoroutine.cs.meta new file mode 100644 index 0000000..289ef3e --- /dev/null +++ b/Assets/Rivet/Editor/EditorCoroutine.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 95c9da3192d364b37b715ecacfdbc85a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor/Result.cs b/Assets/Rivet/Editor/Result.cs new file mode 100644 index 0000000..d2d154d --- /dev/null +++ b/Assets/Rivet/Editor/Result.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; + +// https://josef.codes/my-take-on-the-result-class-in-c-sharp/ +public abstract class Result +{ + public bool Success { get; protected set; } + public bool Failure => !Success; +} + +public abstract class Result : Result +{ + private T _data; + + protected Result(T data) + { + Data = data; + } + + public T Data + { + get => Success ? _data : throw new Exception($"You can't access .{nameof(Data)} when .{nameof(Success)} is false"); + set => _data = value; + } +} + +public class SuccessResult : Result +{ + public SuccessResult() + { + Success = true; + } +} + +public class SuccessResult : Result +{ + public SuccessResult(T data) : base(data) + { + Success = true; + } +} + +public class ErrorResult : Result, IErrorResult +{ + public ErrorResult(string message) : this(message, Array.Empty()) + { + } + + public ErrorResult(string message, IReadOnlyCollection errors) + { + Message = message; + Success = false; + Errors = errors ?? Array.Empty(); + } + + public string Message { get; } + public IReadOnlyCollection Errors { get; } +} + +public class ErrorResult : Result, IErrorResult +{ + public ErrorResult(string message) : this(message, Array.Empty()) + { + } + + public ErrorResult(string message, IReadOnlyCollection errors) : base(default) + { + Message = message; + Success = false; + Errors = errors ?? Array.Empty(); + } + + public string Message { get; set; } + public IReadOnlyCollection Errors { get; } +} + +public class Error +{ + public Error(string details) : this(null, details) + { + + } + + public Error(string code, string details) + { + Code = code; + Details = details; + } + + public string Code { get; } + public string Details { get; } +} + +internal interface IErrorResult +{ + string Message { get; } + IReadOnlyCollection Errors { get; } +} diff --git a/Assets/Rivet/Editor/Result.cs.meta b/Assets/Rivet/Editor/Result.cs.meta new file mode 100644 index 0000000..095a9cb --- /dev/null +++ b/Assets/Rivet/Editor/Result.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f2e6722c072dc45198427642016e356d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor/RivetCLI.cs b/Assets/Rivet/Editor/RivetCLI.cs new file mode 100644 index 0000000..4fee3c3 --- /dev/null +++ b/Assets/Rivet/Editor/RivetCLI.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Data.Common; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; +using UnityEngine; + +public static class RivetCLI +{ + public const string REQUIRED_RIVET_CLI_VERSION = "v1.0.1"; + public const string RIVET_CLI_PATH_SETTING = "RivetCLIPath"; + + public static bool CLIInstalled() + { + string editorRivetPath = GetRivetCLIPath(); + + if (string.IsNullOrEmpty(editorRivetPath)) + { + return false; + } + + var result = RunCommand("sidekick", "get-cli-version"); + switch (result) + { + case SuccessResult successResult: + // Verify that the version that came back is correct + if (successResult.Data["Ok"] == null) + { + return false; + } + var cliVersion = successResult.Data["Ok"]["version"].ToString(); + if (cliVersion != REQUIRED_RIVET_CLI_VERSION) + { + return false; + } + return true; + case ErrorResult _: + return false; + default: + UnityEngine.Debug.Log("Got different result type"); + return false; + } + } + + public static Result RunCommand(params string[] args) + { + return RunRivetCLI(args); + } + + public static string GetBinDir() + { + string homePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + return Path.Combine(homePath, ".rivet", REQUIRED_RIVET_CLI_VERSION, "bin"); + } + + public static string GetRivetCLIPath() + { + // // Assuming _RivetEditorSettings is a static class with a method GetSetting + // string cliPath = _RivetEditorSettings.GetSetting(RIVET_CLI_PATH_SETTING); + // if (!string.IsNullOrEmpty(cliPath)) + // { + // return cliPath; + // } + + return Path.Combine(GetBinDir(), "rivet"); + } + + public static Result RunRivetCLI(params string[] args) + { + UnityEngine.Debug.Log($"Running Rivet CLI: {GetRivetCLIPath()} {string.Join(" ", args)}"); + + if (!File.Exists(GetRivetCLIPath())) + { + UnityEngine.Debug.LogError("File does not exist: " + GetRivetCLIPath()); + return new ErrorResult("File does not exist: " + GetRivetCLIPath()); + } + + var startInfo = new ProcessStartInfo + { + FileName = GetRivetCLIPath(), + Arguments = string.Join(" ", args), + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + + try + { + using (var process = Process.Start(startInfo)) + { + var output = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + + return new SuccessResult(JObject.Parse(output)); + } + } + catch (Exception ex) + { + return new ErrorResult("Failed to start process: " + ex.Message); + } + } + + public static Result _install() + { + string bin_dir = GetBinDir(); + + System.Environment.SetEnvironmentVariable("RIVET_CLI_VERSION", REQUIRED_RIVET_CLI_VERSION); + System.Environment.SetEnvironmentVariable("BIN_DIR", bin_dir); + + var process = new System.Diagnostics.Process(); + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + + if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) + { + process.StartInfo.FileName = "powershell.exe"; + process.StartInfo.Arguments = "-Command \"iwr https://raw.githubusercontent.com/rivet-gg/cli/$env:RIVET_CLI_VERSION/install/windows.ps1 -useb | iex\""; + } + else + { + process.StartInfo.FileName = "/bin/sh"; + process.StartInfo.Arguments = "-c \"curl -fsSL https://raw.githubusercontent.com/rivet-gg/cli/${RIVET_CLI_VERSION}/install/unix.sh | sh\""; + } + + process.Start(); + string output = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + + return new SuccessResult(output); + } +} \ No newline at end of file diff --git a/Assets/Rivet/Editor/RivetCLI.cs.meta b/Assets/Rivet/Editor/RivetCLI.cs.meta new file mode 100644 index 0000000..3f3aeeb --- /dev/null +++ b/Assets/Rivet/Editor/RivetCLI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 111d28c48b6bf457380774e98e995473 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor/RivetPlugin.cs b/Assets/Rivet/Editor/RivetPlugin.cs new file mode 100644 index 0000000..df5e5e0 --- /dev/null +++ b/Assets/Rivet/Editor/RivetPlugin.cs @@ -0,0 +1,48 @@ +#define IN_EDITOR + +using UnityEngine; +using UnityEditor; +using System.Diagnostics; +using System.IO; +using Newtonsoft.Json.Linq; + +namespace Rivet +{ + public class RivetPluginWindow : EditorWindow + { + // Define an interface for the states + public interface IState + { + void OnEnter(RivetPluginWindow pluginWindow); + void OnGUI(); + } + + [MenuItem("Window/Rivet Plugin")] + public static void ShowWindow() + { + GetWindow("Rivet Plugin"); + } + + // Add a variable to hold the current state + public IState currentState; + + // Add a method to handle the state transitions + public void TransitionToState(IState newState) + { + currentState = newState; + currentState.OnEnter(this); + } + + void OnGUI() + { + // Call the OnGUI method of the current state + currentState.OnGUI(); + } + + void OnEnable() + { + // Initialize the state machine + TransitionToState(new Installer()); + } + } +} \ No newline at end of file diff --git a/Assets/Rivet/Editor/RivetPlugin.cs.meta b/Assets/Rivet/Editor/RivetPlugin.cs.meta new file mode 100644 index 0000000..6451381 --- /dev/null +++ b/Assets/Rivet/Editor/RivetPlugin.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d2c369bd409046d288bb3600eaa5c79 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor/RivetThread.cs b/Assets/Rivet/Editor/RivetThread.cs new file mode 100644 index 0000000..5e907e9 --- /dev/null +++ b/Assets/Rivet/Editor/RivetThread.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading; + +public class RivetThread +{ + public object _lock = new object(); + public Thread _thread; + public object _output; + + public object Output + { + get + { + lock (_lock) + { + return _output; + } + } + private set + { + lock (_lock) + { + _output = value; + } + } + } + + public RivetThread(Action action) + { + _thread = new Thread(() => + { + // var result = action(); + // Output = result; + }); + _thread.Start(); + } + + public void WaitToFinish() + { + _thread.Join(); + } +} \ No newline at end of file diff --git a/Assets/Rivet/Editor/RivetThread.cs.meta b/Assets/Rivet/Editor/RivetThread.cs.meta new file mode 100644 index 0000000..8d7b8d8 --- /dev/null +++ b/Assets/Rivet/Editor/RivetThread.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8639b3eea24644577853dc347ca5030e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor/Windows.meta b/Assets/Rivet/Editor/Windows.meta new file mode 100644 index 0000000..a3314c0 --- /dev/null +++ b/Assets/Rivet/Editor/Windows.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1ad51a974ce3f4d979ad6b27d2e252ba +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor/Windows/Installer.cs b/Assets/Rivet/Editor/Windows/Installer.cs new file mode 100644 index 0000000..442e394 --- /dev/null +++ b/Assets/Rivet/Editor/Windows/Installer.cs @@ -0,0 +1,108 @@ +using UnityEngine; +using UnityEditor; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using System.Collections; +using Newtonsoft.Json.Linq; + +namespace Rivet +{ + public class Installer : RivetPluginWindow.IState + { + public RivetPluginWindow window; + public string installLabelText; + public bool installButtonEnabled; + + public Installer() + { + } + + public void OnEnter(RivetPluginWindow pluginWindow) + { + new System.Threading.Thread(() => + { + + // var api_address = apiEndpointLineEdit.Text; // replace with your actual control + var api_address = "https://api.rivet.gg"; + + var getLinkResult = RivetCLI.RunCommand( + "--api-endpoint", + api_address, + "sidekick", + "get-namespace-development-token", + "--namespace", + "staging" + ); + + switch (getLinkResult) + { + case SuccessResult successResult: + var token = successResult.Data["Ok"]["token"].ToString(); + break; + case ErrorResult errorResult: + UnityEngine.Debug.LogError(errorResult.Message); + break; + } + + }).Start(); + + this.window = pluginWindow; + + + // Prepare the installer + installLabelText = ReplacePlaceholders("%%version%% %%bin_dir%%"); + installButtonEnabled = false; + + // Start a new thread + new System.Threading.Thread(() => + { + if (RivetCLI.CLIInstalled()) + { + // Change to the Login screen + window.TransitionToState(new Login()); + } + else + { + installButtonEnabled = true; + } + }).Start(); + } + + public void OnGUI() + { + // Code to run every frame in the Installer state + EditorGUILayout.LabelField(installLabelText); + EditorGUI.BeginDisabledGroup(!installButtonEnabled); + if (GUILayout.Button("Install")) + { + // Handle the Install button click + new System.Threading.Thread(() => + { + // window.TransitionToState(new LoginState(window)); + // return; + var result = RivetCLI._install(); + + switch (result) + { + case SuccessResult successResult: + // Change to the Login screen + window.TransitionToState(new Login()); + break; + case ErrorResult errorResult: + // Debug the error + UnityEngine.Debug.LogError(errorResult.Message); + break; + } + }).Start(); + } + EditorGUI.EndDisabledGroup(); + } + + public string ReplacePlaceholders(string text) + { + return text.Replace("%%version%%", RivetCLI.REQUIRED_RIVET_CLI_VERSION) + .Replace("%%bin_dir%%", RivetCLI.GetBinDir()); + } + } +} \ No newline at end of file diff --git a/Assets/Rivet/Editor/Windows/Installer.cs.meta b/Assets/Rivet/Editor/Windows/Installer.cs.meta new file mode 100644 index 0000000..5b1e341 --- /dev/null +++ b/Assets/Rivet/Editor/Windows/Installer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5f0ad3329b8084d5f9a5539b2eef9fa4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor/Windows/Loading.cs b/Assets/Rivet/Editor/Windows/Loading.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/Rivet/Editor/Windows/Loading.cs.meta b/Assets/Rivet/Editor/Windows/Loading.cs.meta new file mode 100644 index 0000000..96faf30 --- /dev/null +++ b/Assets/Rivet/Editor/Windows/Loading.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 69340115a29fc4adb84e12ca0ae1398c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor/Windows/Login.cs b/Assets/Rivet/Editor/Windows/Login.cs new file mode 100644 index 0000000..d2843e8 --- /dev/null +++ b/Assets/Rivet/Editor/Windows/Login.cs @@ -0,0 +1,142 @@ +using UnityEngine; +using UnityEditor; +using System.Collections; +using Newtonsoft.Json.Linq; + +namespace Rivet +{ + public class Login : RivetPluginWindow.IState + { + public RivetPluginWindow window; + public string url; + bool loginButtonEnabled = true; + + public Login() + { + } + + public void OnEnter(RivetPluginWindow pluginWindow) + { + // Initialize the URL + url = ""; + + this.window = pluginWindow; + + // First, check if we're already logged in + var result = RivetCLI.RunCommand( + "sidekick", + "check-login-state"); + + switch (result) + { + case SuccessResult getLinkSuccessResult: + if (getLinkSuccessResult.Data["Ok"] == null) + { + // RivetPluginBridge.DisplayCliError(result); TODO: + UnityEngine.Debug.LogError("Error: " + result.Data); + loginButtonEnabled = true; + return; + } + + window.TransitionToState(new Plugin()); + + break; + } + } + + public void OnGUI() + { + // Display the URL text box and the Link button + EditorGUILayout.LabelField("Enter the URL:"); + url = EditorGUILayout.TextField(url); + + UnityEditor.EditorGUI.BeginDisabledGroup(!loginButtonEnabled); + + if (GUILayout.Button("Sign in to Rivet")) + { + new System.Threading.Thread(() => + { + // Disable the button sign in button + loginButtonEnabled = false; + + // var api_address = apiEndpointLineEdit.Text; // replace with your actual control + var api_address = "https://api.rivet.gg"; + + var getLinkResult = RivetCLI.RunCommand( + "--api-endpoint", + api_address, + "sidekick", + "get-link"); + + switch (getLinkResult) + { + case SuccessResult getLinkSuccessResult: + // Verify that the version that came back is correct + if (getLinkSuccessResult.Data["Ok"] == null) + { + // RivetPluginBridge.DisplayCliError(result); TODO: + UnityEngine.Debug.LogError("Error: " + getLinkResult.Data); + loginButtonEnabled = true; + return; + } + + var data = getLinkSuccessResult.Data["Ok"]; + + // Now that we have the link, open it in the user's browser + EditorApplication.delayCall += () => + { + Application.OpenURL(data["device_link_url"].ToString()); + }; + + // Long-poll the Rivet API until the user has logged in + var waitForLoginResult = RivetCLI.RunCommand( + "--api-endpoint", + api_address, + "sidekick", + "wait-for-login", + "--device-link-token", + data["device_link_token"].ToString()); + + switch (getLinkResult) + { + case SuccessResult waitForLoginSuccessResult: + if (waitForLoginSuccessResult.Data["Ok"] == null) + { + // RivetPluginBridge.DisplayCliError(result); TODO: + UnityEngine.Debug.LogError("Error: " + getLinkResult.Data); + loginButtonEnabled = true; + return; + } + + window.TransitionToState(new Plugin()); + + break; + + case ErrorResult waitForLoginErrorResult: + // RivetPluginBridge.DisplayCliError(result); TODO: + UnityEngine.Debug.LogError("Error: " + getLinkResult.Data); + loginButtonEnabled = true; + return; + + } + + break; + + case ErrorResult errorResult: + // RivetPluginBridge.DisplayCliError(result); TODO: + UnityEngine.Debug.LogError("Error: " + getLinkResult.Data); + loginButtonEnabled = true; + return; + default: + UnityEngine.Debug.LogError("Error: " + getLinkResult.Data); + loginButtonEnabled = true; + return; + } + }).Start(); + } + + UnityEditor.EditorGUI.EndDisabledGroup(); + + } + } +} \ No newline at end of file diff --git a/Assets/Rivet/Editor/Windows/Login.cs.meta b/Assets/Rivet/Editor/Windows/Login.cs.meta new file mode 100644 index 0000000..693cd8f --- /dev/null +++ b/Assets/Rivet/Editor/Windows/Login.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 842ef04ebec2947d2ac7251e1d94731d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Editor/Windows/Plugin.cs b/Assets/Rivet/Editor/Windows/Plugin.cs new file mode 100644 index 0000000..3091412 --- /dev/null +++ b/Assets/Rivet/Editor/Windows/Plugin.cs @@ -0,0 +1,386 @@ +using UnityEngine; +using UnityEditor; +using Newtonsoft.Json.Linq; +using System.Linq; +using Newtonsoft.Json; +using System.Net.Http; +using System.Net; +using System.Collections.Generic; +using System.Security.Cryptography; + +public struct BootstrapData +{ + [JsonProperty("api_endpoint")] public string ApiEndpoint; + [JsonProperty("game_id")] public string GameId; + [JsonProperty("token")] public string Token; +} + +public class Region +{ + public string provider; + public string provider_display_name; + public string region_display_name; + public string region_id; + public string region_name_id; + public string universal_region; +} + +public class Namespace +{ + public string create_ts; + public string display_name; + public string name_id; + public string namespace_id; + public string version_id; +} + +public class Version +{ + public string create_ts; + public string display_name; + public string version_id; +} + +public class Game +{ + public List available_regions; + public string create_ts; + public string developer_group_id; + public string display_name; + public string game_id; + public string name_id; + public List namespaces; + public int total_player_count; + public List versions; +} + +public class Watch +{ + public string index; +} + +public class Root +{ + public Game game; + public Watch watch; +} + +public class GameData +{ + public List<(Namespace, Version)> namespaces; +} + +namespace Rivet +{ + public class Plugin : RivetPluginWindow.IState + { + public Texture logoTexture; // Assign this in the Unity editor + public GameData gameData; + public int selectedIndex = 0; + public bool showPlaytestOptions = true; + public bool showDeployOptions = false; + public bool showSettingsOptions = false; + public RivetPluginWindow window; + public BootstrapData bootstrapData; + private bool thisMachineSelected = false; + private string rivetEditorToken = ""; + + public void OnEnter(RivetPluginWindow pluginWindow) + { + this.window = pluginWindow; + GetBootstrapData(); + } + + private void GetBootstrapData() + { + new System.Threading.Thread(() => + { + var getBootstrapResult = RivetCLI.RunCommand( + "sidekick", + "get-bootstrap-data" + ); + + switch (getBootstrapResult) + { + case SuccessResult successResult: + var data = successResult.Data["Ok"]; + // TODO: Deserialize this better + bootstrapData = new BootstrapData + { + ApiEndpoint = data["api_endpoint"].ToString(), + GameId = data["game_id"].ToString(), + Token = data["token"].ToString() + }; + + Debug.Log("Bootstrap data: " + bootstrapData.ApiEndpoint + " " + bootstrapData.GameId + " " + bootstrapData.Token); + + // Fetch plugin data + gameData = FetchPluginData(); + + break; + case ErrorResult errorResult: + UnityEngine.Debug.LogError(errorResult.Message); + break; + } + }).Start(); + } + + public GameData FetchPluginData() + { + using var httpClient = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Get, $"{bootstrapData.ApiEndpoint}/cloud/games/{bootstrapData.GameId}"); + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", bootstrapData.Token); + var response = httpClient.SendAsync(request).Result; + + if (response.StatusCode != HttpStatusCode.OK) + { + Debug.LogError("Failed to fetch plugin data"); + return null; + } + + var responseBody = response.Content.ReadAsStringAsync().Result; + var root = JsonConvert.DeserializeObject(responseBody); + + var gameData = new GameData + { + namespaces = new List<(Namespace, Version)>() + }; + + foreach (var ns in root.game.namespaces) + { + var version = root.game.versions.FirstOrDefault(version => version.version_id == ns.version_id); + + if (version != null) + { + gameData.namespaces.Add((ns, version)); + } + } + + // Return the data to the main thread + return gameData; + } + + public void OnGUI() + { + // if (GUILayout.Button("Build and Deploy")) + // { + // // Check if Linux build support is installed + // if (!BuildPipeline.IsBuildTargetSupported(BuildTargetGroup.Standalone, BuildTarget.StandaloneLinux64)) + // { + // Debug.LogError("Linux build support is not installed"); + // // return; + // } + + // // Set the build settings + // BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions + // { + // scenes = EditorBuildSettings.scenes.Select(scene => scene.path).ToArray(), + // locationPathName = "build/LinuxServer/LinuxServer.x86_64", // Output path + // target = BuildTarget.StandaloneLinux64, // Target platform + // subtarget = (int)StandaloneBuildSubtarget.Server // Headless mode for server build + // }; + + // // Build the player + // BuildPipeline.BuildPlayer(buildPlayerOptions); + + // // Run deploy with CLI + // new System.Threading.Thread(() => + // { + // var result = RivetCLI.RunCommand( + // "sidekick", + // "--show-terminal", + // "deploy", + // "--namespace", + // gameData.namespaces[selectedIndex].Item1.name_id + // ); + // }).Start(); + // } + // return; + + // The existing GUI code goes here + // Debug.LogError("Start 1"); + GUILayout.BeginVertical(); + + try + { + + // Logo + GUILayout.Label(logoTexture, GUILayout.Width(200), GUILayout.Height(200)); + + // Horizontal line + GUILayout.Label("", GUI.skin.horizontalSlider); + + // Links + GUILayout.BeginHorizontal(); + if (GUILayout.Button("Hub")) Application.OpenURL("https://hub.rivet.gg/"); + if (GUILayout.Button("Docs")) Application.OpenURL("https://rivet.gg/docs"); + if (GUILayout.Button("Discord")) Application.OpenURL("https://rivet.gg/discord"); + GUILayout.EndHorizontal(); + + // Buttons + if (GUILayout.Button("Playtest")) { showPlaytestOptions = true; showDeployOptions = false; showSettingsOptions = false; } + if (GUILayout.Button("Deploy")) { showDeployOptions = true; showPlaytestOptions = false; showSettingsOptions = false; } + if (GUILayout.Button("Settings")) { showSettingsOptions = true; showDeployOptions = false; showPlaytestOptions = false; } + + // Playtest options + if (showPlaytestOptions) + { + GUILayout.Label("Server"); + GUIStyle buttonStyle = new GUIStyle(GUI.skin.button); + GUIStyle toggleButtonStyle = new GUIStyle(GUI.skin.button) { normal = GUI.skin.button.active }; + + + GUILayout.BeginHorizontal(); + bool thisMachineClicked = GUILayout.Button("This machine", thisMachineSelected ? toggleButtonStyle : buttonStyle); + if (thisMachineClicked) thisMachineSelected = true; + + bool rivetServersClicked = GUILayout.Button("Rivet servers", !thisMachineSelected ? toggleButtonStyle : buttonStyle); + if (rivetServersClicked) thisMachineSelected = false; + GUILayout.EndHorizontal(); + + GUILayout.Label("Namespace"); + if (gameData == null) + { + GUILayout.Label("Loading..."); + } + else + { + var namespaces = gameData.namespaces.Select(space => space.Item1.display_name).ToArray(); + if (namespaces.Length == 0) + { + GUILayout.Label("No namespaces found"); + } + int oldSelectedIndex = selectedIndex; + selectedIndex = EditorGUILayout.Popup(selectedIndex, namespaces); + + if (thisMachineClicked || rivetServersClicked || oldSelectedIndex != selectedIndex) + { + GetNamespaceToken(); + } + } + } + + // Deploy options + if (showDeployOptions) + { + GUILayout.Label("Build and deploy server"); + if (gameData == null) + { + GUILayout.Label("Loading..."); + } + else + { + var namespaces = gameData.namespaces.Select(space => space.Item1.display_name).ToArray(); + if (namespaces.Length == 0) + { + GUILayout.Label("No namespaces found"); + } + selectedIndex = EditorGUILayout.Popup(selectedIndex, namespaces); + } + + if (GUILayout.Button("Build and Deploy")) + { + // Check if Linux build support is installed + if (!BuildPipeline.IsBuildTargetSupported(BuildTargetGroup.Standalone, BuildTarget.StandaloneLinux64)) + { + Debug.LogError("Linux build support is not installed"); + // return; + } + + // Set the build settings + BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions + { + scenes = EditorBuildSettings.scenes.Select(scene => scene.path).ToArray(), + locationPathName = "builds/LinuxServer/LinuxServer.x86_64", // Output path + target = BuildTarget.StandaloneLinux64, // Target platform + subtarget = (int)StandaloneBuildSubtarget.Server // Headless mode for server build + }; + + // Build the player + var result = BuildPipeline.BuildPlayer(buildPlayerOptions); + Debug.Log("Build result: " + result); + + // Run deploy with CLI + new System.Threading.Thread(() => + { + var result = RivetCLI.RunCommand( + "sidekick", + "--show-terminal", + "deploy", + "--namespace", + gameData.namespaces[selectedIndex].Item1.name_id + ); + }).Start(); + + return; + } + } + + // Settings options + if (showSettingsOptions) + { + if (GUILayout.Button("Unlink game")) + { + new System.Threading.Thread(() => + { + // First, check if we're already logged in + var result = RivetCLI.RunCommand( + "unlink"); + + switch (result) + { + case SuccessResult getLinkSuccessResult: + if (getLinkSuccessResult.Data["Ok"] == null) + { + // RivetPluginBridge.DisplayCliError(result); TODO: + UnityEngine.Debug.LogError("Error: " + result.Data); + return; + } + + break; + } + + window.TransitionToState(new Login()); + }).Start(); + }; + } + } + finally + { + GUILayout.EndVertical(); + } + } + + /// + /// Retrieves the namespace token based on the selected machine and namespace ID. + /// + private void GetNamespaceToken() + { + new System.Threading.Thread(() => + { + var command = thisMachineSelected ? "get-namespace-development-token" : "get-namespace-public-token"; + var namespaceId = gameData.namespaces[selectedIndex].Item1.name_id; + + var result = RivetCLI.RunCommand("sidekick", command, "--namespace", namespaceId); + + switch (result) + { + case SuccessResult successResult: + var token = successResult.Data["Ok"]["token"].ToString(); + UnityEngine.Debug.Log("Rivet Token: " + token); + rivetEditorToken = token; + UnityEditor.EditorApplication.delayCall += () => + { + PlayerPrefs.SetString("RIVET_EDITOR_TOKEN", token); + Debug.Log("Saved token to PlayerPrefs"); + }; + break; + case ErrorResult errorResult: + UnityEngine.Debug.LogError(errorResult.Message); + break; + } + + }).Start(); + } + } +} \ No newline at end of file diff --git a/Assets/Rivet/Editor/Windows/Plugin.cs.meta b/Assets/Rivet/Editor/Windows/Plugin.cs.meta new file mode 100644 index 0000000..275eec8 --- /dev/null +++ b/Assets/Rivet/Editor/Windows/Plugin.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eab40917f63af4e008a47dd59a7d6693 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Runtime.meta b/Assets/Rivet/Runtime.meta new file mode 100644 index 0000000..0f1fc17 --- /dev/null +++ b/Assets/Rivet/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ec74c9f56be464ac1997682685d4d5fd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Runtime/RivetAPI.cs b/Assets/Rivet/Runtime/RivetAPI.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/Rivet/Runtime/RivetAPI.cs.meta b/Assets/Rivet/Runtime/RivetAPI.cs.meta new file mode 100644 index 0000000..9c1c807 --- /dev/null +++ b/Assets/Rivet/Runtime/RivetAPI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f459b84dae7b743ce84f68aed1f2b191 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Runtime/RivetHelper.cs b/Assets/Rivet/Runtime/RivetHelper.cs new file mode 100644 index 0000000..df1f195 --- /dev/null +++ b/Assets/Rivet/Runtime/RivetHelper.cs @@ -0,0 +1,69 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace Rivet +{ + public class RivetHelper : MonoBehaviour + { + public delegate void StartServer(); + public static event StartServer OnStartServer; + + public delegate void StartClient(); + public static event StartClient OnStartClient; + + private bool multiplayerSetup = false; + private Dictionary playerTokens = new Dictionary(); + private string playerToken = null; + + // Singleton instance + public static RivetHelper Instance { get; private set; } + + private void Awake() + { + // Singleton setup + if (Instance == null) + { + Instance = this; + DontDestroyOnLoad(gameObject); + } + else + { + Destroy(gameObject); + } + } + + public bool IsDedicatedServer() + { + // Check if the environment variable UNITY_SERVER is set + return System.Environment.GetEnvironmentVariable("UNITY_SERVER") == "1"; + } + + public void SetupMultiplayer() + { + Debug.Assert(!multiplayerSetup, "RivetHelper.SetupMultiplayer already called"); + multiplayerSetup = true; + + // Setup SceneMultiplayer here + + if (IsDedicatedServer()) + { + Debug.Log("Starting server"); + OnStartServer?.Invoke(); + } + else + { + Debug.Log("Starting client"); + OnStartClient?.Invoke(); + } + } + + public void SetPlayerToken(string _playerToken) + { + Debug.Assert(multiplayerSetup, "RivetHelper.SetupMultiplayer has not been called"); + Debug.Assert(!IsDedicatedServer(), "Cannot call RivetHelper.SetPlayerToken on server"); + playerToken = _playerToken; + } + + // Implement other methods here + } +} \ No newline at end of file diff --git a/Assets/Rivet/Runtime/RivetHelper.cs.meta b/Assets/Rivet/Runtime/RivetHelper.cs.meta new file mode 100644 index 0000000..334da80 --- /dev/null +++ b/Assets/Rivet/Runtime/RivetHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6522624065039444bb1273c3b1abb08c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Runtime/RivetManager.cs b/Assets/Rivet/Runtime/RivetManager.cs new file mode 100644 index 0000000..ff1a200 --- /dev/null +++ b/Assets/Rivet/Runtime/RivetManager.cs @@ -0,0 +1,383 @@ +#nullable enable +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.Serialization; +using UnityEngine; +using UnityEngine.Networking; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Linq; +using UnityEngine.Serialization; + +[JsonConverter(typeof(StringEnumConverter))] +public enum CreateLobbyRequestPublicity +{ + [EnumMember(Value = "public")] Public, + [EnumMember(Value = "private")] Private, +} + +public struct FindLobbyRequest +{ + [JsonProperty("game_modes")] public string[] GameModes; + [JsonProperty("regions")] public string[]? Regions; +} + +public struct JoinLobbyRequest +{ + [JsonProperty("lobby_id")] public string LobbyId; +} + +public struct CreateLobbyRequest +{ + [JsonProperty("game_mode")] public string GameMode; + [JsonProperty("region")] public string? Region; + [JsonProperty("publicity")] public CreateLobbyRequestPublicity Publicity; + [JsonProperty("lobby_config")] public JObject? LobbyConfig; +} + +public struct FindLobbyResponse +{ + [JsonProperty("lobby")] public RivetLobby Lobby; + [JsonProperty("ports")] public Dictionary Ports; + [JsonProperty("player")] public RivetPlayer Player; +} + +public struct RivetLobby +{ + [JsonProperty("lobby_id")] public string LobbyId; + [JsonProperty("host")] public string Host; + [JsonProperty("port")] public int Port; +} + +public struct RivetLobbyPort +{ + [JsonProperty("hostname")] public string? Hostname; + [JsonProperty("port")] public ushort Port; + [JsonProperty("is_tls")] public bool IsTls; +} + +public struct RivetPlayer +{ + [JsonProperty("token")] public string Token; +} + +[CreateAssetMenu(fileName = "RivetSettings", menuName = "ScriptableObjects/RivetSettings", order = 1)] +public class RivetSettings : ScriptableObject +{ + public string? rivetToken; +} + +public class RivetManager : MonoBehaviour +{ + const string MatchmakerApiEndpoint = "https://api.rivet.gg/matchmaker"; + + [HideInInspector] + public string? rivetToken = null; + + /// + /// The response from the last call. Used to maintain information about the Rivet player & + /// lobby. + /// + public FindLobbyResponse? FindLobbyResponse { get; private set; } + + private void Start() + { + Debug.Log("RivetManager.Start"); + // Try to set the Rivet Token from the asset if it exists + if (rivetToken == null || rivetToken.Length == 0) + { + var rivetSettings = Resources.Load("RivetSettings"); + if (rivetSettings != null) + { + Debug.Log("RivetSettings: " + rivetSettings.rivetToken); + rivetToken = rivetSettings.rivetToken; + } + } + } + + #region API: Matchmaker.Lobbies + + /// + /// Documentation + /// + /// + /// + /// + /// + /// + public IEnumerator FindLobby(FindLobbyRequest request, Action success, + Action fail) + { + yield return PostRequest(MatchmakerApiEndpoint + "/lobbies/find", + request, res => + { + // Save response + FindLobbyResponse = res; + + + + success(res); + }, fail); + } + + /// + /// Documentation + /// + /// + /// + /// + /// + /// + public IEnumerator JoinLobby(JoinLobbyRequest request, Action success, + Action fail) + { + yield return PostRequest(MatchmakerApiEndpoint + "/lobbies/join", + request, res => + { + // Save response + FindLobbyResponse = res; + + // // Connect to server + // var port = res.Ports["default"]; + // Debug.Log("Connecting to " + port.Hostname + ":" + port.Port); + // _networkManager.ClientManager.StartConnection(port.Hostname, port.Port); + + success(res); + }, fail); + } + + /// + /// Documentation + /// + /// + /// + /// + /// + /// + public IEnumerator CreateLobby(CreateLobbyRequest request, Action success, + Action fail) + { + yield return PostRequest(MatchmakerApiEndpoint + "/lobbies/create", + request, res => + { + // Save response + FindLobbyResponse = res; + + // // Connect to server + // var port = res.Ports["default"]; + // Debug.Log("Connecting to " + port.Hostname + ":" + port.Port); + // _networkManager.ClientManager.StartConnection(port.Hostname, port.Port); + + success(res); + }, fail); + } + + /// + /// Documentation + /// + /// + /// + /// + /// + public IEnumerator LobbyReady(Action success, Action fail) + { + yield return PostRequest, object>(MatchmakerApiEndpoint + "/lobbies/ready", + new Dictionary(), (_) => success(), fail); + } + + #endregion + + #region API: Matchmaker.Players + + /// + /// Documentation + /// + /// + /// + /// + /// + /// + public IEnumerator PlayerConnected(string playerToken, Action success, Action fail) + { + yield return PostRequest, object>( + MatchmakerApiEndpoint + "/players/connected", + new Dictionary + { + { "player_token", playerToken }, + }, (_) => success(), fail); + } + + /// + /// Documentation + /// + /// + /// + /// + /// + /// + public IEnumerator PlayerDisconnected(string playerToken, Action success, Action fail) + { + yield return PostRequest, object>( + MatchmakerApiEndpoint + "/players/disconnected", new Dictionary + { + { "player_token", playerToken }, + }, (_) => success(), fail); + } + + #endregion + + #region API Requests + + private string GetToken() + { + // Check which environment we are in +#if UNITY_SERVER + Debug.Log("UNITY_SERVER"); + Debug.Log(Environment.GetEnvironmentVariable("RIVET_TOKEN")); +#endif +#if IN_EDITOR + Debug.Log("IN_EDITOR"); + string value = PlayerPrefs.GetString("RIVET_EDITOR_TOKEN"); + Debug.Log("Editor token: " + value); +#endif + +#if UNITY_SERVER + var token = Environment.GetEnvironmentVariable("RIVET_TOKEN"); + if (token != null) + { + return token; + } +#endif + + // Try loading from PlayerPrefs + string value = PlayerPrefs.GetString("RIVET_EDITOR_TOKEN"); + if (value.Length > 0) + { + return value; + } + + if (rivetToken != null && rivetToken.Length > 0) + { + return rivetToken; + } + + throw new Exception("RIVET_TOKEN not set"); + } + + public IEnumerator PostRequest(string url, TReq requestBody, Action success, Action fail, string token = "") + { + Debug.Log("PostRequest: " + url); + if (token.Length == 0) + { + token = GetToken(); + } + + var debugRequestDescription = "POST " + url; + + var requestBodyStr = JsonConvert.SerializeObject(requestBody, + new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + Debug.Log(debugRequestDescription + " Request: " + requestBodyStr + "\n" + Environment.StackTrace); + + var www = UnityWebRequest.Post(url, requestBodyStr, "application/json"); + www.SetRequestHeader("Authorization", "Bearer " + token); + + yield return www.SendWebRequest(); + + switch (www.result) + { + case UnityWebRequest.Result.InProgress: + Debug.Log("In progress"); + break; + case UnityWebRequest.Result.Success: + if (www.responseCode == 200) + { + Debug.Log(debugRequestDescription + " Success: " + www.downloadHandler.text); + var responseBody = JsonConvert.DeserializeObject(www.downloadHandler.text); + success(responseBody!); + } + else + { + string statusError = "Error status " + www.responseCode + ": " + www.downloadHandler.text; + Debug.LogError(debugRequestDescription + " " + statusError); + fail(statusError); + } + + break; + case UnityWebRequest.Result.ConnectionError: + string connectionError = "ConnectionError: " + www.error; + Debug.LogError(debugRequestDescription + " " + connectionError + "\n" + Environment.StackTrace); + fail(connectionError); + break; + case UnityWebRequest.Result.ProtocolError: + string protocolError = "ProtocolError: " + www.error + " " + www.downloadHandler.text; + Debug.LogError(debugRequestDescription + " " + protocolError + "\n" + Environment.StackTrace); + fail(protocolError); + break; + case UnityWebRequest.Result.DataProcessingError: + string dpe = "DataProcessingError: " + www.error; + Debug.LogError(debugRequestDescription + " " + dpe + "\n" + Environment.StackTrace); + fail(dpe); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public IEnumerator GetRequest(string url, Action success, Action fail, string token = "") + { + if (token.Length == 0) + { + token = GetToken(); + } + + var debugRequestDescription = "GET " + url; + Debug.Log(debugRequestDescription + "\n" + Environment.StackTrace); + + var www = UnityWebRequest.Get(url); + www.SetRequestHeader("Authorization", "Bearer " + GetToken()); + + yield return www.SendWebRequest(); + + switch (www.result) + { + case UnityWebRequest.Result.InProgress: + Debug.Log("In progress"); + break; + case UnityWebRequest.Result.Success: + if (www.responseCode == 200) + { + Debug.Log(debugRequestDescription + " Success: " + www.downloadHandler.text); + var responseBody = JsonConvert.DeserializeObject(www.downloadHandler.text); + success(responseBody!); + } + else + { + string statusError = "Error status " + www.responseCode + ": " + www.downloadHandler.text; + Debug.LogError(debugRequestDescription + " " + statusError); + fail(statusError); + } + + break; + case UnityWebRequest.Result.ConnectionError: + string connectionError = "ConnectionError: " + www.error; + Debug.LogError(debugRequestDescription + " " + connectionError + "\n" + Environment.StackTrace); + fail(connectionError); + break; + case UnityWebRequest.Result.ProtocolError: + string protocolError = "ProtocolError: " + www.error + " " + www.downloadHandler.text; + Debug.LogError(debugRequestDescription + " " + protocolError + "\n" + Environment.StackTrace); + fail(protocolError); + break; + case UnityWebRequest.Result.DataProcessingError: + string dpe = "DataProcessingError: " + www.error; + Debug.LogError(debugRequestDescription + " " + dpe + "\n" + Environment.StackTrace); + fail(dpe); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + #endregion +} \ No newline at end of file diff --git a/Assets/Rivet/Runtime/RivetManager.cs.meta b/Assets/Rivet/Runtime/RivetManager.cs.meta new file mode 100644 index 0000000..955eb8e --- /dev/null +++ b/Assets/Rivet/Runtime/RivetManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 968401380e77644ab8c094fa37084533 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Runtime/RivetManager.prefab b/Assets/Rivet/Runtime/RivetManager.prefab new file mode 100644 index 0000000..09b0d5c --- /dev/null +++ b/Assets/Rivet/Runtime/RivetManager.prefab @@ -0,0 +1,48 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &8128138867176465573 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5067471196427801089} + - component: {fileID: 5429124248067667519} + m_Layer: 0 + m_Name: RivetManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 2147483647 + m_IsActive: 1 +--- !u!4 &5067471196427801089 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8128138867176465573} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &5429124248067667519 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8128138867176465573} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 968401380e77644ab8c094fa37084533, type: 3} + m_Name: + m_EditorClassIdentifier: + rivetToken: + gameModeName: diff --git a/Assets/Rivet/Runtime/RivetManager.prefab.meta b/Assets/Rivet/Runtime/RivetManager.prefab.meta new file mode 100644 index 0000000..9acc544 --- /dev/null +++ b/Assets/Rivet/Runtime/RivetManager.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1757080393b12a4fda212c0494ff52f1 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/Runtime/RivetRuntime.cs b/Assets/Rivet/Runtime/RivetRuntime.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/Rivet/Runtime/RivetRuntime.cs.meta b/Assets/Rivet/Runtime/RivetRuntime.cs.meta new file mode 100644 index 0000000..326af3e --- /dev/null +++ b/Assets/Rivet/Runtime/RivetRuntime.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5e6c4d24fa99541398fd4eeb6cd6f707 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Rivet/package.json b/Assets/Rivet/package.json new file mode 100644 index 0000000..d990de3 --- /dev/null +++ b/Assets/Rivet/package.json @@ -0,0 +1,16 @@ +{ + "name": "com.rivet.rivet", + "displayName": "Rivet", + "version": "24.0.0", + "unity": "2021.3", + "description": "Description of the Rivet package", + "keywords": ["rivet", "unity", "package"], + "author": { + "name": "Forest Anderson", + "email": "forest@rivet.gg", + "url": "https://rivet.gg" + }, + "dependencies": { + "com.unity.nuget.newtonsoft-json": "2.0.2" + } +} diff --git a/Assets/Rivet/package.json.meta b/Assets/Rivet/package.json.meta new file mode 100644 index 0000000..4d7c8fd --- /dev/null +++ b/Assets/Rivet/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0044b76306aa49a93a9b09e523a3a59d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes.meta b/Assets/Scenes.meta new file mode 100644 index 0000000..e138ea0 --- /dev/null +++ b/Assets/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 272822135ab674879abb099a52f87a0a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity new file mode 100644 index 0000000..9421266 --- /dev/null +++ b/Assets/Scenes/SampleScene.unity @@ -0,0 +1,208 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 3 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 0 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 0 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 0 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &519420028 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 519420032} + - component: {fileID: 519420031} + - component: {fileID: 519420029} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &519420029 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 +--- !u!20 &519420031 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 2 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 1 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 0 + m_HDR: 1 + m_AllowMSAA: 0 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 0 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &519420032 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 519420028} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Scenes/SampleScene.unity.meta b/Assets/Scenes/SampleScene.unity.meta new file mode 100644 index 0000000..c1e3c88 --- /dev/null +++ b/Assets/Scenes/SampleScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2cda990e2423bbf4892e6590ba056729 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ComingSoon.cs b/ComingSoon.cs deleted file mode 100644 index 2878e3e..0000000 --- a/ComingSoon.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -class Program -{ - static void Main() - { - Console.WriteLine("Coming soon..."); - } -} - diff --git a/Packages/manifest.json b/Packages/manifest.json new file mode 100644 index 0000000..866c11f --- /dev/null +++ b/Packages/manifest.json @@ -0,0 +1,46 @@ +{ + "dependencies": { + "com.unity.collab-proxy": "2.3.1", + "com.unity.feature.2d": "2.0.0", + "com.unity.ide.rider": "3.0.27", + "com.unity.ide.visualstudio": "2.0.22", + "com.unity.nuget.newtonsoft-json": "3.2.1", + "com.unity.test-framework": "1.1.33", + "com.unity.textmeshpro": "3.0.6", + "com.unity.timeline": "1.7.6", + "com.unity.toolchain.macos-arm64-linux-x86_64": "2.0.0", + "com.unity.ugui": "1.0.0", + "com.unity.visualscripting": "1.9.1", + "com.unity.modules.ai": "1.0.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.cloth": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.screencapture": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.umbra": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } +} diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json new file mode 100644 index 0000000..2eacf77 --- /dev/null +++ b/Packages/packages-lock.json @@ -0,0 +1,499 @@ +{ + "dependencies": { + "com.unity.2d.animation": { + "version": "9.1.0", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.2d.common": "8.0.2", + "com.unity.2d.sprite": "1.0.0", + "com.unity.collections": "1.1.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.uielements": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.2d.aseprite": { + "version": "1.1.0", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.2d.sprite": "1.0.0", + "com.unity.2d.common": "6.0.6", + "com.unity.mathematics": "1.2.6", + "com.unity.modules.animation": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.2d.common": { + "version": "8.0.2", + "depth": 2, + "source": "registry", + "dependencies": { + "com.unity.2d.sprite": "1.0.0", + "com.unity.mathematics": "1.1.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.burst": "1.7.3" + }, + "url": "https://packages.unity.com" + }, + "com.unity.2d.pixel-perfect": { + "version": "5.0.3", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.2d.psdimporter": { + "version": "8.0.4", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.2d.animation": "9.1.0", + "com.unity.2d.common": "8.0.2", + "com.unity.2d.sprite": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.2d.sprite": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": {} + }, + "com.unity.2d.spriteshape": { + "version": "9.0.2", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.mathematics": "1.1.0", + "com.unity.2d.common": "8.0.1", + "com.unity.modules.physics2d": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.2d.tilemap": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.uielements": "1.0.0" + } + }, + "com.unity.2d.tilemap.extras": { + "version": "3.1.2", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.modules.tilemap": "1.0.0", + "com.unity.2d.tilemap": "1.0.0", + "com.unity.ugui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.burst": { + "version": "1.8.12", + "depth": 3, + "source": "registry", + "dependencies": { + "com.unity.mathematics": "1.2.1", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.collab-proxy": { + "version": "2.3.1", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.collections": { + "version": "1.2.4", + "depth": 2, + "source": "registry", + "dependencies": { + "com.unity.burst": "1.6.6", + "com.unity.test-framework": "1.1.31" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ext.nunit": { + "version": "1.0.6", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.feature.2d": { + "version": "2.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.2d.animation": "9.1.0", + "com.unity.2d.pixel-perfect": "5.0.3", + "com.unity.2d.psdimporter": "8.0.4", + "com.unity.2d.sprite": "1.0.0", + "com.unity.2d.spriteshape": "9.0.2", + "com.unity.2d.tilemap": "1.0.0", + "com.unity.2d.tilemap.extras": "3.1.2", + "com.unity.2d.aseprite": "1.1.0" + } + }, + "com.unity.ide.rider": { + "version": "3.0.27", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ide.visualstudio": { + "version": "2.0.22", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.9" + }, + "url": "https://packages.unity.com" + }, + "com.unity.mathematics": { + "version": "1.2.6", + "depth": 2, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.nuget.newtonsoft-json": { + "version": "3.2.1", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.sysroot": { + "version": "2.0.7", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.sysroot.linux-x86_64": { + "version": "2.0.6", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.sysroot": "2.0.7" + }, + "url": "https://packages.unity.com" + }, + "com.unity.test-framework": { + "version": "1.1.33", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.textmeshpro": { + "version": "3.0.6", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.timeline": { + "version": "1.7.6", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.director": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.toolchain.macos-arm64-linux-x86_64": { + "version": "2.0.0", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.sysroot": "2.0.7", + "com.unity.sysroot.linux-x86_64": "2.0.6" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ugui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0" + } + }, + "com.unity.visualscripting": { + "version": "1.9.1", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.modules.ai": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.androidjni": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.animation": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.assetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.audio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.cloth": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.director": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.animation": "1.0.0" + } + }, + "com.unity.modules.imageconversion": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.imgui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.jsonserialize": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.particlesystem": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics2d": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.screencapture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.subsystems": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.terrain": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.terrainphysics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.terrain": "1.0.0" + } + }, + "com.unity.modules.tilemap": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics2d": "1.0.0" + } + }, + "com.unity.modules.ui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.uielements": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.umbra": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unityanalytics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.unitywebrequest": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unitywebrequestassetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestaudio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.audio": "1.0.0" + } + }, + "com.unity.modules.unitywebrequesttexture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestwww": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.vehicles": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.video": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.vr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } + }, + "com.unity.modules.wind": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.xr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.subsystems": "1.0.0" + } + } + } +}