Add server validation, profile store, session launch

Introduce server validation and a session launch flow with persistent server profiles.

- Add ConnectAndValidateServer to Networking to validate handshake/timeouts and return human-friendly errors; avoid auto-connecting in transform send path and remove unconditional local connect on startup.
- Add ServerProfileStore (header + implementation) to persist server->saveFile bindings under Documents/My Games/.../CommonwealthOnline/server-profiles.json with thread-safe load/save, lookup, touch and bind operations.
- Add SessionLaunch (header + implementation) to handle launching validated sessions, first-time join flow (auto coc to SanctuaryExt, character creation menus, save binding), F4SE message handling and periodic Update processing.
- Update ServerBrowserBridge to parse/validate ports, use ConnectAndValidateServer, display joinLoading UI events, schedule validated session launch on the game task thread, and centralize join success/failure handling.
- Wire SessionLaunch into main.cpp (update loop and F4SE message hook).
- Improve direct-connect UI in app.js with client-side validation, port parsing, and handling of joinLoading events.

These changes provide better connection validation, user feedback during joins, and automatic handling for first-time server joins with persistence of associated saves.
This commit is contained in:
2026-06-09 16:26:33 +12:00
parent e6748556ae
commit e61922678b
9 changed files with 963 additions and 47 deletions
+25 -1
View File
@@ -188,9 +188,30 @@
sendAction("joinServer", { serverId: serverId });
}
function validateDirectConnect(address, port) {
if (!address || !String(address).trim()) {
return "Server address is required.";
}
var portNum = parseInt(port, 10);
if (!port || isNaN(portNum) || portNum < 1 || portNum > 65535) {
return "Port must be a number between 1 and 65535.";
}
return null;
}
function joinDirectLocal(address, port) {
var validationError = validateDirectConnect(address, port);
if (validationError) {
showJoinOverlay(validationError, true);
return;
}
showJoinOverlay("Connecting to " + address + ":" + port + "...");
sendAction("openDirectConnect", { address: address, port: port });
sendAction("openDirectConnect", { address: address, port: String(portNumFrom(port)) });
}
function portNumFrom(port) {
var portNum = parseInt(port, 10);
return isNaN(portNum) ? 7777 : portNum;
}
function showJoinOverlay(message, isError) {
@@ -472,6 +493,9 @@
case "joinStarted":
showJoinOverlay("Connecting...");
break;
case "joinLoading":
showJoinOverlay(event.message || "Loading...");
break;
case "joinFailed":
showJoinOverlay(event.reason || "Connection failed", true);
break;