Cleaned up and commented version.
This commit is contained in:
@@ -4,6 +4,8 @@ using UnityEngine;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
|
||||
// Die Klasse FrameModel stellt das Gerüst für die Daten dar, die über den SupabaseManager an die Datenbank gesenden und von dort empfangen werden.
|
||||
|
||||
namespace GhostSystem
|
||||
{
|
||||
[Table("object_positions")]
|
||||
|
||||
@@ -7,14 +7,16 @@ using System;
|
||||
using Oculus.Interaction;
|
||||
using System.IO;
|
||||
|
||||
// Dieses Script ist für die Aufnahme des Replays während des Trainingsdurchlaufs zuständig.
|
||||
|
||||
namespace GhostSystem
|
||||
{
|
||||
public class GhostRecorderBatch : MonoBehaviour
|
||||
{
|
||||
public string SceneId;
|
||||
public float SampleRate = 0.05f;
|
||||
public int BatchSize = 10;
|
||||
public float FlushInterval = 2f;
|
||||
public string SceneId; // SceneID zum Übergeben des Raumnamens an die Datenbank.
|
||||
public float SampleRate = 0.05f; // Entspricht einer Framerate von 20 FPS.
|
||||
public int BatchSize = 10; // Anzahl der Frames nach denen ein neuer Batch beginnt. Wird nicht mehr genutzt, da manueller Flush.
|
||||
public float FlushInterval = 2f; // Länge des Intervalls bis zum nächsten Flush. Wird nicht mehr genutzt, da manueller Flush.
|
||||
|
||||
public List<Transform> TrackedObjects;
|
||||
private int doorCount;
|
||||
@@ -41,8 +43,8 @@ namespace GhostSystem
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
SupabaseManager.OnSuperbaseReady += HandleSupabaseReady;
|
||||
RoomManager.OnSceneIdReady += HandleSceneIdReady;
|
||||
SupabaseManager.OnSuperbaseReady += HandleSupabaseReady; // Das Skript horcht nach den beiden Events, da eine Aufnahme in die Datenbank nur möglich sien soll,
|
||||
RoomManager.OnSceneIdReady += HandleSceneIdReady; // wenn die Szene komplett geladen und die Verbindung zu Supabase aufgebaut ist.
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
@@ -61,8 +63,8 @@ namespace GhostSystem
|
||||
supabaseReady = true;
|
||||
}
|
||||
|
||||
private void HandleSceneIdReady(string id, List<GameObject> doors, List<RecieveHeat> targets)
|
||||
{
|
||||
private void HandleSceneIdReady(string id, List<GameObject> doors, List<RecieveHeat> targets) // Die Szene muss komplett geladen sein, da mit dem Event die SceneID
|
||||
{ // übergeben wird.
|
||||
SceneId = id;
|
||||
sceneIdReady = true;
|
||||
if (SceneId == "Base")
|
||||
@@ -72,8 +74,8 @@ namespace GhostSystem
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (GameObject door in doors)
|
||||
{
|
||||
foreach (GameObject door in doors) // neben den Tracked Objects, die in jeder Szene gleich sind (Player-Root, Kopf, Hände, Augen), müssen die Türen jedes Raums
|
||||
{ // zu den Tracked Objects hinzugefügt werden. Nur so wird die Bewegung der Tür auch im Replay wiedergegeben.
|
||||
var rotator = door.GetComponentInChildren<OneGrabRotateTransformer>();
|
||||
if (rotator != null)
|
||||
{
|
||||
@@ -86,12 +88,12 @@ namespace GhostSystem
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_isRecording) return;
|
||||
if (!_isRecording) return; // Code im Update wird nur ausgeführt, während die Aufnahme läuft.
|
||||
|
||||
_sampleTimer += Time.deltaTime;
|
||||
_flushTimer += Time.deltaTime;
|
||||
|
||||
if (_sampleTimer >= SampleRate)
|
||||
if (_sampleTimer >= SampleRate) // Es wird immer die Dauer bis zum nächsten Record-Frame runter gezählt und ein Sample aufgenommen.
|
||||
{
|
||||
_sampleTimer = 0f;
|
||||
RecordSample();
|
||||
@@ -107,11 +109,11 @@ namespace GhostSystem
|
||||
|
||||
private void RecordSample()
|
||||
{
|
||||
foreach (var obj in TrackedObjects)
|
||||
{
|
||||
foreach (var obj in TrackedObjects) // Ein Sample besteht aus einer Liste von FrameModels, die für den Zustand jedes getrackten Objekts zum aktuellen Zeitpunkt
|
||||
{ // erstellt werden.
|
||||
var frame = new GhostFrame(SceneId, obj.name, obj.position, obj.rotation);
|
||||
_frameBuffer.Add(FrameModel.FromGhostFrame(frame));
|
||||
}
|
||||
_frameBuffer.Add(FrameModel.FromGhostFrame(frame)); // Die Frames werden an den Buffer angehängt, um noch während der Aufnahme noch nciht so viele System-
|
||||
} // Ressourcen zu verbrauchen.
|
||||
}
|
||||
|
||||
private async UniTaskVoid FlushBatchAsync() // Die eigentliche Methode zum Hochladen eines Batches/leeren des Buffers.
|
||||
@@ -148,7 +150,7 @@ namespace GhostSystem
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask FallbackToOfflineAsync(List<FrameModel> batch) // Schreibt den Buffer in eine Datei, falls Upload nicht möglich
|
||||
private async UniTask FallbackToOfflineAsync(List<FrameModel> batch) // Schreibt den Buffer in eine Datei, falls Upload nicht möglich. Kann zu Rucklern führen.
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -165,7 +167,7 @@ namespace GhostSystem
|
||||
}
|
||||
}
|
||||
|
||||
public void StartRecording()
|
||||
public void StartRecording() // Wenn die Bedingungen erfüllt sind, startet diese Methode die Aufnahme. Sie kann von anderen Skripten aufgerufen werden.
|
||||
{
|
||||
if (!supabaseReady || !sceneIdReady) return;
|
||||
_isRecording = true;
|
||||
@@ -173,8 +175,8 @@ namespace GhostSystem
|
||||
Debug.Log($"Recording {SceneId}");
|
||||
}
|
||||
|
||||
public void StopRecording()
|
||||
{
|
||||
public void StopRecording() // Diese Methode stoppt die Aufnahme, löst ein entsprechendes Event aus, das abonniert werden kann und entfernt die Türen des aktuellen
|
||||
{ // Raums wieder aus der Liste der Tracked Objects.
|
||||
_isRecording = false;
|
||||
sceneIdReady = false;
|
||||
OnStopRecord?.Invoke();
|
||||
|
||||
@@ -11,6 +11,9 @@ using System.Globalization;
|
||||
using Oculus.Interaction;
|
||||
using System.Reactive;
|
||||
using Postgrest.Interfaces;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
// Dieses Script ist für die Wiedergabe des Replays mit den Daten aus der Datenbank zuständig.
|
||||
|
||||
namespace GhostSystem
|
||||
{
|
||||
@@ -81,18 +84,18 @@ namespace GhostSystem
|
||||
RoomManager.OnSceneIdReady -= HandleSceneIdReady;
|
||||
}
|
||||
|
||||
private void HandleSupabaseReady()
|
||||
private void HandleSupabaseReady() // Wen die Verbindung zu Supabase aufgebaut ist, wird versucht, das Replay der Szene zu initialisieren.
|
||||
{
|
||||
supabaseReady = true;
|
||||
TryInitializeReplay();
|
||||
}
|
||||
|
||||
private void HandleSceneIdReady(string id, List<GameObject> doors, List<RecieveHeat> targets)
|
||||
{
|
||||
loadingScreen.SetActive(true);
|
||||
private void HandleSceneIdReady(string id, List<GameObject> doors, List<RecieveHeat> targets) // Sobald die Szene geladen ist, wird ebenfalls versucht, das Replay zu
|
||||
{ // initialisieren.
|
||||
loadingScreen.SetActive(true); // Während der erste Batch an Frames aus der Datenbank geladen wird, wird ein Ladekringel angezeigt.
|
||||
sceneIdReady = true;
|
||||
loadedSceneId = id;
|
||||
foreach (GameObject door in doors)
|
||||
loadedSceneId = id; // die aktuelle SceneID wird gesetzt.
|
||||
foreach (GameObject door in doors) // Auch hier müssen die Türen zugeordnet werden.
|
||||
{
|
||||
TrackedObjectBinding trackedObjectBinding = new TrackedObjectBinding();
|
||||
trackedObjectBinding.objectId = "Door";
|
||||
@@ -104,29 +107,29 @@ namespace GhostSystem
|
||||
TryInitializeReplay();
|
||||
}
|
||||
|
||||
private async UniTaskVoid TryInitializeReplay()
|
||||
private async UniTaskVoid TryInitializeReplay() // Das Replay wird aber nur initialisiert, wenn Supabase und die Szene bereit sind.
|
||||
{
|
||||
if (!supabaseReady || !sceneIdReady) return;
|
||||
|
||||
sceneId = loadedSceneId;
|
||||
awaitingFrames = true;
|
||||
|
||||
await LoadAllFrames();
|
||||
loadingScreen.SetActive(false);
|
||||
StartReplay();
|
||||
_ = PeriodicUpdateFrames();
|
||||
}
|
||||
await LoadAllFrames(); // Es wird auf die ersten 1000 Frames (siehe Batchsize) bzw. alle verfügbaren Frames (falls unter 1000) gewartet.
|
||||
loadingScreen.SetActive(false); // Sind die Frames geladen, wird der Ladescreen wieder deaktiviert
|
||||
StartReplay(); // und das Replay gestartet.
|
||||
_ = PeriodicUpdateFrames(); // Zusätzlich wird ein Unitask ausgelöst, der immer wieder Frames nachlädt, solange noch Frames in der Datenbank vorhanden sind, die
|
||||
} // den Filtern entsprechen und noch nicht geladen wurden.
|
||||
|
||||
public async UniTask LoadAllFrames()
|
||||
{
|
||||
Debug.Log("SceneId used in query: " + sceneId);
|
||||
|
||||
|
||||
var startTime = dateSelector.selectedDateTime();
|
||||
var endTime = dateSelector.EndTime();
|
||||
var startTime = dateSelector.selectedDateTime(); // Erstellt einen Zeitstring aus den im UI eingegebenen Session-Daten
|
||||
var endTime = dateSelector.EndTime(); // und einen zweiten für den Endpunkt der Trainingssession.
|
||||
|
||||
if (dateSelector.startTimeInput.text != "")
|
||||
{
|
||||
if (dateSelector.startTimeInput.text != "") // Es wird dabei geprüft, ob ein fertiger Timestring in das vorgesehene Feld kopiert wurde. Dann werden die
|
||||
{ // Dropdowns ignoriert.
|
||||
startTimeString = dateSelector.startTimeInput.text;
|
||||
}
|
||||
else startTimeString = startTime.ToString("o");
|
||||
@@ -146,36 +149,35 @@ namespace GhostSystem
|
||||
bool more = true;
|
||||
List<FrameModel> allFrames = new();
|
||||
|
||||
while (more)
|
||||
{
|
||||
List<IPostgrestQueryFilter> timestampFilter = new List<IPostgrestQueryFilter>();
|
||||
while (more) // Solange Frames mit dem Filter (in chronologischer Reihenfolge) in der Datenbank vorhanden sind, wird immer ein neuer Batch von maximal 1000 Frames
|
||||
{ // heruntergeladen.
|
||||
List<IPostgrestQueryFilter> timestampFilter = new List<IPostgrestQueryFilter>(); // Es wird eine Filterverkettung für Startzeit und Endzeit erstellt.
|
||||
timestampFilter.Add(new QueryFilter("timestamp", Postgrest.Constants.Operator.GreaterThanOrEqual, startTimeString));
|
||||
timestampFilter.Add(new QueryFilter("timestamp", Postgrest.Constants.Operator.LessThan, endTimeString));
|
||||
|
||||
var query = SupabaseManager.instance.supabase
|
||||
.From<FrameModel>()
|
||||
.Filter("scene_id", Postgrest.Constants.Operator.Equals, sceneId)
|
||||
//.Where(x => x.Timestamp >= "timestamp '2025-06-14 10:27:00' and" x => x.Timestamp.timestamp <= "timestamp '2025-06-14 10:29:00'")
|
||||
//.Filter("timestamp", Postgrest.Constants.Operator.GreaterThanOrEqual, startTime.ToString("o"))
|
||||
.And(timestampFilter)
|
||||
//.Filter("timestamp", Postgrest.Constants.Operator.LessThan, endTime.ToString("o"))
|
||||
.Filter("scene_id", Postgrest.Constants.Operator.Equals, sceneId) // und neben der SceneID...
|
||||
|
||||
.And(timestampFilter) // ...dem SQL-Command hinzugefügt.
|
||||
|
||||
.Order("timestamp", Postgrest.Constants.Ordering.Ascending)
|
||||
.Limit(batchSize) // Limit, wie viele Frames in einem Batch geladen werden. (Standardmäßig 1000)
|
||||
.Offset(offset); // Um zu wissen, wo der nächste Batch anfängt
|
||||
|
||||
if (!string.IsNullOrEmpty(UserFilter))
|
||||
query = query.Filter("user_id", Postgrest.Constants.Operator.Equals, UserFilter);
|
||||
query = query.Filter("user_id", Postgrest.Constants.Operator.Equals, UserFilter); // Wenn die User ID angegebeen wurde, wird auch danach gefiltert.
|
||||
|
||||
var result = await query.Get();
|
||||
var result = await query.Get(); // Es werden die Frames aus der Datenbank geladen
|
||||
if (result.Models.Count == 0) break;
|
||||
if (result.Models[0].SceneId != sceneId) break;
|
||||
Debug.LogError("Model Count: " + result.Models.Count);
|
||||
allFrames.AddRange(result.Models);
|
||||
offset += batchSize;
|
||||
more = result.Models.Count == batchSize;
|
||||
more = result.Models.Count == batchSize; // Und in einer lokalen Liste gespeichert. Ähnlich dem Buffer beim Ghost Recorder.
|
||||
}
|
||||
|
||||
foreach (var frame in allFrames)
|
||||
foreach (var frame in allFrames) // Die Frames werden den Tracked Objects zugeordnet.
|
||||
{
|
||||
if (!objectTracks.ContainsKey(frame.ObjectId))
|
||||
objectTracks[frame.ObjectId] = new List<FrameModel>();
|
||||
@@ -262,18 +264,18 @@ namespace GhostSystem
|
||||
playbackTime = Mathf.Clamp(time, 0f, totalDuration);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
private void Update() // Solange das Replay auf "Play" steht, wird durch die Liste der Frames gegangen und die Positions- und Rotationsdaten aus selbigen angewendet,
|
||||
{ // wenn die Differenz zwischen Startzeit und Timestamp des Frames der bisherigen Playback-Time entspricht.
|
||||
if (!isPlaying || objectTracks.Count == 0) return;
|
||||
|
||||
playbackTime += Time.deltaTime * PlaybackSpeed;
|
||||
|
||||
if (playbackTime > totalDuration)
|
||||
if (playbackTime > totalDuration) // Erreicht das Replay das Ende der Frame-Liste, wird es entwerder gestoppt
|
||||
{
|
||||
if (Loop)
|
||||
{
|
||||
playbackTime = 0f;
|
||||
OnRestartLoop?.Invoke();
|
||||
OnRestartLoop?.Invoke(); // oder neu gestartet, wenn die Loop-Funktion aktiviert ist.
|
||||
}
|
||||
else
|
||||
isPlaying = false;
|
||||
@@ -326,31 +328,31 @@ namespace GhostSystem
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSliderScrubValue(float sliderValue)
|
||||
public void SetSliderScrubValue(float sliderValue) // der aktuelle Playback-Zeitpunkt wird aktualisiert solange gescrubbt wird.
|
||||
{
|
||||
if (isScrubbing)
|
||||
SetPlaybackTime(sliderValue * totalDuration);
|
||||
}
|
||||
|
||||
|
||||
public void OnSliderStartDrag()
|
||||
public void OnSliderStartDrag() // Das Scrubbing wird gestartet, sobald der Slider angefasst wird.
|
||||
{
|
||||
isScrubbing = true;
|
||||
}
|
||||
|
||||
public void OnSliderEndDrag()
|
||||
public void OnSliderEndDrag() // Setzt den aktuellen Playback-Zeitpunkt auf den Timestamp, der dem übergebenen Slider-Wert entspricht.
|
||||
{
|
||||
isScrubbing = false;
|
||||
SetPlaybackTime(playbackSlider.value * totalDuration);
|
||||
}
|
||||
|
||||
public void RemoveDoors()
|
||||
public void RemoveDoors() // Auch hier gibt es wieder eine Funktion, um vor dem Raumwechsel die Türen aus den Tracked Objects zu entfernen.
|
||||
{
|
||||
TrackedObjects.RemoveRange(TrackedObjects.Count - doorCount, doorCount);
|
||||
doorCount = 0;
|
||||
}
|
||||
|
||||
public void StopAwaitingFrames()
|
||||
public void StopAwaitingFrames() // Kann zum Szenenwechsel aufgerufen werden, um das aktuelle Replay und das Nachladen von Frames abzubrechen.
|
||||
{
|
||||
awaitingFrames = false;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ using Client = Supabase.Client;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using TMPro;
|
||||
|
||||
// Dieses Script wickelt die gesamte Kommunikation mit der Datenbank ab. Das Skript enthält noch einige Verbindungstests (z.B. zu Google), die bei Bedarf zum Debuggen verwendet
|
||||
// werden können.
|
||||
|
||||
namespace GhostSystem
|
||||
{
|
||||
public class SupabaseManager : MonoBehaviour
|
||||
@@ -36,7 +39,7 @@ namespace GhostSystem
|
||||
|
||||
private async UniTask Initialize()
|
||||
{
|
||||
if (instance == null)
|
||||
if (instance == null) // Zuerst wird sichergestellt, dass nur eine Instanz des Supabase-Managers existiert und beim Szenenwechsel mitgenommen wird.
|
||||
{
|
||||
instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
@@ -47,29 +50,29 @@ namespace GhostSystem
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log("🌐 Starting Supabase setup...");
|
||||
await RunDiagnostics();
|
||||
Debug.Log("Starting Supabase setup...");
|
||||
await RunDiagnostics(); // Dann wird geprüft, ob die Supabase-Instanz erreichbar ist.
|
||||
|
||||
await InitSupabaseAsync();
|
||||
OnSuperbaseReady?.Invoke();
|
||||
await InitSupabaseAsync(); // Eigentlicher Verbindungsaufbau.
|
||||
OnSuperbaseReady?.Invoke(); // Wenn erfolgreich eine Verbindung zu Supabase aufgebaut wurde, wird das als Event gemeldet.
|
||||
}
|
||||
|
||||
private async UniTask InitSupabaseAsync()
|
||||
private async UniTask InitSupabaseAsync() // Wird aufgerufen, um die Verbindung zu Supabase zu initialisieren.
|
||||
{
|
||||
if (supabase != null)
|
||||
return;
|
||||
|
||||
string path = Path.Combine(Application.persistentDataPath, authFileName);
|
||||
|
||||
if (File.Exists(path))
|
||||
if (File.Exists(path)) // Ist bereits eine Datei mit Email und Passwort auf dem VR-Headset hinterlegt, wird diese ausgelesen.
|
||||
{
|
||||
string[] lines = await UniTask.Run(() => File.ReadAllLines(path));
|
||||
email = lines[0];
|
||||
password = lines[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
email = string.IsNullOrEmpty(SystemInfo.deviceUniqueIdentifier)
|
||||
{ // Falls nicht, werden neue Anmeldedaten erstellt.
|
||||
email = string.IsNullOrEmpty(SystemInfo.deviceUniqueIdentifier) // Fake-Email-Adresse wird aus der GUID des Gerätes generiert.
|
||||
? $"{Guid.NewGuid()}@device.local" // Fallback, generiert theoretisch bei jeder Neuinstallation der App einen anderen User.
|
||||
: $"{SystemInfo.deviceUniqueIdentifier}@device.local";
|
||||
|
||||
@@ -78,7 +81,7 @@ namespace GhostSystem
|
||||
await UniTask.Run(() => File.WriteAllLines(path, new[] { email, password }));
|
||||
}
|
||||
|
||||
Debug.Log($"📧 Using Email: {email}, Password: {password}");
|
||||
Debug.Log($"Using Email: {email}, Password: {password}");
|
||||
|
||||
supabase = new Client(subabaseUrl, supabaseAnonKey);
|
||||
|
||||
@@ -86,39 +89,39 @@ namespace GhostSystem
|
||||
{
|
||||
await supabase.InitializeAsync();
|
||||
textbox.text += "\n\rSupabase initialized.";
|
||||
Debug.Log("✅ Supabase initialized.");
|
||||
Debug.Log("Supabase initialized.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"❌ Initialization failed: {ex}");
|
||||
Debug.LogError($"Initialization failed: {ex}");
|
||||
textbox.text += "\n\rInitialization failed";
|
||||
}
|
||||
|
||||
await AuthenticateAsync(email, password);
|
||||
Debug.Log($"👤 Authenticated user ID: {userId}");
|
||||
Debug.Log($"Authenticated user ID: {userId}");
|
||||
}
|
||||
|
||||
public async UniTask AuthenticateAsync(string email, string password)
|
||||
public async UniTask AuthenticateAsync(string email, string password) // Diese Methode übernimmt die Anmeldung als User bei der Supabase-Instanz.
|
||||
{
|
||||
try
|
||||
{
|
||||
await supabase.Auth.SignIn(email, password);
|
||||
await supabase.Auth.SignIn(email, password); // Nutzt die übergebene Mail-Adresse und das Passwort, um den Unser anzumelden.
|
||||
textbox.text += "\n\rSign-in successful";
|
||||
Debug.Log("🔓 Sign-in successful");
|
||||
Debug.Log("Sign-in successful");
|
||||
}
|
||||
catch (Exception authEx)
|
||||
{
|
||||
catch (Exception authEx) // Funktioniert die Anmeldung nicht, wird versucht, einen neuen User mit der übergebenen Mail-Adresse und dem
|
||||
{ // Passwort zu registrieren.
|
||||
Debug.LogWarning($"Sign-in failed, trying sign-up: {authEx.Message}");
|
||||
try
|
||||
{
|
||||
await supabase.Auth.SignUp(email, password);
|
||||
textbox.text += "\n\rSign-up successful";
|
||||
Debug.Log("🆕 Sign-up successful");
|
||||
Debug.Log("Sign-up successful");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
textbox.text += "\n\rAuthentication failed";
|
||||
Debug.LogError($"❌ Authentication failed: {ex}");
|
||||
Debug.LogError($"Authentication failed: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,7 +131,7 @@ namespace GhostSystem
|
||||
await UniTask.WhenAll(CheckSupabaseHealthAsync(), CheckGoogleConnectionAsync());
|
||||
}
|
||||
|
||||
private async UniTask CheckSupabaseHealthAsync()
|
||||
private async UniTask CheckSupabaseHealthAsync() // Prüft, ob Supabase erreichbar ist.
|
||||
{
|
||||
using var www = UnityWebRequest.Get($"{subabaseUrl}/auth/v1/health");
|
||||
www.SetRequestHeader("apikey", supabaseAnonKey);
|
||||
@@ -137,16 +140,16 @@ namespace GhostSystem
|
||||
if (www.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
//textbox.text += $"\n\rSupabase health: {www.downloadHandler.text}";
|
||||
Debug.Log($"✅ Supabase health: {www.downloadHandler.text}");
|
||||
Debug.Log($"Supabase health: {www.downloadHandler.text}");
|
||||
}
|
||||
else
|
||||
{
|
||||
//textbox.text += $"\n\rSupabase health check failed: {www.error}";
|
||||
Debug.LogError($"❌ Supabase health check failed: {www.error}");
|
||||
Debug.LogError($"Supabase health check failed: {www.error}");
|
||||
}
|
||||
}
|
||||
|
||||
private async UniTask CheckGoogleConnectionAsync()
|
||||
private async UniTask CheckGoogleConnectionAsync() // Testet, ob grundsätzlich eine Internetverbindung besteht.
|
||||
{
|
||||
using var www = UnityWebRequest.Get("https://www.google.com");
|
||||
await www.SendWebRequest();
|
||||
@@ -154,12 +157,12 @@ namespace GhostSystem
|
||||
if (www.result == UnityWebRequest.Result.Success)
|
||||
{
|
||||
//textbox.text += "\n\rGoogle request succeeded.";
|
||||
Debug.Log("✅ Google request succeeded.");
|
||||
Debug.Log("Google request succeeded.");
|
||||
}
|
||||
else
|
||||
{
|
||||
textbox.text += $"\n\rGoogle connection failed: {www.error}";
|
||||
Debug.LogError($"❌ Google connection failed: {www.error}");
|
||||
Debug.LogError($"Google connection failed: {www.error}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user