119 lines
5.7 KiB
C#
119 lines
5.7 KiB
C#
#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR)
|
|
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using SingularityGroup.HotReload.DTO;
|
|
|
|
namespace SingularityGroup.HotReload {
|
|
|
|
static class PlayerCodePatcher {
|
|
static Timer timer;
|
|
|
|
static PlayerCodePatcher() {
|
|
if (PlayerEntrypoint.IsPlayerWithHotReload()) {
|
|
timer = new Timer(OnIntervalThreaded, (Action) OnIntervalMainThread, 500, 500);
|
|
serverHealthyAt = DateTime.MinValue;
|
|
}
|
|
}
|
|
|
|
private static DateTime serverHealthyAt;
|
|
private static TimeSpan TimeSinceServerHealthy() => DateTime.UtcNow - serverHealthyAt;
|
|
|
|
/// <summary>
|
|
/// Set server that you want to try connect to.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// This allows repetitions of:
|
|
/// - try handshake
|
|
/// - success -> try healthcheck
|
|
/// - success -> poll method patches
|
|
/// -
|
|
/// </para>
|
|
/// <para>
|
|
/// Only do this after confirming (with /handshake) that server is compatible with this build.<br/>
|
|
/// The user will be prompted if handshake needs confirmation.
|
|
/// </para>
|
|
/// </remarks>
|
|
internal static Task<ServerHandshake.Result> UpdateHost(PatchServerInfo serverInfo) {
|
|
Log.Debug($"UpdateHost to {(serverInfo == null ? "null" : serverInfo.hostName)}");
|
|
// In player builds, server is remote, se we don't load assemblies from any paths
|
|
RequestHelper.ChangeAssemblySearchPaths(Array.Empty<string>());
|
|
ServerHealthCheck.I.SetServerInfo(null); // stop doing health check on old server
|
|
RequestHelper.SetServerInfo(serverInfo);
|
|
// Show feedback about connection progress (handshake can take ~5 seconds for our big game)
|
|
if (serverInfo == null) {
|
|
Prompts.SetConnectionState(ConnectionSummary.Disconnected);
|
|
} else {
|
|
Prompts.SetConnectionState(ConnectionSummary.Connected);
|
|
Prompts.ShowConnectionDialog();
|
|
}
|
|
return ServerHandshake.I.SetServerInfo(serverInfo);
|
|
}
|
|
|
|
public static Task Disconnect() => UpdateHost(null);
|
|
|
|
static void OnIntervalThreaded(object o) {
|
|
ServerHandshake.I.CheckHandshake();
|
|
ServerHealthCheck.I.CheckHealthAsync().Forget();
|
|
|
|
ThreadUtility.RunOnMainThread((Action)o);
|
|
}
|
|
|
|
static string lastPatchId = string.Empty;
|
|
static void OnIntervalMainThread() {
|
|
PatchServerInfo verifiedServer;
|
|
if(ServerHandshake.I.TryGetVerifiedServer(out verifiedServer)) {
|
|
// now that handshake verified, we are connected.
|
|
// Note: If there is delay between handshake done and chosing to connect, then it may be outdated.
|
|
Prompts.SetConnectionState(ConnectionSummary.Connecting);
|
|
// Note: verified does not imply that server is running, sometimes we verify the host just from the deeplink data
|
|
ServerHealthCheck.I.SetServerInfo(verifiedServer);
|
|
}
|
|
|
|
if(ServerHealthCheck.I.IsServerHealthy) {
|
|
// we may have reconnected to the same host, after losing connection for several seconds
|
|
Prompts.SetConnectionState(ConnectionSummary.Connected, false);
|
|
serverHealthyAt = DateTime.UtcNow;
|
|
RequestHelper.PollMethodPatches(lastPatchId, resp => HandleResponseReceived(resp));
|
|
} else if (ServerHealthCheck.I.WasServerResponding) { // only update prompt state if disconnected server
|
|
var secondsSinceHealthy = TimeSinceServerHealthy().TotalSeconds;
|
|
var reconnectTimeout = 30; // seconds
|
|
if (secondsSinceHealthy > 2) {
|
|
Log.Info("Hot Reload was unreachable for 5 seconds, trying to reconnect...");
|
|
// feedback for the user so they know why patches are not applying
|
|
Prompts.SetConnectionState($"{ConnectionSummary.TryingToReconnect} {reconnectTimeout - secondsSinceHealthy:F0}s", false);
|
|
Prompts.ShowConnectionDialog();
|
|
}
|
|
if (secondsSinceHealthy > reconnectTimeout) {
|
|
// give up on the server, give user a way to connect to another
|
|
Log.Info($"Hot Reload was unreachable for {reconnectTimeout} seconds, disconnecting");
|
|
var disconnectedServer = RequestHelper.ServerInfo;
|
|
Disconnect().Forget();
|
|
// Let user tap button to retry connecting to the same server (maybe just need to run Hot Reload again)
|
|
// Assumption: prompt also has a way to connect to a different server
|
|
Prompts.ShowRetryDialog(disconnectedServer);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void HandleResponseReceived(MethodPatchResponse response) {
|
|
Log.Debug("PollMethodPatches handling MethodPatchResponse id:{0} response.patches.Length:{1} response.failures.Length:{2}",
|
|
response.id, response.patches.Length, response.failures.Length);
|
|
// TODO handle new response data (removed methods etc.)
|
|
if(response.patches.Length > 0) {
|
|
CodePatcher.I.RegisterPatches(response, persist: true);
|
|
}
|
|
if(response.failures.Length > 0) {
|
|
foreach (var failure in response.failures) {
|
|
// feedback to user so they know why their patch wasn't applied
|
|
Log.Warning(failure);
|
|
}
|
|
}
|
|
lastPatchId = response.id;
|
|
}
|
|
}
|
|
|
|
}
|
|
#endif
|