From 4780ff764ff6247cde5dcb134741a6c82e96cf17 Mon Sep 17 00:00:00 2001 From: user Date: Wed, 3 Jun 2026 02:00:33 +0900 Subject: [PATCH] =?UTF-8?q?Fix=20:=20=ED=9B=84=EC=9B=90=20=EC=8B=9C?= =?UTF-8?q?=EC=8A=A4=ED=85=9C=20=EC=B5=9C=EC=A2=85=20=ED=8C=A8=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DonationThrowable/ThrowableObject.cs | 62 ++++++++++++- .../Scripts/WefLab/WefLabWebSocketClient.cs | 89 +++++++++---------- 2 files changed, 102 insertions(+), 49 deletions(-) diff --git a/Assets/Scripts/Streamingle/DonationThrowable/ThrowableObject.cs b/Assets/Scripts/Streamingle/DonationThrowable/ThrowableObject.cs index 82522bd1e..502b16d6b 100644 --- a/Assets/Scripts/Streamingle/DonationThrowable/ThrowableObject.cs +++ b/Assets/Scripts/Streamingle/DonationThrowable/ThrowableObject.cs @@ -42,6 +42,9 @@ namespace Streamingle [Tooltip("Time to stay after reaching target before deactivating")] public float postArrivalLifetime = 3f; + [Tooltip("Time spent shrinking out before disappearing (0 = pop instantly)")] + public float shrinkDuration = 0.4f; + // Trajectory state private Vector3 startPosition; private Vector3 targetPosition; @@ -64,11 +67,16 @@ namespace Streamingle private float lifetime; private float spawnTime; + // Despawn (shrink-out) + private Vector3 originalScale; + private bool isDespawning = false; + void Awake() { rb = GetComponent(); audioSource = GetComponent(); colliders = GetComponents(); + originalScale = transform.localScale; } /// @@ -89,7 +97,11 @@ namespace Streamingle this.hasArrived = false; this.hasCollided = false; this.hasPlayedHitFeedback = false; + this.isDespawning = false; this.isFlying = true; + + // Restore scale in case this pooled object was shrunk out last time + transform.localScale = originalScale; this.currentTime = 0f; this.flightTime = flightDuration; this.startPosition = transform.position; @@ -124,7 +136,7 @@ namespace Streamingle // Check lifetime if (lifetime > 0 && Time.time - spawnTime > lifetime) { - Deactivate(); + BeginDespawn(); return; } @@ -195,8 +207,8 @@ namespace Streamingle rb.angularVelocity = rotationAxis * rotationSpeed * Mathf.Deg2Rad; } - // Schedule deactivation (object falls if no collision) - Invoke(nameof(Deactivate), postArrivalLifetime); + // Schedule despawn (object falls if no collision, then shrinks out) + Invoke(nameof(BeginDespawn), postArrivalLifetime); } void OnCollisionEnter(Collision collision) @@ -286,13 +298,56 @@ namespace Streamingle } } + /// + /// Begin the despawn sequence. Shrinks the object out over shrinkDuration + /// before actually deactivating, so it doesn't pop out abruptly. + /// + private void BeginDespawn() + { + if (isDespawning) return; + isDespawning = true; + isFlying = false; + + // Cancel the scheduled BeginDespawn so it can't fire again mid-shrink. + CancelInvoke(); + + if (shrinkDuration > 0f && gameObject.activeInHierarchy) + { + StartCoroutine(ShrinkAndDeactivate()); + } + else + { + Deactivate(); + } + } + + private System.Collections.IEnumerator ShrinkAndDeactivate() + { + Vector3 from = transform.localScale; + float t = 0f; + while (t < shrinkDuration) + { + t += Time.deltaTime; + float k = Mathf.Clamp01(t / shrinkDuration); + transform.localScale = Vector3.Lerp(from, Vector3.zero, k); + yield return null; + } + transform.localScale = Vector3.zero; + Deactivate(); + } + private void Deactivate() { CancelInvoke(); + StopAllCoroutines(); isFlying = false; hasArrived = false; hasCollided = false; hasPlayedHitFeedback = false; + isDespawning = false; + + // Restore scale so the pooled/recycled object starts at full size next time. + transform.localScale = originalScale; if (launcher != null && launcher.usePooling) { @@ -313,6 +368,7 @@ namespace Streamingle hasArrived = false; hasCollided = false; hasPlayedHitFeedback = false; + isDespawning = false; } } } diff --git a/Assets/Scripts/WefLab/WefLabWebSocketClient.cs b/Assets/Scripts/WefLab/WefLabWebSocketClient.cs index c1f0fd3d4..56755ff7d 100644 --- a/Assets/Scripts/WefLab/WefLabWebSocketClient.cs +++ b/Assets/Scripts/WefLab/WefLabWebSocketClient.cs @@ -234,8 +234,16 @@ namespace WefLab public string ttsLang = "ko"; [Header("Debug")] - [Tooltip("Log the full raw JSON payload of every received socket event. Use this to inspect real platform donation/chat structures.")] - public bool verboseRawLog = true; + [Tooltip("Show detailed WefLab logs in the console (raw payloads, every socket event, ping/pong, handshakes, platform-feed keepalives, chat). " + + "Off keeps the console clean - only donations, connection status and errors are logged.")] + public bool verboseLog = false; + + /// Console log that only fires when verboseLog (detail logging) is enabled. + private void VLog(string message) + { + if (verboseLog) + UnityEngine.Debug.Log(message); + } // Queue state (visible in Inspector for debugging) [Header("Queue Status (Read Only)")] @@ -641,13 +649,13 @@ namespace WefLab return; } - Debug.Log($"[WefLab] ({conn.Label}) Connecting to: {conn.wsUrl}"); + VLog($"[WefLab] ({conn.Label}) Connecting to: {conn.wsUrl}"); conn.ws = new WebSocket(conn.wsUrl); // Capture conn in the handlers so each message knows which socket it came from conn.ws.OnOpen += (sender, e) => - EnqueueMainThreadAction(() => Debug.Log($"[WefLab] ({conn.Label}) WebSocket connection opened")); + EnqueueMainThreadAction(() => VLog($"[WefLab] ({conn.Label}) WebSocket connection opened")); conn.ws.OnMessage += (sender, e) => { @@ -666,7 +674,7 @@ namespace WefLab conn.ws.OnClose += (sender, e) => EnqueueMainThreadAction(() => { - Debug.Log($"[WefLab] ({conn.Label}) WebSocket closed. Code: {e.Code}, Reason: {e.Reason}"); + VLog($"[WefLab] ({conn.Label}) WebSocket closed. Code: {e.Code}, Reason: {e.Reason}"); conn.connected = false; conn.engineSid = ""; conn.socketSid = ""; @@ -752,7 +760,7 @@ namespace WefLab break; case "3": // Engine.IO PONG - Debug.Log($"[WefLab] ({conn.Label}) Received PONG"); + VLog($"[WefLab] ({conn.Label}) Received PONG"); break; case "40": // Socket.IO CONNECT @@ -764,7 +772,7 @@ namespace WefLab break; default: - Debug.Log($"[WefLab] ({conn.Label}) Unknown message type: {messageType} | Data: {data}"); + VLog($"[WefLab] ({conn.Label}) Unknown message type: {messageType} | Data: {data}"); break; } } @@ -786,8 +794,8 @@ namespace WefLab UpdateAggregateState(); - Debug.Log($"[WefLab] ({conn.Label}) Engine.IO OPEN - SID: {conn.engineSid}, PingInterval: {pingInterval}s"); - Debug.Log($"[WefLab] ({conn.Label}) Full handshake data: {payload}"); + VLog($"[WefLab] ({conn.Label}) Engine.IO OPEN - SID: {conn.engineSid}, PingInterval: {pingInterval}s"); + VLog($"[WefLab] ({conn.Label}) Full handshake data: {payload}"); // Send Socket.IO CONNECT SendSocketConnect(conn); @@ -803,7 +811,7 @@ namespace WefLab /// private void HandlePing(PlatformConnection conn) { - Debug.Log($"[WefLab] ({conn.Label}) Received PING, sending PONG"); + VLog($"[WefLab] ({conn.Label}) Received PING, sending PONG"); SendMessage(conn, "3"); // Send PONG } @@ -818,8 +826,8 @@ namespace WefLab { var connectData = JsonConvert.DeserializeObject(payload); conn.socketSid = connectData["sid"]?.ToString() ?? ""; - Debug.Log($"[WefLab] ({conn.Label}) Socket.IO CONNECT - SID: {conn.socketSid}"); - Debug.Log($"[WefLab] ({conn.Label}) Full connect data: {payload}"); + VLog($"[WefLab] ({conn.Label}) Socket.IO CONNECT - SID: {conn.socketSid}"); + VLog($"[WefLab] ({conn.Label}) Full connect data: {payload}"); } conn.connected = true; @@ -868,7 +876,7 @@ namespace WefLab }; string fullMessage = "42" + JsonConvert.SerializeObject(new object[] { "msg", msg }); - Debug.Log($"[WefLab] ({conn.Label}) Sending PLATFORM feed message (start={start})"); + VLog($"[WefLab] ({conn.Label}) Sending PLATFORM feed message (start={start})"); SendMessage(conn, fullMessage); } @@ -914,7 +922,7 @@ namespace WefLab // The server also emits bare events like ["pong"] - ignore anything that isn't a "msg" payload if (eventName != "msg") { - Debug.Log($"[WefLab] ({conn.Label}) Socket event '{eventName}' (ignored)"); + VLog($"[WefLab] ({conn.Label}) Socket event '{eventName}' (ignored)"); return; } @@ -923,10 +931,10 @@ namespace WefLab var envelope = eventArray[1] as JObject; - Debug.Log($"[WefLab] ===== EVENT ({conn.Label}) name={eventName} type={envelope?["type"]} ====="); + VLog($"[WefLab] ===== EVENT ({conn.Label}) name={eventName} type={envelope?["type"]} ====="); // Verbose: dump the full raw payload so real (non-test) platform structures can be verified - if (verboseRawLog && envelope != null) + if (verboseLog && envelope != null) Debug.Log($"[WefLab] RAW ({conn.Label}):\n{envelope.ToString(Formatting.Indented)}"); HandleMessageEnvelope(conn.platform, envelope); @@ -966,15 +974,16 @@ namespace WefLab break; default: - Debug.Log($"[WefLab] ({fallbackPlatform}) Ignored envelope type: '{envType}'"); + VLog($"[WefLab] ({fallbackPlatform}) Ignored envelope type: '{envType}'"); break; } } /// /// Convert a platform's raw donation value to KRW. - /// Prefers the server-provided currency.real (already KRW); otherwise applies the - /// per-platform conversion derived from weflab's client (comm.donation.currency). + /// Unit semantics confirmed by live testing: + /// - afreeca/soop/soopg/twitch: value is an item count (balloon/bit), 1 unit = 100 KRW (x100) + /// - naver(chzzk)/youtube/cime/extdona: value is already the KRW amount (x1, as-is) /// private static int ConvertToKrw(string platform, JObject data, out int rawValue) { @@ -982,32 +991,16 @@ namespace WefLab if (data["value"] != null) int.TryParse(data["value"].ToString(), out rawValue); - // 1) Trust server-provided KRW when present (naver/youtube/twitch send this) - if (data["currency"] is JObject currency && currency["real"] != null - && int.TryParse(currency["real"].ToString(), out int realKrw)) - { - return realKrw; - } - - // 2) Per-platform fallback conversion switch ((platform ?? "").ToLowerInvariant()) { case "afreeca": case "soop": case "soopg": - return rawValue * 100; // 1 balloon / gem = 100 KRW - case "twitch": - return rawValue / 10; // bits -> KRW (approx; currency.real preferred) - - case "naver": // chzzk cheese - case "chzzk": - case "youtube": // superchat - case "cime": - case "extdona": - return rawValue / 100; // raw value is in 1/100 KRW units + return rawValue * 100; // count (balloon / bit) -> KRW default: + // naver(chzzk), youtube, cime, extdona: value is already KRW return rawValue; } } @@ -1021,19 +1014,23 @@ namespace WefLab return; string platform = data["platform"]?.ToString() ?? fallbackPlatform; - string subtype = data["subtype"]?.ToString() ?? ""; + // Real alerts use "subtype"; dashboard test donations use "type" instead. + string subtype = data["subtype"]?.ToString(); + if (string.IsNullOrEmpty(subtype)) + subtype = data["type"]?.ToString() ?? ""; int amount = ConvertToKrw(platform, data, out int rawValue); // Skip non-monetary alerts (subscribe / membership / follow / emoticon, value = 0, ...) if (NonMonetarySubtypes.Contains(subtype) || amount <= 0) { - Debug.Log($"[WefLab] ({platform}) Non-monetary alert ignored (subtype:'{subtype}', value:{rawValue}, krw:{amount})"); + VLog($"[WefLab] ({platform}) Non-monetary alert ignored (subtype:'{subtype}', value:{rawValue}, krw:{amount})"); return; } string message = data["msg"]?.ToString() ?? data["message"]?.ToString() ?? ""; - string donorName = data["name"]?.ToString() ?? data["id"]?.ToString() ?? "Anonymous"; + // Real alerts use "name"; test donations use "uname". + string donorName = data["name"]?.ToString() ?? data["uname"]?.ToString() ?? data["id"]?.ToString() ?? "Anonymous"; string donorId = data["id"]?.ToString() ?? ""; long timestamp = data["time"]?.Value() ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); @@ -1092,9 +1089,9 @@ namespace WefLab }; if (isWarn) - Debug.Log($"[WefLab] CHAT WARN ({platform}): {chat.warnType}"); + VLog($"[WefLab] CHAT WARN ({platform}): {chat.warnType}"); else - Debug.Log($"[WefLab] CHAT ({platform}): {chat.nickname}: {chat.message}" + + VLog($"[WefLab] CHAT ({platform}): {chat.nickname}: {chat.message}" + (chat.emoticons.Count > 0 ? $" [+{chat.emoticons.Count} emoticon]" : "")); onChatReceived?.Invoke(chat); @@ -1189,7 +1186,7 @@ namespace WefLab // Drop duplicates that arrive on more than one socket within the window if (IsDuplicateDonation(donation)) { - Debug.Log($"[WefLab] Duplicate donation ignored: {donation.donorName} {donation.amount} ({donation.platform})"); + VLog($"[WefLab] Duplicate donation ignored: {donation.donorName} {donation.amount} ({donation.platform})"); return; } @@ -1207,7 +1204,7 @@ namespace WefLab donationQueue.Enqueue(donation); queueCount = donationQueue.Count; - Debug.Log($"[WefLab] Donation queued. Queue size: {queueCount}"); + VLog($"[WefLab] Donation queued. Queue size: {queueCount}"); // Start queue processor if not running if (!isProcessingQueue) @@ -1518,7 +1515,7 @@ namespace WefLab /// private void SendSocketConnect(PlatformConnection conn) { - Debug.Log($"[WefLab] ({conn.Label}) Sending Socket.IO CONNECT (40)"); + VLog($"[WefLab] ({conn.Label}) Sending Socket.IO CONNECT (40)"); SendMessage(conn, "40"); } @@ -1561,7 +1558,7 @@ namespace WefLab string json = JsonConvert.SerializeObject(message); string fullMessage = "42" + json; - Debug.Log($"[WefLab] ({conn.Label}) Sending JOIN message: {fullMessage}"); + VLog($"[WefLab] ({conn.Label}) Sending JOIN message: {fullMessage}"); SendMessage(conn, fullMessage); }