功能批量实现 优化 服务端 新浮动小游戏框架 新界面UI

This commit is contained in:
FloatGaming
2026-04-24 13:20:16 +08:00
parent e0bc0bbf08
commit 5eec879582
257 changed files with 38481 additions and 1288 deletions
+111 -159
View File
@@ -6,6 +6,8 @@ using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Text.RegularExpressions;
using UnityEngine.Networking;
using GameServer.Client;
using Debug = UnityEngine.Debug;
@@ -19,7 +21,7 @@ public class WebViewLauncher : MonoBehaviour
public Vector2 windowPosition = new Vector2(100, 100);
[Header("Editor Only Path")]
public string editorExeRoot = @"E:\Unity\ban_total\fdBrowser";
public string editorExeRoot = @"E:\Unity\ban_total\fdGamer\build\windows\x64\runner\Release";
[Header("H5 Folder Paths")]
public string h5EditorPath = @"E:\Unity\ban_total\ban_test\Bansonic_Data\StreamingAssets\h5LG";
@@ -28,6 +30,8 @@ public class WebViewLauncher : MonoBehaviour
public string thirdPartyGamesPath = "_thirdPartyGames";
private Process webProcess;
private const string FlutterExeName = "浮动小游戏.exe";
private const string LocalControlBaseUrl = "http://127.0.0.1:18961";
// Event invoked when web process main window handle becomes available
public event Action WebViewReady;
@@ -87,7 +91,7 @@ public class WebViewLauncher : MonoBehaviour
if (goButton != null) goButton.onClick.AddListener(OnGoButtonClicked);
// Load autostart preference and bind toggle
autostart = PlayerPrefs.GetInt(PREF_AUTOSTART, 1) == 1;
autostart = PlayerPrefs.GetInt(PREF_AUTOSTART, 0) == 1;
if (autostartToggle != null)
{
autostartToggle.isOn = autostart;
@@ -97,72 +101,19 @@ public class WebViewLauncher : MonoBehaviour
// set default homepage if empty
if (string.IsNullOrEmpty(homepageUrl))
{
#if UNITY_EDITOR
string candidate = Path.Combine(h5EditorPath, "_officialDocs", "openings", "index.html");
if (File.Exists(candidate))
homepageUrl = new Uri(candidate).AbsoluteUri;
else
homepageUrl = startUrl;
#else
string candidate = Path.Combine(Application.streamingAssetsPath, "h5LG", "_officialDocs", "openings", "index.html");
if (File.Exists(candidate))
homepageUrl = new Uri(candidate).AbsoluteUri;
else
homepageUrl = startUrl;
#endif
homepageUrl = string.Empty;
}
}
void Start()
{
// Ensure H5 root exists
string h5Folder = GetH5FolderPath();
if (!Directory.Exists(h5Folder))
{
Directory.CreateDirectory(h5Folder);
Debug.Log("Created H5 folder: " + h5Folder);
}
// Ensure controlText is visible by default
if (controlText != null)
{
controlText.gameObject.SetActive(true);
Debug.Log("Control text set to visible by default");
}
// Prefer explicit openings index as startup page when available
string preferred = Path.Combine(h5Folder, "_officialDocs", "openings", "index.html");
if (File.Exists(preferred))
{
startUrl = new Uri(preferred).AbsoluteUri;
Debug.Log("Using preferred startup page: " + startUrl);
// show initial url in input field
if (urlInputField != null)
urlInputField.text = GetDisplayUrl(startUrl);
if (autostart) LaunchWebView();
return;
}
// Documentation text normalized.
var h5SubFolders = ScanH5Folders(h5Folder);
if (h5SubFolders.Count > 0)
{
Debug.Log("Found H5 folders: " + string.Join(", ", h5SubFolders));
string selectedFolder = h5SubFolders[0]; // Documentation text normalized.
string candidatePath = Path.Combine(h5Folder, selectedFolder, "index.html");
startUrl = new Uri(candidatePath).AbsoluteUri; // ensure proper file:/// URI
Debug.Log("Selected H5 folder: " + selectedFolder + ", URL: " + startUrl);
}
else
{
Debug.LogWarning("No H5 subfolders with index.html found in: " + h5Folder);
}
// show initial url in input field if available (use shortened display for local files that are indexed)
if (urlInputField != null)
{
urlInputField.text = GetDisplayUrl(startUrl);
urlInputField.text = string.Empty;
}
if (autostart) LaunchWebView();
@@ -245,7 +196,65 @@ public class WebViewLauncher : MonoBehaviour
}
}
string GetServerBaseUrl()
{
if (NetworkManager.Instance != null && !string.IsNullOrWhiteSpace(NetworkManager.Instance.ServerUrl))
{
string raw = NetworkManager.Instance.ServerUrl.Trim();
if (Uri.TryCreate(raw, UriKind.Absolute, out Uri parsed))
{
return $"{parsed.Scheme}://{parsed.Authority}";
}
return raw.TrimEnd('/');
}
return "http://47.112.187.172:8080";
}
string BuildFlutterLaunchArguments(string openUrl)
{
string args = $"--server-base=\"{GetServerBaseUrl()}\"";
if (!string.IsNullOrWhiteSpace(openUrl))
{
args += $" --open-url=\"{openUrl}\"";
}
return args;
}
IEnumerator SendLocalControlRequest(string path, string method = UnityWebRequest.kHttpVerbGET, string jsonBody = null)
{
string url = $"{LocalControlBaseUrl}{path}";
using UnityWebRequest request = new UnityWebRequest(url, method);
request.downloadHandler = new DownloadHandlerBuffer();
if (!string.IsNullOrEmpty(jsonBody))
{
byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonBody);
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
request.SetRequestHeader("Content-Type", "application/json");
}
yield return request.SendWebRequest();
if (request.result != UnityWebRequest.Result.Success)
{
Debug.LogWarning($"Local Flutter control request failed: {method} {url} -> {request.error}");
}
}
void SendLocalControlOpen(string normalizedUrl)
{
string escaped = normalizedUrl.Replace("\\", "\\\\").Replace("\"", "\\\"");
StartCoroutine(SendLocalControlRequest("/open", UnityWebRequest.kHttpVerbPOST, $"{{\"url\":\"{escaped}\"}}"));
}
void SendLocalControlCommand(string path)
{
StartCoroutine(SendLocalControlRequest(path));
}
void LaunchWebView()
{
LaunchWebViewWithUrl(string.Empty);
}
void LaunchWebViewWithUrlInternal(string openUrl)
{
string exePath = GetExePath();
@@ -253,12 +262,11 @@ public class WebViewLauncher : MonoBehaviour
if (!File.Exists(exePath))
{
Debug.LogError("fdBrowser.exe not found: " + exePath);
Debug.LogError("浮动小游戏主程序不存在: " + exePath);
return;
}
// Documentation text normalized.
string args = $"0 0 800 600 \"{startUrl}\"";
string args = BuildFlutterLaunchArguments(openUrl);
Debug.Log($"Launching: {exePath} {args}");
@@ -273,7 +281,7 @@ public class WebViewLauncher : MonoBehaviour
try
{
webProcess = Process.Start(psi);
Debug.Log("WebView2Host started successfully. Process ID: " + webProcess.Id);
Debug.Log("浮动小游戏已启动。PID: " + webProcess.Id);
// Start coroutine to wait until main window handle is ready
StartCoroutine(WaitForMainWindowHandle());
@@ -373,64 +381,49 @@ public class WebViewLauncher : MonoBehaviour
if (urlInputField != null)
urlInputField.text = GetDisplayUrl(final);
// if process not running, launch; otherwise try to send url to existing window
if (webProcess == null || webProcess.HasExited)
{
Debug.Log("WebView process not running. Launching with URL: " + final);
LaunchWebView();
Debug.Log("浮动小游戏未运行,直接启动并打开 URL: " + final);
LaunchWebViewWithUrlInternal(final);
return;
}
try
{
Debug.Log($"WebView process running. PID={webProcess.Id}, MainWindowHandle=0x{webProcess.MainWindowHandle.ToInt64():X}");
}
catch { }
// Documentation text normalized.
bool sent = WebViewWin32.TrySendUrlToWindow(webProcess, final);
Debug.Log($"TrySendUrlToWindow returned: {sent}");
if (sent)
{
Debug.Log("Sent URL to existing WebView process: " + final);
// Notify followers that webview content may have changed and they should reapply positioning
WebViewReady?.Invoke();
return;
}
// Documentation text normalized.
Debug.Log("Failed to send URL to existing process, restarting to open: " + final);
KillWebView();
LaunchWebView();
// after relaunch, WaitForMainWindowHandle will invoke WebViewReady when handle ready
SendLocalControlOpen(final);
WebViewReady?.Invoke();
}
// Try to send a simple command string to the webview process via WM_COPYDATA. Returns true when SendMessage returned non-zero.
bool TrySendCommand(string cmd)
{
if (webProcess == null || webProcess.HasExited)
{
Debug.LogWarning("TrySendCommand: webProcess not running");
Debug.LogWarning("TrySendCommand: 浮动小游戏未运行");
return false;
}
bool sent = WebViewWin32.TrySendUrlToWindow(webProcess, cmd);
Debug.Log($"TrySendCommand('{cmd}') returned: {sent}");
return sent;
switch (cmd)
{
case "CMD:REFRESH":
SendLocalControlCommand("/refresh");
return true;
case "CMD:BACK":
SendLocalControlCommand("/back");
return true;
case "CMD:FORWARD":
SendLocalControlCommand("/forward");
return true;
case "CMD:HOME":
SendLocalControlCommand("/home");
return true;
default:
return false;
}
}
// UI control methods
public void RefreshWebView()
{
// Try sending a refresh command; fallback to restarting to refresh
if (TrySendCommand("CMD:REFRESH"))
{
Debug.Log("Sent REFRESH command to webview");
return;
}
Debug.Log("REFRESH command not supported, restarting webview to refresh");
KillWebView();
LaunchWebView();
}
@@ -442,7 +435,6 @@ public class WebViewLauncher : MonoBehaviour
public void RestartWebView()
{
Debug.Log("Restarting webview process");
KillWebView();
LaunchWebView();
}
@@ -469,28 +461,7 @@ public class WebViewLauncher : MonoBehaviour
void OnHomepageClicked()
{
// Always navigate to StreamingAssets/h5LG/_officialDocs/openings/index.html
string candidate = Path.Combine(Application.streamingAssetsPath, "h5LG", "_officialDocs", "openings", "index.html");
if (!File.Exists(candidate))
{
Debug.LogWarning("Homepage index not found at: " + candidate + ". Falling back to homepageUrl or startUrl.");
if (!string.IsNullOrEmpty(homepageUrl))
{
LaunchWebViewWithUrl(homepageUrl);
}
else
{
LaunchWebViewWithUrl(startUrl);
}
return;
}
string fileUri = new Uri(candidate).AbsoluteUri;
LaunchWebViewWithUrl(fileUri);
// OpenUrl will update the input field using GetDisplayUrl(final). Ensure display shows the shortened localhost form.
if (urlInputField != null)
urlInputField.text = GetDisplayUrl(fileUri);
StartWebView();
}
void OnUrlInputEndEdit(string text)
@@ -559,7 +530,13 @@ public class WebViewLauncher : MonoBehaviour
public void StartWebView()
{
LaunchWebView();
if (webProcess == null || webProcess.HasExited)
{
LaunchWebView();
return;
}
SendLocalControlCommand("/home");
WebViewReady?.Invoke();
}
public void StopWebView()
@@ -578,19 +555,20 @@ public class WebViewLauncher : MonoBehaviour
string GetExePath()
{
#if UNITY_EDITOR
return Path.Combine(editorExeRoot, "fdBrowser.exe");
string editorPath = Path.Combine(editorExeRoot, FlutterExeName);
if (File.Exists(editorPath))
{
return editorPath;
}
return Path.Combine(Application.streamingAssetsPath, "fdGamer", FlutterExeName);
#else
// Documentation text normalized.
string streamingPath = Path.Combine(Application.streamingAssetsPath, "fdBrowser", "fdBrowser.exe");
string streamingPath = Path.Combine(Application.streamingAssetsPath, "fdGamer", FlutterExeName);
if (File.Exists(streamingPath))
{
Debug.Log("Using StreamingAssets path: " + streamingPath);
return streamingPath;
}
// Documentation text normalized.
string gameRoot = Path.GetDirectoryName(Application.dataPath);
string fallbackPath = Path.Combine(gameRoot, "fdBrowser", "fdBrowser.exe");
Debug.Log("Using fallback path: " + fallbackPath);
string fallbackPath = Path.Combine(gameRoot, "fdGamer", FlutterExeName);
return fallbackPath;
#endif
}
@@ -646,10 +624,7 @@ public class WebViewLauncher : MonoBehaviour
/// </summary>
public bool TryUpdateWebViewRect(int x, int y, int w, int h)
{
if (webProcess == null || webProcess.HasExited)
return false;
return WebViewWin32.TrySetWindowRect(webProcess, x, y, w, h);
return webProcess != null && !webProcess.HasExited;
}
string GetDisplayUrl(string url)
@@ -729,35 +704,12 @@ public class WebViewLauncher : MonoBehaviour
if (urlInputField != null)
urlInputField.text = GetDisplayUrl(finalUrl);
// if process not running, launch; otherwise try to send url to existing window
if (webProcess == null || webProcess.HasExited)
{
Debug.Log("WebView process not running. Launching with URL: " + finalUrl);
LaunchWebView();
LaunchWebViewWithUrlInternal(finalUrl);
return;
}
try
{
Debug.Log($"WebView process running. PID={webProcess.Id}, MainWindowHandle=0x{webProcess.MainWindowHandle.ToInt64():X}");
}
catch { }
// Documentation text normalized.
bool sent = WebViewWin32.TrySendUrlToWindow(webProcess, finalUrl);
Debug.Log($"TrySendUrlToWindow returned: {sent}");
if (sent)
{
Debug.Log("Sent URL to existing WebView process: " + finalUrl);
// Notify followers that webview content may have changed and they should reapply positioning
WebViewReady?.Invoke();
return;
}
// Documentation text normalized.
Debug.Log("Failed to send URL to existing process, restarting to open: " + finalUrl);
KillWebView();
LaunchWebView();
// after relaunch, WaitForMainWindowHandle will invoke WebViewReady when handle ready
SendLocalControlOpen(finalUrl);
WebViewReady?.Invoke();
}
}