Skip to content

Dev#159

Open
GarethIW wants to merge 23 commits intomainfrom
dev
Open

Dev#159
GarethIW wants to merge 23 commits intomainfrom
dev

Conversation

@GarethIW
Copy link
Member

@GarethIW GarethIW commented Mar 11, 2026

Summary by CodeRabbit

  • New Features

    • Added uninstall functionality for Banter package
    • Added streaming browser support option
    • Avatar creation and upload controls now visible by default
  • Improvements

    • Enhanced YouTube data processing with extended content information
    • Improved scene loading and synchronization
    • Updated networking infrastructure for enhanced reliability

@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

📝 Walkthrough

Walkthrough

This PR introduces widespread networking upgrades by migrating HTTP calls from UnityWebRequest to HttpClient across editor services, adds YouTube video metadata support with new serializable data models, refactors message flow timing in runtime components, updates avatar management UI visibility, and implements a package uninstaller command.

Changes

Cohort / File(s) Summary
HTTP Client Migration
Editor/Resources/Builder/BuilderWindow.cs, Editor/Resources/Builder/SideQuest/SqEditorAppApi.cs
Replaces UnityWebRequest-based HTTP calls with HttpClient using static instance and async-to-coroutine polling. Updates error handling to check HttpResponseMessage status codes and changes avatar retrieval endpoint from /v2/avatars?author_id=me to /v2/avatars/mine; switches PATCH to PUT for avatar updates.
Avatar Management UI
Editor/Resources/Builder/BuilderWindow.uxml
Changes avatar creation/upload controls from hidden (display: none) to visible (display: flex) by default, affecting Label and DropdownField initial render state.
Package Management
Editor/Resources/Builder/MenuUtils.cs
Adds new UninstallBanter() editor menu command that prompts user confirmation, removes package via PackageManager API, deletes related directories, removes compile defines, and re-opens project.
YouTube Video Metadata
Runtime/Scripts/Models/YtInfo/YtThumbnail.cs, Runtime/Scripts/Models/YtInfo/YtVideoDetails.cs, Runtime/Scripts/Models/YtInfo/YtResponseContext.cs
Introduces serializable data models for YouTube video details (videoId, title, channelId, lengthSeconds, etc.) and thumbnail containers; adds videoDetails field to YtResponseContext to support expanded metadata parsing.
YouTube Content Processing
Runtime/Scripts/Scene/BanterScene.cs
Adds defensive sanitization of deserialized YouTube metadata: removes vertical bars from title/thumbnail URLs, replaces vertical bars with spaces in shortDescription, strips newlines, includes debug logging for load operations.
Runtime Lifecycle & Message Flow
Runtime/Scripts/BanterLink/BanterLink.cs, Runtime/Scripts/BanterLink/BanterPipe.cs, Runtime/Scripts/BanterStarterUpper.cs, Runtime/Scripts/Utils/DontDestroyOnLoad.cs
Comments out DOM_READY state transition; adds 2-second delay before SCENE_READY followed by WaitUntil scene load with additional logging; removes debug statements from BanterPipe and BanterStarterUpper; adds DefaultExecutionOrder(-9999) attribute and converts DontDestroyOnLoad from Start() to Awake() with gameObject parameter.
Streaming Browser Support
Runtime/Scripts/Scene/Components/BanterBrowser.cs
Adds IsStreamingBrowser public boolean field to conditionally instantiate either Prefabs/BanterBrowserStreaming or Prefabs/BanterBrowserBuild prefab; changes RunActions invocation to pass empty string if actions is null/empty.
Code Cleanup & Refactoring
Runtime/Scripts/Utils/AddPanelStuff.cs, Editor/Scripts/VisualScripting/VsStubsAllowed.cs, Editor/SDKInitialiseOnLoad.cs
Removes debug logging statements in AddPanelStuff; removes extensive UnityEngine.UI and timeline-related string entries from VsStubsAllowed array; normalizes spacing in conditional and adds commented-out CreateUninstaller block in SDKInitialiseOnLoad with no functional behavior changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Re-implement cdn file upload with HttpClient #157 — Modifies SqEditorAppApi.cs to replace UnityWebRequest uploads with HttpClient-based implementation using shared HttpClient instance.
  • Dev #126 — Modifies BuilderWindow HTTP coroutines and SqEditorAppApi request helpers, replacing UnityWebRequest with HttpClient networking.
  • Dev #143 — Modifies Editor/Resources/Builder/BuilderWindow.cs for UI/API changes in the same file as the HTTP client migration.

Suggested reviewers

  • sidequestlegend

Poem

🐰 The networks hop through HttpClient's gate,
YouTube streams and avatars await,
Async dances flow with gentle grace,
Cleaner code at brisker pace,
SDK springs to brighter days! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Dev' is vague and does not meaningfully convey the changes in this multi-file pull request that includes HTTP client migrations, API updates, UI changes, and model additions. Replace with a descriptive title summarizing the main change, such as 'Migrate HTTP requests from UnityWebRequest to HttpClient' or 'Refactor networking and add YouTube video details support'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (7)
Editor/SDKInitialiseOnLoad.cs (1)

39-39: Remove the commented-out uninstaller scaffold.

The real uninstall flow now lives in Editor/Resources/Builder/MenuUtils.cs. Keeping a second, fully commented copy here has already started to drift (pacakge.json, different cleanup steps) and makes the initializer much harder to scan.

Also applies to: 73-156

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/SDKInitialiseOnLoad.cs` at line 39, Remove the commented-out
uninstaller scaffold from Editor/SDKInitialiseOnLoad.cs: delete the commented
line "// CreateUninstaller();" and the larger commented block spanning the
existing uninstaller scaffold (previously lines 73-156) so the initializer no
longer contains the outdated, duplicated uninstall logic; rely on the real
uninstall flow in Editor/Resources/Builder/MenuUtils.cs and ensure no other
commented duplicate functions (e.g., CreateUninstaller) remain in this file.
Editor/Resources/Builder/SideQuest/SqEditorAppApi.cs (2)

719-759: HttpRequestMessage and HttpResponseMessage not disposed.

Same resource management concern as other methods. Both objects should be disposed after use.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/Resources/Builder/SideQuest/SqEditorAppApi.cs` around lines 719 - 759,
JsonGet currently creates an HttpRequestMessage (request) and obtains an
HttpResponseMessage (response) without disposing them; wrap request creation and
the code path that uses response in proper disposal (e.g., using blocks or
try/finally Dispose calls) so request is disposed after SendAsync and response
(and its Content if needed) is disposed after reading the content or on all
error/early-return paths; update symbols referenced (JsonGet, request,
_httpClient.SendAsync, response, response.Content, readTask) to ensure every
exit/exception path disposes those objects.

687-717: HttpResponseMessage and StringContent not disposed.

Both the content and response should be disposed. The StringContent passed to PostAsync and the returned HttpResponseMessage implement IDisposable.

Suggested pattern
 private IEnumerator PostFormEncodedStringNoAuth<T>(string urlPath, string data, Action<T> OnCompleted, Action<Exception> OnError)
 {
-    var content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded");
-    var task = _httpClient.PostAsync(new Uri(Config.RootApiUri, urlPath), content);
+    using var content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded");
+    var task = _httpClient.PostAsync(new Uri(Config.RootApiUri, urlPath), content);
     while (!task.IsCompleted) yield return null;
     // ... error handling ...
-    var response = task.Result;
+    using var response = task.Result;
     // ... rest of method
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/Resources/Builder/SideQuest/SqEditorAppApi.cs` around lines 687 - 717,
In PostFormEncodedStringNoAuth, both the created StringContent (content) and the
HttpResponseMessage (response from _httpClient.PostAsync) must be disposed; wrap
the StringContent creation in a using (or try/finally) and similarly dispose the
response after you finish using it (i.e. after awaiting/finishing
response.Content.ReadAsStringAsync and storing the result) to avoid leaking
resources; ensure you still handle task faults, non-success status codes, and
call OnError/OnCompleted as before while disposing in all control paths (use
using/try-finally around content and a using/try-finally around
response/readTask/result).
Editor/Resources/Builder/BuilderWindow.cs (3)

1189-1201: HttpResponseMessage not disposed.

The response from GetAsync should be disposed to release underlying resources (connection back to pool). Without disposal, connections may not be returned to the pool promptly.

Suggested fix using try-finally
 public IEnumerator Json<T>(string url, Action<T> callback)
 {
     var task = _httpClient.GetAsync(url);
     while (!task.IsCompleted) yield return null;
     if (task.IsFaulted) throw task.Exception.InnerException ?? task.Exception;
     var response = task.Result;
-    if (!response.IsSuccessStatusCode)
-        throw new System.Exception(response.StatusCode + ": " + response.ReasonPhrase);
-    var readTask = response.Content.ReadAsStringAsync();
-    while (!readTask.IsCompleted) yield return null;
-    if (readTask.IsFaulted) throw readTask.Exception.InnerException ?? readTask.Exception;
-    callback(JsonUtility.FromJson<T>(readTask.Result));
+    try
+    {
+        if (!response.IsSuccessStatusCode)
+            throw new System.Exception(response.StatusCode + ": " + response.ReasonPhrase);
+        var readTask = response.Content.ReadAsStringAsync();
+        while (!readTask.IsCompleted) yield return null;
+        if (readTask.IsFaulted) throw readTask.Exception.InnerException ?? readTask.Exception;
+        callback(JsonUtility.FromJson<T>(readTask.Result));
+    }
+    finally
+    {
+        response.Dispose();
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/Resources/Builder/BuilderWindow.cs` around lines 1189 - 1201, The
HttpResponseMessage returned in Json<T>(string url, Action<T> callback) is never
disposed; wrap the code that inspects response and reads its content so the
response is always disposed (e.g., assign response = task.Result, start the
readTask, await its completion, and then in a try/finally or using-equivalent
ensure response.Dispose() in the finally block). Make sure you read
response.Content.ReadAsStringAsync() and obtain readTask.Result before disposing
the response, and preserve existing error handling (throw on non-success status
or task faults).

1203-1231: HttpRequestMessage and HttpResponseMessage not disposed.

Both the request and response objects implement IDisposable and should be disposed to properly release resources. This is especially important for the StringContent within the request.

Suggested fix pattern
 public IEnumerator Json<T>(string url, T postData, Action<string> callback, Dictionary<string, string> headers = null)
 {
     var json = JsonUtility.ToJson(postData);
-    var request = new HttpRequestMessage(HttpMethod.Post, url)
-    {
-        Content = new StringContent(json, Encoding.UTF8, "application/json")
-    };
+    using var request = new HttpRequestMessage(HttpMethod.Post, url)
+    {
+        Content = new StringContent(json, Encoding.UTF8, "application/json")
+    };
     if (headers != null)
     {
         foreach (var header in headers)
         {
             if (!header.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase))
                 request.Headers.TryAddWithoutValidation(header.Key, header.Value);
         }
     }
     var task = _httpClient.SendAsync(request);
     while (!task.IsCompleted) yield return null;
     if (task.IsFaulted) throw task.Exception.InnerException ?? task.Exception;
-    var response = task.Result;
+    using var response = task.Result;
     // ... rest of the method

Note: using declarations require C# 8.0+. If using an older version, wrap in try-finally blocks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/Resources/Builder/BuilderWindow.cs` around lines 1203 - 1231, The
Json<T> coroutine creates an HttpRequestMessage (and a StringContent) and uses
HttpResponseMessage from _httpClient.SendAsync without disposing them; wrap the
HttpRequestMessage (and its Content) in a using (or try/finally Dispose) and
also ensure the HttpResponseMessage is disposed after you finish reading its
content (read response.Content.ReadAsStringAsync() and await/complete that
before disposing); update the code around HttpRequestMessage creation,
_httpClient.SendAsync call, and handling of response/response.Content (symbols:
Json<T>, HttpRequestMessage, StringContent, _httpClient.SendAsync,
HttpResponseMessage, response.Content.ReadAsStringAsync) so all IDisposable
objects are properly disposed while preserving the existing error/exception
handling and callback behavior.

53-54: Consider configuring timeout for consistency.

The HttpClient instance here lacks timeout configuration, while SqEditorAppApi.cs sets a 10-minute timeout. For image downloads and API calls, consider adding a similar timeout configuration or using a shared HttpClient instance across both files to ensure consistent behavior.

Suggested configuration
-    private static readonly HttpClient _httpClient = new HttpClient();
+    private static readonly HttpClient _httpClient = new HttpClient()
+    {
+        Timeout = TimeSpan.FromMinutes(5)
+    };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/Resources/Builder/BuilderWindow.cs` around lines 53 - 54, The static
HttpClient _httpClient in BuilderWindow.cs has no timeout set; update it to use
the same 10-minute timeout as SqEditorAppApi.cs (or refactor to reuse the shared
HttpClient used by SqEditorAppApi) by configuring the Timeout property on the
shared/static HttpClient instance (refer to the _httpClient field in
BuilderWindow and the HttpClient usage in SqEditorAppApi.cs) so both image
downloads and API calls have consistent timeout behavior.
Editor/Scripts/VisualScripting/VsStubsAllowed.cs (1)

12635-12798: Consider centralizing the 29 inherited Unity members shared across these four NavMesh types.

This block duplicates the same inherited Object/Component surface (29 members) across NavMeshLink, NavMeshModifier, NavMeshModifierVolume, and NavMeshSurface. If the allowlist matcher supports it, extracting these common members into a reusable list or type-level pattern would reduce the maintenance burden if Unity adds or renames inherited members in future releases.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/Scripts/VisualScripting/VsStubsAllowed.cs` around lines 12635 - 12798,
The four types Unity.AI.Navigation.NavMeshLink, NavMeshModifier,
NavMeshModifierVolume, and NavMeshSurface repeat the same 29 inherited
Object/Component members; extract those shared strings into a single reusable
list (e.g., commonUnityComponentMembers) and reuse/concatenate it when building
the allowlist entries for each of the four types (replace the duplicated block
in the NavMeshLink, NavMeshModifier, NavMeshModifierVolume, and NavMeshSurface
entries with a reference to that shared list or a type-level pattern), so future
changes to inherited Unity members only need to be made in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Editor/Resources/Builder/BuilderWindow.cs`:
- Around line 376-384: In the Texture coroutine, check the boolean return value
of Texture2D.LoadImage after creating tex and loading task.Result; if LoadImage
returns false, dispose/destroy the invalid Texture2D, log or surface the error,
and invoke the provided callback with null (or throw a descriptive exception)
instead of passing an invalid texture; update the Texture method (the coroutine
named Texture and its callback usage) to handle the failed load path cleanly so
callers can detect and handle image decoding failures.

In `@Editor/Resources/Builder/MenuUtils.cs`:
- Around line 50-68: UninstallBanter() currently deletes the local package
folders but leaves file: entries in Packages/manifest.json, causing broken
references; update UninstallBanter() to load Packages/manifest.json, remove the
dependencies for "com.sidequest.ora" and the "com.basis.bundlemanagement",
"com.basis.sdk", "com.basis.odinserializer" keys (the same packages added by
InitialiseOnLoad.ImportOraPackage() and ImportBasisPackages()), write the
manifest back (preserving JSON formatting), then remove the directories and keep
the existing EditUtils.RemoveCompileDefine calls and
EditorApplication.OpenProject(Directory.GetCurrentDirectory()) so the project is
reopened with a cleaned manifest.
- Around line 45-49: The busy-wait loop that polls RemoveRequest.IsCompleted
after calling Client.Remove("com.sidequest.banter") blocks the editor update
pump; instead register a callback on EditorApplication.update to poll the
RemoveRequest until IsCompleted, then unregister the callback and continue
processing. Locate the call site using Client.Remove and the RemoveRequest
variable name, replace the while(!request.IsCompleted) { } spin with an
EditorApplication.update delegate that checks request.IsCompleted each tick,
calls whatever follow-up logic is needed when completed, and removes itself from
EditorApplication.update to avoid leaking the callback.

In `@Runtime/Scripts/BanterLink/BanterLink.cs`:
- Around line 147-153: Remove the hard-coded await new WaitForSeconds(2) and
instead rely on the concrete readiness check (do not introduce arbitrary
sleeps); use the existing WaitUntil that checks scene.loaded (and call
scene.SetLoaded() at the appropriate time before/inside that check) so emission
of UNITY_LOADED happens immediately when scene.loaded becomes true—modify the
block around WaitForSeconds/WaitUntil (references: WaitForSeconds, WaitUntil,
scene.SetLoaded(), scene.loaded, LogLine.Do) to await the readiness condition
only and remove the two-second delay.
- Around line 136-141: Restore handling for APICommands.DOM_READY by re-enabling
the branch that sets scene.state = SceneState.DOM_READY, invokes
scene.events.OnDomReady, and calls scene.SetLoaded() so DOM readiness
contributes to scene.isReady; locate the commented block around
APICommands.DOM_READY in BanterLink.cs and uncomment or reimplement that exact
behavior. If instead DOM_READY is intended to be retired, update the readiness
contract in the same change by modifying BanterScene.isReady and any listeners
that depend on OnDomReady to use the new readiness signal (ensure all references
to SceneState.DOM_READY, scene.events.OnDomReady.Invoke(), and scene.SetLoaded()
are removed or replaced consistently).

---

Nitpick comments:
In `@Editor/Resources/Builder/BuilderWindow.cs`:
- Around line 1189-1201: The HttpResponseMessage returned in Json<T>(string url,
Action<T> callback) is never disposed; wrap the code that inspects response and
reads its content so the response is always disposed (e.g., assign response =
task.Result, start the readTask, await its completion, and then in a try/finally
or using-equivalent ensure response.Dispose() in the finally block). Make sure
you read response.Content.ReadAsStringAsync() and obtain readTask.Result before
disposing the response, and preserve existing error handling (throw on
non-success status or task faults).
- Around line 1203-1231: The Json<T> coroutine creates an HttpRequestMessage
(and a StringContent) and uses HttpResponseMessage from _httpClient.SendAsync
without disposing them; wrap the HttpRequestMessage (and its Content) in a using
(or try/finally Dispose) and also ensure the HttpResponseMessage is disposed
after you finish reading its content (read response.Content.ReadAsStringAsync()
and await/complete that before disposing); update the code around
HttpRequestMessage creation, _httpClient.SendAsync call, and handling of
response/response.Content (symbols: Json<T>, HttpRequestMessage, StringContent,
_httpClient.SendAsync, HttpResponseMessage, response.Content.ReadAsStringAsync)
so all IDisposable objects are properly disposed while preserving the existing
error/exception handling and callback behavior.
- Around line 53-54: The static HttpClient _httpClient in BuilderWindow.cs has
no timeout set; update it to use the same 10-minute timeout as SqEditorAppApi.cs
(or refactor to reuse the shared HttpClient used by SqEditorAppApi) by
configuring the Timeout property on the shared/static HttpClient instance (refer
to the _httpClient field in BuilderWindow and the HttpClient usage in
SqEditorAppApi.cs) so both image downloads and API calls have consistent timeout
behavior.

In `@Editor/Resources/Builder/SideQuest/SqEditorAppApi.cs`:
- Around line 719-759: JsonGet currently creates an HttpRequestMessage (request)
and obtains an HttpResponseMessage (response) without disposing them; wrap
request creation and the code path that uses response in proper disposal (e.g.,
using blocks or try/finally Dispose calls) so request is disposed after
SendAsync and response (and its Content if needed) is disposed after reading the
content or on all error/early-return paths; update symbols referenced (JsonGet,
request, _httpClient.SendAsync, response, response.Content, readTask) to ensure
every exit/exception path disposes those objects.
- Around line 687-717: In PostFormEncodedStringNoAuth, both the created
StringContent (content) and the HttpResponseMessage (response from
_httpClient.PostAsync) must be disposed; wrap the StringContent creation in a
using (or try/finally) and similarly dispose the response after you finish using
it (i.e. after awaiting/finishing response.Content.ReadAsStringAsync and storing
the result) to avoid leaking resources; ensure you still handle task faults,
non-success status codes, and call OnError/OnCompleted as before while disposing
in all control paths (use using/try-finally around content and a
using/try-finally around response/readTask/result).

In `@Editor/Scripts/VisualScripting/VsStubsAllowed.cs`:
- Around line 12635-12798: The four types Unity.AI.Navigation.NavMeshLink,
NavMeshModifier, NavMeshModifierVolume, and NavMeshSurface repeat the same 29
inherited Object/Component members; extract those shared strings into a single
reusable list (e.g., commonUnityComponentMembers) and reuse/concatenate it when
building the allowlist entries for each of the four types (replace the
duplicated block in the NavMeshLink, NavMeshModifier, NavMeshModifierVolume, and
NavMeshSurface entries with a reference to that shared list or a type-level
pattern), so future changes to inherited Unity members only need to be made in
one place.

In `@Editor/SDKInitialiseOnLoad.cs`:
- Line 39: Remove the commented-out uninstaller scaffold from
Editor/SDKInitialiseOnLoad.cs: delete the commented line "//
CreateUninstaller();" and the larger commented block spanning the existing
uninstaller scaffold (previously lines 73-156) so the initializer no longer
contains the outdated, duplicated uninstall logic; rely on the real uninstall
flow in Editor/Resources/Builder/MenuUtils.cs and ensure no other commented
duplicate functions (e.g., CreateUninstaller) remain in this file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 884f27af-e9b8-4a0d-8331-e091c6515ac4

📥 Commits

Reviewing files that changed from the base of the PR and between 8cff56e and d415c90.

⛔ Files ignored due to path filters (1)
  • Runtime/Objects/Loading/LoadingCage.mat is excluded by !**/*.mat
📒 Files selected for processing (18)
  • Editor/Resources/Builder/BuilderWindow.cs
  • Editor/Resources/Builder/BuilderWindow.uxml
  • Editor/Resources/Builder/MenuUtils.cs
  • Editor/Resources/Builder/SideQuest/SqEditorAppApi.cs
  • Editor/SDKInitialiseOnLoad.cs
  • Editor/Scripts/VisualScripting/VsStubsAllowed.cs
  • Runtime/Scripts/BanterLink/BanterLink.cs
  • Runtime/Scripts/BanterLink/BanterPipe.cs
  • Runtime/Scripts/BanterStarterUpper.cs
  • Runtime/Scripts/Models/YtInfo/YtResponseContext.cs
  • Runtime/Scripts/Models/YtInfo/YtThumbnail.cs
  • Runtime/Scripts/Models/YtInfo/YtThumbnail.cs.meta
  • Runtime/Scripts/Models/YtInfo/YtVideoDetails.cs
  • Runtime/Scripts/Models/YtInfo/YtVideoDetails.cs.meta
  • Runtime/Scripts/Scene/BanterScene.cs
  • Runtime/Scripts/Scene/Components/BanterBrowser.cs
  • Runtime/Scripts/Utils/AddPanelStuff.cs
  • Runtime/Scripts/Utils/DontDestroyOnLoad.cs
💤 Files with no reviewable changes (3)
  • Runtime/Scripts/Utils/AddPanelStuff.cs
  • Runtime/Scripts/BanterStarterUpper.cs
  • Runtime/Scripts/BanterLink/BanterPipe.cs

Comment on lines 376 to 384
public IEnumerator Texture(string url, Action<Texture2D> callback)
{
using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url))
{
yield return uwr.SendWebRequest();
if (uwr.result != UnityWebRequest.Result.Success)
{
throw new System.Exception(uwr.error);
}
else
{
callback(DownloadHandlerTexture.GetContent(uwr));
}
}
var task = _httpClient.GetByteArrayAsync(url);
while (!task.IsCompleted) yield return null;
if (task.IsFaulted) throw task.Exception.InnerException ?? task.Exception;
var tex = new Texture2D(1, 1);
tex.LoadImage(task.Result);
callback(tex);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unchecked LoadImage return value.

Texture2D.LoadImage() returns a boolean indicating success. If the downloaded bytes are not a valid image format, this will fail silently and the callback will receive an invalid texture.

Suggested fix
     var tex = new Texture2D(1, 1);
-    tex.LoadImage(task.Result);
-    callback(tex);
+    if (!tex.LoadImage(task.Result))
+    {
+        UnityEngine.Object.DestroyImmediate(tex);
+        throw new Exception($"Failed to load image from URL: {url}");
+    }
+    callback(tex);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public IEnumerator Texture(string url, Action<Texture2D> callback)
{
using (UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(url))
{
yield return uwr.SendWebRequest();
if (uwr.result != UnityWebRequest.Result.Success)
{
throw new System.Exception(uwr.error);
}
else
{
callback(DownloadHandlerTexture.GetContent(uwr));
}
}
var task = _httpClient.GetByteArrayAsync(url);
while (!task.IsCompleted) yield return null;
if (task.IsFaulted) throw task.Exception.InnerException ?? task.Exception;
var tex = new Texture2D(1, 1);
tex.LoadImage(task.Result);
callback(tex);
}
public IEnumerator Texture(string url, Action<Texture2D> callback)
{
var task = _httpClient.GetByteArrayAsync(url);
while (!task.IsCompleted) yield return null;
if (task.IsFaulted) throw task.Exception.InnerException ?? task.Exception;
var tex = new Texture2D(1, 1);
if (!tex.LoadImage(task.Result))
{
UnityEngine.Object.DestroyImmediate(tex);
throw new Exception($"Failed to load image from URL: {url}");
}
callback(tex);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/Resources/Builder/BuilderWindow.cs` around lines 376 - 384, In the
Texture coroutine, check the boolean return value of Texture2D.LoadImage after
creating tex and loading task.Result; if LoadImage returns false,
dispose/destroy the invalid Texture2D, log or surface the error, and invoke the
provided callback with null (or throw a descriptive exception) instead of
passing an invalid texture; update the Texture method (the coroutine named
Texture and its callback usage) to handle the failed load path cleanly so
callers can detect and handle image decoding failures.

Comment on lines +45 to +49
RemoveRequest request = Client.Remove("com.sidequest.banter");
while(!request.IsCompleted)
{

}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n Editor/Resources/Builder/MenuUtils.cs | sed -n '40,60p'

Repository: SideQuestVR/BanterSDK

Length of output: 1033


🏁 Script executed:

# Search for RemoveRequest usage in the codebase to understand the pattern
rg "RemoveRequest" -A 5 -B 2

Repository: SideQuestVR/BanterSDK

Length of output: 1260


🌐 Web query:

UnityEditor.PackageManager.Client.Remove RemoveRequest async completion IsCompleted editor update

💡 Result:

Client.Remove(...) returns a RemoveRequest and runs asynchronously—there’s no callback/event on the request itself, so you detect completion by polling request.IsCompleted and then checking request.Status / request.Error. IsCompleted becomes true only when the UPM operation finishes. [1][2]

Typical Editor pattern (poll each editor tick): [3]

using UnityEditor;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;

static RemoveRequest _req;

static void RemovePkg(string name)
{
    _req = Client.Remove(name);
    EditorApplication.update += Tick;
}

static void Tick()
{
    if (!_req.IsCompleted) return;

    EditorApplication.update -= Tick;

    if (_req.Status == StatusCode.Success) UnityEngine.Debug.Log("Removed");
    else UnityEngine.Debug.LogError(_req.Error.message);
}

If you also need to know when the Editor has applied/compiled/imported the resulting package set (not just when the request completed), subscribe to UnityEditor.PackageManager.Events.registeredPackages (raised after the Editor applies the changed list). [4] Unity notes cases where the change is only effective after a “full editor tick”, so chaining package operations is often done sequentially via EditorApplication.update (or by waiting for registeredPackages). [5]

Sources: [1] Client.Remove doc, [2] Request.IsCompleted/Status/Error, [3] Unity manual example showing the EditorApplication.update + IsCompleted polling pattern, [4] Package Manager Events, [5] Unity Issue Tracker note about changes becoming effective after a full editor tick.


Replace the busy-wait loop with EditorApplication.update callback.

Client.Remove() completes asynchronously during editor updates. The tight loop on lines 46–49 blocks that update pump, freezing the editor and preventing the uninstall from ever completing. Use EditorApplication.update to poll IsCompleted instead.

Safer pattern
             RemoveRequest request = Client.Remove("com.sidequest.banter");
-            while(!request.IsCompleted)
-            {
-                
-            }
+            EditorApplication.update += WaitForRemove;
+            void WaitForRemove()
+            {
+                if (!request.IsCompleted) return;
+                EditorApplication.update -= WaitForRemove;
+                if (request.Status == StatusCode.Success)
+                {
+                    // continue cleanup here
+                }
+                else
+                {
+                    Debug.LogError($"Remove failed: {request.Error.message}");
+                }
+            }
+            return;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
RemoveRequest request = Client.Remove("com.sidequest.banter");
while(!request.IsCompleted)
{
}
RemoveRequest request = Client.Remove("com.sidequest.banter");
EditorApplication.update += WaitForRemove;
void WaitForRemove()
{
if (!request.IsCompleted) return;
EditorApplication.update -= WaitForRemove;
if (request.Status == StatusCode.Success)
{
// continue cleanup here
}
else
{
Debug.LogError($"Remove failed: {request.Error.message}");
}
}
return;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/Resources/Builder/MenuUtils.cs` around lines 45 - 49, The busy-wait
loop that polls RemoveRequest.IsCompleted after calling
Client.Remove("com.sidequest.banter") blocks the editor update pump; instead
register a callback on EditorApplication.update to poll the RemoveRequest until
IsCompleted, then unregister the callback and continue processing. Locate the
call site using Client.Remove and the RemoveRequest variable name, replace the
while(!request.IsCompleted) { } spin with an EditorApplication.update delegate
that checks request.IsCompleted each tick, calls whatever follow-up logic is
needed when completed, and removes itself from EditorApplication.update to avoid
leaking the callback.

Comment on lines +50 to +68
if(Directory.Exists("Packages/com.basis.bundlemanagement"))
{
Directory.Delete("Packages/com.basis.bundlemanagement", true);
}
if(Directory.Exists("Packages/com.basis.sdk"))
{
Directory.Delete("Packages/com.basis.sdk", true);
}
if(Directory.Exists("Packages/com.basis.odinserializer"))
{
Directory.Delete("Packages/com.basis.odinserializer", true);
}
if (Directory.Exists("Packages/com.sidequest.ora"))
{
Directory.Delete("Packages/com.sidequest.ora", true);
}
EditUtils.RemoveCompileDefine("BANTER_ORA", new BuildTargetGroup[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone });
EditUtils.RemoveCompileDefine("BASIS_BUNDLE_MANAGEMENT", new BuildTargetGroup[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone });
EditorApplication.OpenProject(Directory.GetCurrentDirectory());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "MenuUtils.cs" | head -20

Repository: SideQuestVR/BanterSDK

Length of output: 104


🏁 Script executed:

find . -type f -name "manifest.json" | head -10

Repository: SideQuestVR/BanterSDK

Length of output: 47


🏁 Script executed:

rg -l "ImportOraPackage\|ImportBasisPackages" --type cs

Repository: SideQuestVR/BanterSDK

Length of output: 47


🏁 Script executed:

cat -n "./Editor/Resources/Builder/MenuUtils.cs" | head -100

Repository: SideQuestVR/BanterSDK

Length of output: 3531


🏁 Script executed:

rg "manifest\.json|ImportOraPackage|ImportBasisPackages|InitialiseOnLoad" --type cs -i

Repository: SideQuestVR/BanterSDK

Length of output: 928


🏁 Script executed:

git ls-files | grep -i manifest

Repository: SideQuestVR/BanterSDK

Length of output: 47


🏁 Script executed:

cat -n "./Editor/SDKInitialiseOnLoad.cs"

Repository: SideQuestVR/BanterSDK

Length of output: 19671


Remove the manifest.json entries for the deleted packages.

InitialiseOnLoad.ImportOraPackage() and ImportBasisPackages() add com.sidequest.ora and the com.basis.* packages (com.basis.bundlemanagement, com.basis.sdk, com.basis.odinserializer) as file: dependencies to Packages/manifest.json. Deleting these directories in UninstallBanter() without removing those manifest entries leaves the reopened project with broken package references pointing to missing local packages.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Editor/Resources/Builder/MenuUtils.cs` around lines 50 - 68,
UninstallBanter() currently deletes the local package folders but leaves file:
entries in Packages/manifest.json, causing broken references; update
UninstallBanter() to load Packages/manifest.json, remove the dependencies for
"com.sidequest.ora" and the "com.basis.bundlemanagement", "com.basis.sdk",
"com.basis.odinserializer" keys (the same packages added by
InitialiseOnLoad.ImportOraPackage() and ImportBasisPackages()), write the
manifest back (preserving JSON formatting), then remove the directories and keep
the existing EditUtils.RemoveCompileDefine calls and
EditorApplication.OpenProject(Directory.GetCurrentDirectory()) so the project is
reopened with a cleaned manifest.

Comment on lines +136 to +141
// else if (msg.StartsWith(APICommands.DOM_READY))
// {
// scene.state = SceneState.DOM_READY;
// scene.events.OnDomReady.Invoke();
// scene.SetLoaded();
// }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Restore DOM_READY or update the readiness contract in the same change.

This was the only branch that raised scene.events.OnDomReady and transitioned the scene into SceneState.DOM_READY. With it commented out, that callback never fires, and DOM readiness no longer participates in the scene.isReady progression. If DOM_READY is being retired, BanterScene.isReady and any listeners should be updated in the same PR.

💡 Minimal fix
-            // else if (msg.StartsWith(APICommands.DOM_READY))
-            // {
-            //     scene.state = SceneState.DOM_READY;
-            //     scene.events.OnDomReady.Invoke();
-            //     scene.SetLoaded();
-            // }
+            else if (msg.StartsWith(APICommands.DOM_READY))
+            {
+                scene.state = SceneState.DOM_READY;
+                scene.events.OnDomReady.Invoke();
+                scene.SetLoaded();
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// else if (msg.StartsWith(APICommands.DOM_READY))
// {
// scene.state = SceneState.DOM_READY;
// scene.events.OnDomReady.Invoke();
// scene.SetLoaded();
// }
else if (msg.StartsWith(APICommands.DOM_READY))
{
scene.state = SceneState.DOM_READY;
scene.events.OnDomReady.Invoke();
scene.SetLoaded();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Runtime/Scripts/BanterLink/BanterLink.cs` around lines 136 - 141, Restore
handling for APICommands.DOM_READY by re-enabling the branch that sets
scene.state = SceneState.DOM_READY, invokes scene.events.OnDomReady, and calls
scene.SetLoaded() so DOM readiness contributes to scene.isReady; locate the
commented block around APICommands.DOM_READY in BanterLink.cs and uncomment or
reimplement that exact behavior. If instead DOM_READY is intended to be retired,
update the readiness contract in the same change by modifying
BanterScene.isReady and any listeners that depend on OnDomReady to use the new
readiness signal (ensure all references to SceneState.DOM_READY,
scene.events.OnDomReady.Invoke(), and scene.SetLoaded() are removed or replaced
consistently).

Comment on lines +147 to +153
await new WaitForSeconds(2);
await new WaitUntil(() =>
{
scene.SetLoaded();
return scene.loaded;
});
});
LogLine.Do(LogLine.banterColor, LogTag.Banter, "After Banter Scene Loaded.");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid a hard-coded 2s pause in the load handshake.

Line 147 adds a guaranteed two-second delay before UNITY_LOADED can be emitted, even when the scene is already ready. If this is masking another race, please wait on that concrete condition instead of slowing every load.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Runtime/Scripts/BanterLink/BanterLink.cs` around lines 147 - 153, Remove the
hard-coded await new WaitForSeconds(2) and instead rely on the concrete
readiness check (do not introduce arbitrary sleeps); use the existing WaitUntil
that checks scene.loaded (and call scene.SetLoaded() at the appropriate time
before/inside that check) so emission of UNITY_LOADED happens immediately when
scene.loaded becomes true—modify the block around WaitForSeconds/WaitUntil
(references: WaitForSeconds, WaitUntil, scene.SetLoaded(), scene.loaded,
LogLine.Do) to await the readiness condition only and remove the two-second
delay.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant