Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "NetSession.h"
21 :
22 : #include "NetClient.h"
23 : #include "NetMessage.h"
24 : #include "NetServer.h"
25 : #include "NetStats.h"
26 : #include "ps/CLogger.h"
27 : #include "ps/Profile.h"
28 :
29 : constexpr int NETCLIENT_POLL_TIMEOUT = 50;
30 :
31 : constexpr int CHANNEL_COUNT = 1;
32 :
33 0 : CNetClientSession::CNetClientSession(CNetClient& client) :
34 : m_Client(client), m_FileTransferer(this), m_Host(nullptr), m_Server(nullptr),
35 : m_Stats(nullptr), m_IncomingMessages(16), m_OutgoingMessages(16),
36 0 : m_LoopRunning(false), m_ShouldShutdown(false), m_MeanRTT(0), m_LastReceivedTime(0)
37 : {
38 0 : }
39 :
40 0 : CNetClientSession::~CNetClientSession()
41 : {
42 0 : ENSURE(!m_LoopRunning);
43 :
44 0 : delete m_Stats;
45 :
46 0 : if (m_Host && m_Server)
47 : {
48 : // Disconnect immediately (we can't wait for acks)
49 0 : enet_peer_disconnect_now(m_Server, NDR_SERVER_SHUTDOWN);
50 0 : enet_host_destroy(m_Host);
51 :
52 0 : m_Host = NULL;
53 0 : m_Server = NULL;
54 : }
55 0 : }
56 :
57 0 : bool CNetClientSession::Connect(const CStr& server, const u16 port, ENetHost* enetClient)
58 : {
59 0 : ENSURE(!m_LoopRunning);
60 0 : ENSURE(!m_Host);
61 0 : ENSURE(!m_Server);
62 :
63 : // Create ENet host
64 : ENetHost* host;
65 0 : if (enetClient != nullptr)
66 0 : host = enetClient;
67 : else
68 0 : host = enet_host_create(NULL, 1, CHANNEL_COUNT, 0, 0);
69 :
70 0 : if (!host)
71 0 : return false;
72 :
73 : // Bind to specified host
74 : ENetAddress addr;
75 0 : addr.port = port;
76 0 : if (enet_address_set_host(&addr, server.c_str()) < 0)
77 0 : return false;
78 :
79 : // Initiate connection to server
80 0 : ENetPeer* peer = enet_host_connect(host, &addr, CHANNEL_COUNT, 0);
81 0 : if (!peer)
82 0 : return false;
83 :
84 0 : m_Host = host;
85 0 : m_Server = peer;
86 :
87 0 : m_Stats = new CNetStatsTable(m_Server);
88 0 : if (CProfileViewer::IsInitialised())
89 0 : g_ProfileViewer.AddRootTable(m_Stats);
90 :
91 0 : return true;
92 : }
93 :
94 0 : void CNetClientSession::RunNetLoop(CNetClientSession* session)
95 : {
96 0 : ENSURE(!session->m_LoopRunning);
97 0 : session->m_LoopRunning = true;
98 :
99 0 : debug_SetThreadName("NetClientSession loop");
100 :
101 0 : while (!session->m_ShouldShutdown)
102 : {
103 0 : ENSURE(session->m_Host && session->m_Server);
104 :
105 0 : session->m_FileTransferer.Poll();
106 0 : session->Poll();
107 0 : session->Flush();
108 :
109 0 : session->m_LastReceivedTime = enet_time_get() - session->m_Server->lastReceiveTime;
110 0 : session->m_MeanRTT = session->m_Server->roundTripTime;
111 : }
112 :
113 0 : session->m_LoopRunning = false;
114 :
115 : // Deleting the session is handled in this thread as it might outlive the CNetClient.
116 0 : SAFE_DELETE(session);
117 0 : }
118 :
119 0 : void CNetClientSession::Shutdown()
120 : {
121 0 : m_ShouldShutdown = true;
122 0 : }
123 :
124 0 : void CNetClientSession::Poll()
125 : {
126 : ENetEvent event;
127 :
128 : // Use the timeout to make the thread wait and save CPU time.
129 0 : if (enet_host_service(m_Host, &event, NETCLIENT_POLL_TIMEOUT) <= 0)
130 0 : return;
131 :
132 0 : if (event.type == ENET_EVENT_TYPE_CONNECT)
133 : {
134 0 : ENSURE(event.peer == m_Server);
135 :
136 : // Report the server address immediately.
137 0 : char hostname[256] = "(error)";
138 0 : enet_address_get_host_ip(&event.peer->address, hostname, ARRAY_SIZE(hostname));
139 0 : LOGMESSAGE("Net client: Connected to %s:%u", hostname, (unsigned int)event.peer->address.port);
140 0 : m_Connected = true;
141 :
142 0 : m_IncomingMessages.push(event);
143 : }
144 0 : else if (event.type == ENET_EVENT_TYPE_DISCONNECT)
145 : {
146 0 : ENSURE(event.peer == m_Server);
147 :
148 : // Report immediately.
149 0 : LOGMESSAGE("Net client: Disconnected");
150 0 : m_Connected = false;
151 :
152 0 : m_IncomingMessages.push(event);
153 : }
154 0 : else if (event.type == ENET_EVENT_TYPE_RECEIVE)
155 0 : m_IncomingMessages.push(event);
156 : }
157 :
158 0 : void CNetClientSession::Flush()
159 : {
160 : ENetPacket* packet;
161 0 : while (m_OutgoingMessages.pop(packet))
162 0 : if (enet_peer_send(m_Server, CNetHost::DEFAULT_CHANNEL, packet) < 0)
163 : {
164 : // Report the error, but do so silently if we know we are disconnected.
165 0 : if (m_Connected)
166 0 : LOGERROR("NetClient: Failed to send packet to server");
167 : else
168 0 : LOGMESSAGE("NetClient: Failed to send packet to server");
169 : }
170 :
171 0 : enet_host_flush(m_Host);
172 0 : }
173 :
174 0 : void CNetClientSession::ProcessPolledMessages()
175 : {
176 : ENetEvent event;
177 0 : while(m_IncomingMessages.pop(event))
178 : {
179 0 : if (event.type == ENET_EVENT_TYPE_CONNECT)
180 0 : m_Client.HandleConnect();
181 0 : else if (event.type == ENET_EVENT_TYPE_DISCONNECT)
182 : {
183 : // This deletes the session, so we must break;
184 0 : m_Client.HandleDisconnect(event.data);
185 0 : break;
186 : }
187 0 : else if (event.type == ENET_EVENT_TYPE_RECEIVE)
188 : {
189 0 : CNetMessage* msg = CNetMessageFactory::CreateMessage(event.packet->data, event.packet->dataLength, m_Client.GetScriptInterface());
190 0 : if (msg)
191 : {
192 0 : LOGMESSAGE("Net client: Received message %s of size %lu from server", msg->ToString().c_str(), (unsigned long)msg->GetSerializedLength());
193 :
194 0 : m_Client.HandleMessage(msg);
195 : }
196 : // Thread-safe
197 0 : enet_packet_destroy(event.packet);
198 : }
199 : }
200 0 : }
201 :
202 0 : bool CNetClientSession::SendMessage(const CNetMessage* message)
203 : {
204 0 : ENSURE(m_Host && m_Server);
205 :
206 : // Thread-safe.
207 0 : ENetPacket* packet = CNetHost::CreatePacket(message);
208 0 : if (!packet)
209 0 : return false;
210 :
211 0 : if (!m_OutgoingMessages.push(packet))
212 : {
213 0 : LOGERROR("NetClient: Failed to push message on the outgoing queue.");
214 0 : return false;
215 : }
216 :
217 0 : return true;
218 : }
219 :
220 0 : u32 CNetClientSession::GetLastReceivedTime() const
221 : {
222 0 : if (!m_Server)
223 0 : return 0;
224 :
225 0 : return m_LastReceivedTime;
226 : }
227 :
228 0 : u32 CNetClientSession::GetMeanRTT() const
229 : {
230 0 : if (!m_Server)
231 0 : return 0;
232 :
233 0 : return m_MeanRTT;
234 : }
235 :
236 0 : CNetServerSession::CNetServerSession(CNetServerWorker& server, ENetPeer* peer) :
237 0 : m_Server(server), m_FileTransferer(this), m_Peer(peer), m_HostID(0), m_GUID(), m_UserName()
238 : {
239 0 : }
240 :
241 0 : u32 CNetServerSession::GetIPAddress() const
242 : {
243 0 : return m_Peer->address.host;
244 : }
245 :
246 0 : u32 CNetServerSession::GetLastReceivedTime() const
247 : {
248 0 : if (!m_Peer)
249 0 : return 0;
250 :
251 0 : return enet_time_get() - m_Peer->lastReceiveTime;
252 : }
253 :
254 0 : u32 CNetServerSession::GetMeanRTT() const
255 : {
256 0 : if (!m_Peer)
257 0 : return 0;
258 :
259 0 : return m_Peer->roundTripTime;
260 : }
261 :
262 0 : void CNetServerSession::Disconnect(NetDisconnectReason reason)
263 : {
264 0 : if (reason == NDR_UNKNOWN)
265 0 : LOGWARNING("Disconnecting client without communicating the disconnect reason!");
266 :
267 0 : Update((uint)NMT_CONNECTION_LOST, NULL);
268 :
269 0 : enet_peer_disconnect(m_Peer, static_cast<enet_uint32>(reason));
270 0 : }
271 :
272 0 : void CNetServerSession::DisconnectNow(NetDisconnectReason reason)
273 : {
274 0 : if (reason == NDR_UNKNOWN)
275 0 : LOGWARNING("Disconnecting client without communicating the disconnect reason!");
276 :
277 0 : enet_peer_disconnect_now(m_Peer, static_cast<enet_uint32>(reason));
278 0 : }
279 :
280 0 : bool CNetServerSession::SendMessage(const CNetMessage* message)
281 : {
282 0 : return m_Server.SendMessage(m_Peer, message);
283 : }
|