2 using System.Collections.Immutable;
4 using System.Threading.Tasks;
5 using Microsoft.Xna.Framework;
9 internal abstract class ClientPeer<TEndpoint> : ClientPeer where TEndpoint :
Endpoint
11 public new TEndpoint ServerEndpoint => (base.ServerEndpoint as TEndpoint)!;
12 protected ClientPeer(TEndpoint serverEndpoint, ImmutableArray<Endpoint> allServerEndpoints, Callbacks callbacks, Option<int> ownerKey)
13 : base(serverEndpoint, allServerEndpoints, callbacks, ownerKey) { }
16 internal abstract class ClientPeer
18 public ImmutableArray<ServerContentPackage> ServerContentPackages {
get;
set; } =
19 ImmutableArray<ServerContentPackage>.Empty;
21 public bool AllowModDownloads {
get;
private set; } =
true;
23 public string AutomaticallyAttemptedPassword =
string.Empty;
25 public readonly record
struct Callbacks(
26 Callbacks.MessageCallback OnMessageReceived,
27 Callbacks.DisconnectCallback OnDisconnect,
28 Callbacks.InitializationCompleteCallback OnInitializationComplete)
30 public delegate
void MessageCallback(IReadMessage message);
31 public delegate
void DisconnectCallback(PeerDisconnectPacket disconnectPacket);
32 public delegate
void InitializationCompleteCallback();
35 protected readonly Callbacks callbacks;
37 public readonly
Endpoint ServerEndpoint;
38 public readonly ImmutableArray<Endpoint> AllServerEndpoints;
39 public NetworkConnection? ServerConnection {
get;
protected set; }
41 protected bool IsOwner => ownerKey.IsSome();
42 protected readonly Option<int> ownerKey;
44 public bool IsActive => isActive;
46 protected bool isActive;
48 protected ClientPeer(Endpoint serverEndpoint, ImmutableArray<Endpoint> allServerEndpoints, Callbacks callbacks, Option<int> ownerKey)
50 ServerEndpoint = serverEndpoint;
51 AllServerEndpoints = allServerEndpoints;
52 this.callbacks = callbacks;
53 this.ownerKey = ownerKey;
56 public abstract void Start();
57 public abstract void Close(PeerDisconnectPacket peerDisconnectPacket);
58 public abstract void Update(
float deltaTime);
59 public abstract void Send(IWriteMessage msg, DeliveryMethod deliveryMethod,
bool compressPastThreshold =
true);
60 public abstract void SendPassword(
string password);
62 protected abstract void SendMsgInternal(PeerPacketHeaders headers, INetSerializableStruct? body);
64 protected ConnectionInitialization initializationStep;
65 public bool ContentPackageOrderReceived {
get;
set; }
66 protected int passwordSalt;
67 protected Option<AuthenticationTicket> authTicket;
68 private GUIMessageBox? passwordMsgBox;
70 public bool WaitingForPassword
71 => isActive && initializationStep == ConnectionInitialization.Password
72 && passwordMsgBox !=
null
73 && GUIMessageBox.MessageBoxes.Contains(passwordMsgBox);
81 protected abstract Task<Option<AccountId>> GetAccountId();
83 protected void OnInitializationComplete()
85 passwordMsgBox?.Close();
86 if (initializationStep == ConnectionInitialization.Success) {
return; }
88 callbacks.OnInitializationComplete.Invoke();
89 initializationStep = ConnectionInitialization.Success;
92 protected void ReadConnectionInitializationStep(IncomingInitializationMessage inc)
94 if (inc.InitializationStep != ConnectionInitialization.Password)
96 passwordMsgBox?.Close();
99 switch (inc.InitializationStep)
101 case ConnectionInitialization.AuthInfoAndVersion:
103 if (initializationStep != ConnectionInitialization.AuthInfoAndVersion) {
return; }
105 TaskPool.Add($
"{GetType().Name}.{nameof(GetAccountId)}", GetAccountId(), t =>
110 if (!t.TryGetResult(out Option<AccountId> accountId))
112 Close(PeerDisconnectPacket.WithReason(DisconnectReason.AuthenticationFailed));
115 var headers =
new PeerPacketHeaders
117 DeliveryMethod = DeliveryMethod.Reliable,
118 PacketHeader = PacketHeader.IsConnectionInitializationStep,
119 Initialization = ConnectionInitialization.AuthInfoAndVersion
122 var body =
new ClientAuthTicketAndVersionPacket
124 Name = GameMain.Client?.Name ??
"Unknown",
126 AccountId = accountId,
127 AuthTicket = authTicket,
128 GameVersion = GameMain.Version.ToString(),
129 Language = GameSettings.CurrentConfig.Language.Value
132 SendMsgInternal(headers, body);
136 case ConnectionInitialization.ContentPackageOrder:
138 if (initializationStep
139 is ConnectionInitialization.AuthInfoAndVersion
140 or ConnectionInitialization.Password)
142 initializationStep = ConnectionInitialization.ContentPackageOrder;
145 if (initializationStep != ConnectionInitialization.ContentPackageOrder) {
return; }
147 PeerPacketHeaders headers =
new PeerPacketHeaders
149 DeliveryMethod = DeliveryMethod.Reliable,
150 PacketHeader = PacketHeader.IsConnectionInitializationStep,
151 Initialization = ConnectionInitialization.ContentPackageOrder
154 var orderPacket = INetSerializableStruct.Read<ServerPeerContentPackageOrderPacket>(inc.Message);
156 if (!ContentPackageOrderReceived)
158 ServerContentPackages = orderPacket.ContentPackages;
159 AllowModDownloads = orderPacket.AllowModDownloads;
160 if (ServerContentPackages.Length == 0)
162 string errorMsg =
"Error in ContentPackageOrder message: list of content packages enabled on the server was empty.";
163 GameAnalyticsManager.AddErrorEventOnce(
"ClientPeer.ReadConnectionInitializationStep:NoContentPackages", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
164 DebugConsole.ThrowError(errorMsg);
166 ContentPackageOrderReceived =
true;
168 SendMsgInternal(headers,
null);
172 case ConnectionInitialization.Password:
173 if (initializationStep == ConnectionInitialization.AuthInfoAndVersion)
175 initializationStep = ConnectionInitialization.Password;
178 if (initializationStep != ConnectionInitialization.Password) {
return; }
180 var passwordPacket = INetSerializableStruct.Read<ServerPeerPasswordPacket>(inc.Message);
182 if (WaitingForPassword) {
return; }
184 passwordPacket.Salt.TryUnwrap(out passwordSalt);
185 passwordPacket.RetriesLeft.TryUnwrap(out var retries);
187 if (!
string.IsNullOrWhiteSpace(AutomaticallyAttemptedPassword))
189 SendPassword(AutomaticallyAttemptedPassword);
193 LocalizedString pwMsg = TextManager.Get(
"PasswordRequired");
195 passwordMsgBox?.Close();
196 passwordMsgBox =
new GUIMessageBox(pwMsg,
"",
new LocalizedString[] { TextManager.Get(
"OK"), TextManager.Get(
"Cancel") },
197 relativeSize:
new Vector2(0.25f, 0.1f), minSize:
new Point(400, GUI.IntScale(170)));
198 var passwordHolder =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.5f), passwordMsgBox.Content.RectTransform), childAnchor:
Anchor.TopCenter);
199 var passwordBox =
new GUITextBox(
new RectTransform(
new Vector2(0.8f, 1f), passwordHolder.RectTransform) { MinSize = new Point(0, 20) })
206 var incorrectPasswordText =
new GUITextBlock(
new RectTransform(
new Vector2(1f, 0.0f), passwordHolder.RectTransform), TextManager.Get(
"incorrectpassword"), GUIStyle.Red, GUIStyle.Font, textAlignment: Alignment.Center);
207 incorrectPasswordText.RectTransform.MinSize =
new Point(0, (
int)incorrectPasswordText.TextSize.Y);
208 passwordHolder.Recalculate();
211 passwordMsgBox.Content.Recalculate();
212 passwordMsgBox.Content.RectTransform.MinSize =
new Point(0, passwordMsgBox.Content.RectTransform.Children.Sum(c => c.Rect.Height));
213 passwordMsgBox.Content.Parent.RectTransform.MinSize =
new Point(0, (
int)(passwordMsgBox.Content.RectTransform.MinSize.Y / passwordMsgBox.Content.RectTransform.RelativeSize.Y));
215 var okButton = passwordMsgBox.Buttons[0];
216 okButton.OnClicked += (_, __) =>
218 SendPassword(passwordBox.Text);
221 okButton.OnClicked += passwordMsgBox.Close;
223 var cancelButton = passwordMsgBox.Buttons[1];
224 cancelButton.OnClicked = (_, __) =>
226 Close(PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
227 passwordMsgBox?.Close(); passwordMsgBox =
null;
232 passwordBox.OnEnterPressed += (_, __) =>
234 okButton.OnClicked.Invoke(okButton, okButton.UserData);
238 passwordBox.Select();
245 public abstract void ForceTimeOut();
247 public abstract void DebugSendRawMessage(IWriteMessage msg);
ConnectionInitialization InitializationStep