A game about forced loneliness, made by TACStudios
1using System;
2using System.Threading;
3
4using UnityEditor;
5
6using Codice.Client.Common.EventTracking;
7using Codice.Client.Common.Connection;
8using Codice.Client.Common.Threading;
9using Codice.CM.Common;
10using Codice.LogWrapper;
11using PlasticPipe;
12using Unity.PlasticSCM.Editor.UI;
13
14namespace Unity.PlasticSCM.Editor
15{
16 internal class PlasticConnectionMonitor :
17 HandleCredsAliasAndServerCert.IHostUnreachableExceptionListener
18 {
19 internal bool IsTryingReconnection { get { return mIsTryingReconnection; } }
20 internal bool IsConnected { get { return PlasticPlugin.IsUnitTesting || mIsConnected; } }
21
22 internal void CheckConnection()
23 {
24 mIsTryingReconnection = true;
25 mResetEvent.Set();
26 }
27
28 internal void SetAsConnected()
29 {
30 mIsConnected = true;
31 }
32
33 internal void Stop()
34 {
35 if (!mIsMonitoringServerConnection)
36 return;
37
38 mLog.Debug("Stop");
39
40 mIsMonitoringServerConnection = false;
41 mResetEvent.Set();
42 }
43
44 internal void SetRepositorySpecForEventTracking(RepositorySpec repSpec)
45 {
46 mRepSpecForEventTracking = repSpec;
47 }
48
49 internal void OnConnectionError(Exception ex, string server)
50 {
51 lock (mOnConnectionErrorLock)
52 {
53 LogConnectionError(ex, mIsConnected);
54
55 if (!mIsConnected)
56 return;
57
58 mIsConnected = false;
59 }
60
61 HandleConnectionLost(
62 mRepSpecForEventTracking,
63 () => StartMonitoring(server));
64 }
65
66 void HandleCredsAliasAndServerCert.IHostUnreachableExceptionListener.OnHostUnreachableException(
67 Exception ex,
68 PlasticServer plasticServer)
69 {
70 OnConnectionError(ex, plasticServer.OriginalUrl);
71 }
72
73 void StartMonitoring(string server)
74 {
75 mLog.Debug("StartMonitoring");
76
77 mIsMonitoringServerConnection = true;
78
79 Thread thread = new Thread(MonitorServerConnection);
80 thread.IsBackground = true;
81 thread.Name = "Plastic SCM Connection Monitor thread";
82 thread.Start(server);
83 }
84
85 void MonitorServerConnection(object obj)
86 {
87 string server = (string)obj;
88
89 while (true)
90 {
91 if (!mIsMonitoringServerConnection)
92 break;
93
94 try
95 {
96 mResetEvent.Reset();
97
98 if (HasConnectionToServer(server))
99 {
100 mIsConnected = true;
101 HandleConnectionRestored(mRepSpecForEventTracking);
102 break;
103 }
104
105 RepaintPlasticWindowIfOpened();
106
107 mResetEvent.WaitOne(CONNECTION_POLL_TIME_MS);
108 }
109 catch (Exception ex)
110 {
111 mLog.Error("Error checking network connectivity", ex);
112 mLog.DebugFormat("Stacktrace: {0}", ex.StackTrace);
113 }
114 finally
115 {
116 mIsTryingReconnection = false;
117 }
118 }
119 }
120
121 static void HandleConnectionLost(
122 RepositorySpec repSpecForEventTracking,
123 Action startMonitoringAction)
124 {
125 TrackConnectionLostEvent(repSpecForEventTracking);
126
127 EditorDispatcher.Dispatch(() =>
128 {
129 PlasticPlugin.Disable();
130
131 startMonitoringAction();
132
133 PlasticWindow window = GetWindowIfOpened.Plastic();
134
135 if (window != null)
136 window.Repaint();
137 });
138 }
139
140 static void HandleConnectionRestored(
141 RepositorySpec repSpecForEventTracking)
142 {
143 TrackConnectionRestoredEvent(repSpecForEventTracking);
144
145 EditorDispatcher.Dispatch(() =>
146 {
147 PlasticPlugin.Enable();
148
149 PlasticWindow window = GetWindowIfOpened.Plastic();
150
151 if (window != null)
152 window.RefreshWorkspaceUI();
153 });
154 }
155
156 static void RepaintPlasticWindowIfOpened()
157 {
158 EditorDispatcher.Dispatch(() =>
159 {
160 PlasticWindow window = GetWindowIfOpened.Plastic();
161
162 if (window != null)
163 window.Repaint();
164 });
165 }
166
167 static void LogConnectionError(Exception ex, bool isConnected)
168 {
169 mLog.WarnFormat(isConnected ?
170 "A network exception will cause the plugin to go offline" :
171 "A network exception happened while the plugin was offline!");
172
173 ExceptionsHandler.LogException("PlasticConnectionMonitor", ex);
174 }
175
176 static void TrackConnectionLostEvent(RepositorySpec repSpec)
177 {
178 if (repSpec == null)
179 return;
180
181 TrackFeatureUseEvent.For(
182 repSpec,
183 TrackFeatureUseEvent.Features.UnityPackage.DisableAutomatically);
184 }
185
186 static void TrackConnectionRestoredEvent(RepositorySpec repSpec)
187 {
188 if (repSpec == null)
189 return;
190
191 TrackFeatureUseEvent.For(
192 repSpec,
193 TrackFeatureUseEvent.Features.UnityPackage.EnableAutomatically);
194 }
195
196 static bool HasConnectionToServer(string server)
197 {
198 try
199 {
200 mLog.DebugFormat("Checking connection to {0}...", server);
201
202 return PlasticGui.Plastic.API.CheckServerConnection(server);
203 }
204 catch (Exception ex)
205 {
206 mLog.DebugFormat("Checking connection to {0} failed: {1}",
207 server,
208 ex.Message);
209 return false;
210 }
211 }
212
213 volatile bool mIsMonitoringServerConnection;
214 volatile bool mIsTryingReconnection;
215 volatile bool mIsConnected = true;
216
217 RepositorySpec mRepSpecForEventTracking;
218
219 readonly object mOnConnectionErrorLock = new object();
220 readonly ManualResetEvent mResetEvent = new ManualResetEvent(false);
221
222 const int CONNECTION_POLL_TIME_MS = 30000;
223
224 static readonly ILog mLog = PlasticApp.GetLogger("PlasticConnectionMonitor");
225 }
226}