diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9139e86a..1a08d576 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -5,6 +5,8 @@ on:
push:
tags:
- '*'
+ branches:
+ - master
jobs:
variables:
@@ -28,7 +30,12 @@ jobs:
- name: Set tag
id: set_tag
- run: echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
+ run: |
+ if [[ $GITHUB_REF == refs/tags/* ]]; then
+ echo "VERSION=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_OUTPUT
+ else
+ echo "VERSION=${{ steps.set_unity_version.outputs.VERSION }}-webgl2-master" >> $GITHUB_OUTPUT
+ fi
- name: Set target name
id: set_build_name
@@ -62,8 +69,8 @@ jobs:
buildProject:
name: Create Unity WebGL Build 🏗
- # only build with additional parameters, the tag alone should only create a release draft
- if: ${{ needs.variables.outputs.TAG != needs.variables.outputs.UNITY_VERSION }}
+ # Build if it's a master push or if the tag is not just the unity version
+ if: ${{ github.ref == 'refs/heads/master' || needs.variables.outputs.TAG != needs.variables.outputs.UNITY_VERSION }}
needs: [ variables ]
runs-on: ubuntu-latest
strategy:
@@ -112,7 +119,7 @@ jobs:
createRelease:
name: Create Github release 🐙
# only run for the pure tag without build parameters
- if: ${{ needs.variables.outputs.TAG == needs.variables.outputs.UNITY_VERSION }}
+ if: ${{ github.ref_type == 'tag' && needs.variables.outputs.TAG == needs.variables.outputs.UNITY_VERSION }}
needs: [ variables ]
runs-on: ubuntu-latest
steps:
diff --git a/Assets/Plugins/WebGL/WebBridge/CommonCommands.cs b/Assets/Plugins/WebGL/WebBridge/CommonCommands.cs
index 0c64ed34..d4d48e2d 100644
--- a/Assets/Plugins/WebGL/WebBridge/CommonCommands.cs
+++ b/Assets/Plugins/WebGL/WebBridge/CommonCommands.cs
@@ -9,6 +9,7 @@
// --------------------------------------------------------------------------------------------------------------------
using System;
+using System.Collections;
using System.Collections.Generic;
using Supyrb.Attributes;
using UnityEngine;
@@ -274,5 +275,84 @@ public void LogShaderCompilation(int enabled)
GraphicsSettings.logWhenShaderIsCompiled = enabled == 1;
Debug.Log($"GraphicsSettings.logWhenShaderIsCompiled: {GraphicsSettings.logWhenShaderIsCompiled}");
}
+
+ ///
+ /// Copy text to clipboard using the browser's clipboard API
+ ///
+ /// Text to copy to clipboard
+ [WebCommand(Description = "Copy text to clipboard")]
+ public void CopyToClipboard(string text)
+ {
+ WebToolPlugins.CopyToClipboard(text);
+ }
+
+ ///
+ /// Check if the browser has an internet connection
+ ///
+ [WebCommand(Description = "Check if browser is online")]
+ public void CheckOnlineStatus()
+ {
+ bool isOnline = WebToolPlugins.IsOnline();
+ Debug.Log($"Online Status: {(isOnline ? "Connected" : "Disconnected")}");
+ }
+
+ ///
+ /// Captures the current screen and saves it as a PNG file.
+ ///
+ [WebCommand(Description = "Save current screen as PNG")]
+ public void SaveScreenshot()
+ {
+ SaveScreenshotSuperSize(1);
+ }
+
+ ///
+ /// Captures the current screen and saves it as a PNG file.
+ ///
+ /// 1 for normal size, 2 for double size, 4 for quadruple size
+ [WebCommand(Description = "Save current screen as PNG with variable super size")]
+ public void SaveScreenshotSuperSize(int superSize)
+ {
+ StartCoroutine(CaptureScreenshot(superSize));
+ }
+
+ private IEnumerator CaptureScreenshot(int superSize)
+ {
+ // Wait for the end of frame to ensure everything is rendered
+ yield return new WaitForEndOfFrame();
+
+ string filename = "screenshot.png";
+ try
+ {
+ // Capture the screen
+ Texture2D screenshot = ScreenCapture.CaptureScreenshotAsTexture(superSize);
+
+ try
+ {
+ // Convert to PNG
+ byte[] pngData = screenshot.EncodeToPNG();
+
+ // Download through browser
+ WebToolPlugins.DownloadBinaryFile(filename, pngData, "image/png");
+
+ Debug.Log($"Screenshot saved as {filename} ({screenshot.width}x{screenshot.height}) with size {pngData.Length} bytes");
+ }
+ finally
+ {
+ // Clean up the texture
+ if (Application.isPlaying)
+ {
+ Destroy(screenshot);
+ }
+ else
+ {
+ DestroyImmediate(screenshot);
+ }
+ }
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogError($"Failed to save screenshot: {e.Message}");
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Assets/Plugins/WebGL/WebBridge/WebBridge.cs b/Assets/Plugins/WebGL/WebBridge/WebBridge.cs
index 0bca0b6a..4ca27a17 100644
--- a/Assets/Plugins/WebGL/WebBridge/WebBridge.cs
+++ b/Assets/Plugins/WebGL/WebBridge/WebBridge.cs
@@ -109,6 +109,7 @@ public void Help()
{
sb.AppendLine($"---{webCommand.GetType().Name}---");
MethodInfo[] methods = webCommand.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance);
+ Array.Sort(methods, (a, b) => string.Compare(a.Name, b.Name));
for (int i = 0; i < methods.Length; i++)
{
@@ -123,11 +124,11 @@ public void Help()
var parameter = parameters[j];
if (parameter.ParameterType == typeof(string))
{
- sb.Append($", \"{parameter.ParameterType} {parameter.Name}\"");
+ sb.Append($", \"{GetFriendlyTypeName(parameter.ParameterType)} {parameter.Name}\"");
}
else
{
- sb.Append($", {parameter.ParameterType} {parameter.Name}");
+ sb.Append($", {GetFriendlyTypeName(parameter.ParameterType)} {parameter.Name}");
}
}
@@ -140,5 +141,16 @@ public void Help()
sb.AppendLine($"\nRun a command with 'runUnityCommand(\"COMMAND_NAME\",PARAMETER);'");
Debug.Log(sb.ToString());
}
+
+ private string GetFriendlyTypeName(Type type)
+ {
+ if (type == typeof(int)) return "int";
+ if (type == typeof(long)) return "long";
+ if (type == typeof(float)) return "float";
+ if (type == typeof(double)) return "double";
+ if (type == typeof(bool)) return "bool";
+ if (type == typeof(string)) return "string";
+ return type.Name;
+ }
}
}
\ No newline at end of file
diff --git a/Assets/Plugins/WebGL/WebTools/WebToolPlugins.cs b/Assets/Plugins/WebGL/WebTools/WebToolPlugins.cs
index 8b3c0cd4..b79af50b 100644
--- a/Assets/Plugins/WebGL/WebTools/WebToolPlugins.cs
+++ b/Assets/Plugins/WebGL/WebTools/WebToolPlugins.cs
@@ -35,9 +35,14 @@ public static class WebToolPlugins
[DllImport("__Internal")]
private static extern uint _GetTotalMemorySize();
[DllImport("__Internal")]
- private static extern uint _GetStaticMemorySize();
+ private static extern bool _CopyToClipboard(string text);
[DllImport("__Internal")]
- private static extern uint _GetDynamicMemorySize();
+ private static extern int _IsOnline();
+ [DllImport("__Internal")]
+ private static extern void _DownloadFile(string filename, string content);
+ [DllImport("__Internal")]
+ private static extern void _DownloadBlob(string filename, byte[] byteArray, int byteLength, string mimeType);
+
#endif
private static bool _infoPanelVisible = false;
@@ -154,23 +159,6 @@ public static bool IsMobileDevice()
userAgent.Contains("Android");
}
- ///
- /// Get the total memory size used by the application in MB
- ///
- /// Size in MB
- public static float GetTotalMemorySize()
- {
-#if UNITY_WEBGL && !UNITY_EDITOR
- var bytes = _GetTotalMemorySize();
- return GetMegaBytes(bytes);
-#elif UNITY_EDITOR && WEBTOOLS_LOG_CALLS
- Debug.Log($"{nameof(WebToolPlugins)}.{nameof(GetTotalMemorySize)} called");
- return -1f;
-#else
- return -1f;
-#endif
- }
-
///
/// Log all current memory data in MB
///
@@ -178,25 +166,24 @@ public static void LogMemory()
{
#if UNITY_WEBGL && !UNITY_EDITOR
var managed = GetManagedMemorySize();
- var native = GetNativeMemorySize();
var total = GetTotalMemorySize();
- Debug.Log($"Memory stats:\nManaged: {managed:0.00}MB\nNative: {native:0.00}MB\nTotal: {total:0.00}MB");
+ Debug.Log($"Memory stats:\nManaged: {managed:0.00}MB\nTotal: {total:0.00}MB");
#elif UNITY_EDITOR && WEBTOOLS_LOG_CALLS
Debug.Log($"{nameof(WebToolPlugins)}.{nameof(LogMemory)} called");
#endif
}
///
- /// Get the static memory size used by the application in MB
+ /// Get the total memory size used by the application in MB
///
/// Size in MB
- public static float GetStaticMemorySize()
+ public static float GetTotalMemorySize()
{
#if UNITY_WEBGL && !UNITY_EDITOR
- var bytes = _GetStaticMemorySize();
+ var bytes = _GetTotalMemorySize();
return GetMegaBytes(bytes);
#elif UNITY_EDITOR && WEBTOOLS_LOG_CALLS
- Debug.Log($"{nameof(WebToolPlugins)}.{nameof(GetStaticMemorySize)} called");
+ Debug.Log($"{nameof(WebToolPlugins)}.{nameof(GetTotalMemorySize)} called");
return -1f;
#else
return -1f;
@@ -204,56 +191,93 @@ public static float GetStaticMemorySize()
}
///
- /// Get the dynamic memory size used by the application in MB
+ /// Get the managed memory size used by the application in MB
///
/// Size in MB
- public static float GetDynamicMemorySize()
+ public static float GetManagedMemorySize()
{
-#if UNITY_WEBGL && !UNITY_EDITOR
- var bytes = _GetStaticMemorySize();
+ var bytes = (uint)GC.GetTotalMemory(false);
return GetMegaBytes(bytes);
-#elif UNITY_EDITOR && WEBTOOLS_LOG_CALLS
- Debug.Log($"{nameof(WebToolPlugins)}.{nameof(GetDynamicMemorySize)} called");
- return -1f;
-#else
- return -1f;
-#endif
}
///
- /// Get the native memory size used by the application in MB (Static + Dynamic memory)
+ /// Converts bytes (B) to mega bytes (MB)
///
- /// Size in MB
- public static float GetNativeMemorySize()
+ /// bytes to convert
+ /// bytes / (1024 * 1024)
+ private static float GetMegaBytes(uint bytes)
{
-#if UNITY_WEBGL && !UNITY_EDITOR
- return GetDynamicMemorySize() + GetStaticMemorySize();
-#elif UNITY_EDITOR && WEBTOOLS_LOG_CALLS
- Debug.Log($"{nameof(WebToolPlugins)}.{nameof(GetNativeMemorySize)} called");
- return -1f;
-#else
- return -1f;
-#endif
+ return (float)bytes / (1024 * 1024);
}
///
- /// Get the managed memory size used by the application in MB
+ /// Copies the specified text to the system clipboard using the browser's clipboard API.
+ /// Only works in WebGL builds and requires clipboard-write permission in modern browsers.
///
- /// Size in MB
- public static float GetManagedMemorySize()
+ /// The text to copy to the clipboard
+ /// True if the copy operation was successful, false otherwise
+ public static void CopyToClipboard(string text)
{
- var bytes = (uint)GC.GetTotalMemory(false);
- return GetMegaBytes(bytes);
+ #if UNITY_WEBGL && !UNITY_EDITOR
+ _CopyToClipboard(text);
+ #elif UNITY_EDITOR && WEBTOOLS_LOG_CALLS
+ Debug.Log($"{nameof(WebToolPlugins)}.{nameof(CopyToClipboard)} called with: {text}");
+ #endif
}
///
- /// Converts bytes (B) to mega bytes (MB)
+ /// Checks if the browser currently has an internet connection using the navigator.onLine property.
///
- /// bytes to convert
- /// bytes / (1024 * 1024)
- private static float GetMegaBytes(uint bytes)
+ /// True if the browser is online, false if it's offline
+ public static bool IsOnline()
{
- return (float)bytes / (1024 * 1024);
+ #if UNITY_WEBGL && !UNITY_EDITOR
+ return _IsOnline() == 1;
+ #elif UNITY_EDITOR && WEBTOOLS_LOG_CALLS
+ Debug.Log($"{nameof(WebToolPlugins)}.{nameof(IsOnline)} called");
+ return true;
+ #else
+ return true;
+ #endif
+ }
+
+ ///
+ /// Downloads a text file through the browser with the specified filename and content.
+ /// Creates a temporary anchor element to trigger the download.
+ ///
+ /// The name of the file to be downloaded
+ /// The text content to be saved in the file
+ public static void DownloadTextFile(string filename, string content)
+ {
+ #if UNITY_WEBGL && !UNITY_EDITOR
+ _DownloadFile(filename, content);
+ #elif UNITY_EDITOR && WEBTOOLS_LOG_CALLS
+ Debug.Log($"{nameof(WebToolPlugins)}.{nameof(DownloadTextFile)} called with filename: {filename}");
+ #endif
+ }
+
+ ///
+ /// Downloads a binary file through the browser with the specified filename and data.
+ /// Creates a Blob with the specified MIME type and triggers the download.
+ ///
+ /// The name of the file to be downloaded
+ /// The binary data to be saved in the file
+ /// The MIME type of the file (defaults to "application/octet-stream")
+ ///
+ ///
+ /// // Example: Save a Texture2D as PNG
+ /// Texture2D texture;
+ /// byte[] pngData = texture.EncodeToPNG();
+ /// WebToolPlugins.DownloadBinaryFile("texture.png", pngData, "image/png");
+ ///
+ ///
+ public static void DownloadBinaryFile(string filename, byte[] data, string mimeType = "application/octet-stream")
+ {
+ #if UNITY_WEBGL && !UNITY_EDITOR
+ _DownloadBlob(filename, data, data.Length, mimeType);
+ #elif UNITY_EDITOR && WEBTOOLS_LOG_CALLS
+ Debug.Log($"{nameof(WebToolPlugins)}.{nameof(DownloadBinaryFile)} called with filename: {filename}");
+ #endif
}
}
}
\ No newline at end of file
diff --git a/Assets/Plugins/WebGL/WebTools/WebToolPlugins.jslib b/Assets/Plugins/WebGL/WebTools/WebToolPlugins.jslib
index 893777d5..637e9216 100644
--- a/Assets/Plugins/WebGL/WebTools/WebToolPlugins.jslib
+++ b/Assets/Plugins/WebGL/WebTools/WebToolPlugins.jslib
@@ -46,7 +46,7 @@ var WebGlPlugins =
}
var currentTimeRounded = currentTime.toFixed(2);
- console.log('Time tracker event ' +eventNameText +': ' + currentTimeRounded + 'ms');
+ console.log('Time tracker event ' + eventNameText + ': ' + currentTimeRounded + 'ms');
},
_AddFpsTrackingEvent: function(fps) {
@@ -99,43 +99,53 @@ var WebGlPlugins =
return -1;
},
- _GetTotalStackSize: function()
- {
- if(typeof Module !== 'undefined' && typeof Module.STACK_SIZE !== 'undefined') {
- return Module.STACK_SIZE;
- }
- if(typeof TOTAL_STACK !== 'undefined') { // Legacy support
- return TOTAL_STACK;
- }
-
- console.warn("Problem with retrieving stack size");
- return -1;
+ _CopyToClipboard: function(text) {
+ var str = UTF8ToString(text);
+ navigator.clipboard.writeText(str)
+ .then(function() {
+ })
+ .catch(function(err) {
+ console.error('Failed to copy text: ', err);
+ });
},
- _GetStaticMemorySize: function()
- {
- if(typeof Module !== 'undefined' && typeof Module.staticAlloc !== 'undefined') {
- return Module.staticAlloc;
- }
- if(typeof STATICTOP !== 'undefined' && typeof STATIC_BASE !== 'undefined') { // Legacy support
- return STATICTOP - STATIC_BASE;
- }
+ _IsOnline: function() {
+ return navigator.onLine ? 1 : 0;
+ },
- console.warn("Problem with retrieving static memory size");
- return -1;
+ _DownloadFile: function(filename, content) {
+ var filenameStr = UTF8ToString(filename);
+ var contentStr = UTF8ToString(content);
+
+ var element = document.createElement('a');
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(contentStr));
+ element.setAttribute('download', filenameStr);
+ element.style.display = 'none';
+ document.body.appendChild(element);
+ element.click();
+ document.body.removeChild(element);
},
- _GetDynamicMemorySize: function()
- {
- if(typeof Module !== 'undefined' && typeof Module.dynamicAlloc !== 'undefined') {
- return Module.dynamicAlloc;
- }
- if(typeof HEAP32 !== 'undefined' && typeof DYNAMICTOP_PTR !== 'undefined' && typeof DYNAMIC_BASE !== 'undefined') { // Legacy support
- return HEAP32[DYNAMICTOP_PTR >> 2] - DYNAMIC_BASE;
+ _DownloadBlob: function(filename, byteArray, byteLength, mimeType) {
+ var filenameStr = UTF8ToString(filename);
+ var mimeTypeStr = UTF8ToString(mimeType);
+
+ var data = new Uint8Array(byteLength);
+ for (var i = 0; i < byteLength; i++) {
+ data[i] = HEAPU8[byteArray + i];
}
- console.warn("Problem with retrieving dynamic memory size");
- return -1;
+ var blob = new Blob([data], { type: mimeTypeStr });
+ var url = URL.createObjectURL(blob);
+
+ var element = document.createElement('a');
+ element.setAttribute('href', url);
+ element.setAttribute('download', filenameStr);
+ element.style.display = 'none';
+ document.body.appendChild(element);
+ element.click();
+ document.body.removeChild(element);
+ URL.revokeObjectURL(url);
}
};
diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset
index 62a17737..6970908a 100644
--- a/ProjectSettings/ProjectSettings.asset
+++ b/ProjectSettings/ProjectSettings.asset
@@ -140,7 +140,7 @@ PlayerSettings:
loadStoreDebugModeEnabled: 0
visionOSBundleVersion: 1.0
tvOSBundleVersion: 1.0
- bundleVersion: 1.3.0
+ bundleVersion: 1.4.0
preloadedAssets: []
metroInputSource: 0
wsaTransparentSwapchain: 0