Update clean-workspaces.sh after SpiderMonkey's upgrade to 78.6.0 and 91.13.1.
[0ad.git] / source / network / NetSession.cpp
blob99db518d188763b6831dcaed54b4db4164e64aaf
1 /* Copyright (C) 2021 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include "NetSession.h"
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"
29 constexpr int NETCLIENT_POLL_TIMEOUT = 50;
31 constexpr int CHANNEL_COUNT = 1;
33 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 m_LoopRunning(false), m_ShouldShutdown(false), m_MeanRTT(0), m_LastReceivedTime(0)
40 CNetClientSession::~CNetClientSession()
42 ENSURE(!m_LoopRunning);
44 delete m_Stats;
46 if (m_Host && m_Server)
48 // Disconnect immediately (we can't wait for acks)
49 enet_peer_disconnect_now(m_Server, NDR_SERVER_SHUTDOWN);
50 enet_host_destroy(m_Host);
52 m_Host = NULL;
53 m_Server = NULL;
57 bool CNetClientSession::Connect(const CStr& server, const u16 port, ENetHost* enetClient)
59 ENSURE(!m_LoopRunning);
60 ENSURE(!m_Host);
61 ENSURE(!m_Server);
63 // Create ENet host
64 ENetHost* host;
65 if (enetClient != nullptr)
66 host = enetClient;
67 else
68 host = enet_host_create(NULL, 1, CHANNEL_COUNT, 0, 0);
70 if (!host)
71 return false;
73 // Bind to specified host
74 ENetAddress addr;
75 addr.port = port;
76 if (enet_address_set_host(&addr, server.c_str()) < 0)
77 return false;
79 // Initiate connection to server
80 ENetPeer* peer = enet_host_connect(host, &addr, CHANNEL_COUNT, 0);
81 if (!peer)
82 return false;
84 m_Host = host;
85 m_Server = peer;
87 m_Stats = new CNetStatsTable(m_Server);
88 if (CProfileViewer::IsInitialised())
89 g_ProfileViewer.AddRootTable(m_Stats);
91 return true;
94 void CNetClientSession::RunNetLoop(CNetClientSession* session)
96 ENSURE(!session->m_LoopRunning);
97 session->m_LoopRunning = true;
99 debug_SetThreadName("NetClientSession loop");
101 while (!session->m_ShouldShutdown)
103 ENSURE(session->m_Host && session->m_Server);
105 session->m_FileTransferer.Poll();
106 session->Poll();
107 session->Flush();
109 session->m_LastReceivedTime = enet_time_get() - session->m_Server->lastReceiveTime;
110 session->m_MeanRTT = session->m_Server->roundTripTime;
113 session->m_LoopRunning = false;
115 // Deleting the session is handled in this thread as it might outlive the CNetClient.
116 SAFE_DELETE(session);
119 void CNetClientSession::Shutdown()
121 m_ShouldShutdown = true;
124 void CNetClientSession::Poll()
126 ENetEvent event;
128 // Use the timeout to make the thread wait and save CPU time.
129 if (enet_host_service(m_Host, &event, NETCLIENT_POLL_TIMEOUT) <= 0)
130 return;
132 if (event.type == ENET_EVENT_TYPE_CONNECT)
134 ENSURE(event.peer == m_Server);
136 // Report the server address immediately.
137 char hostname[256] = "(error)";
138 enet_address_get_host_ip(&event.peer->address, hostname, ARRAY_SIZE(hostname));
139 LOGMESSAGE("Net client: Connected to %s:%u", hostname, (unsigned int)event.peer->address.port);
140 m_Connected = true;
142 m_IncomingMessages.push(event);
144 else if (event.type == ENET_EVENT_TYPE_DISCONNECT)
146 ENSURE(event.peer == m_Server);
148 // Report immediately.
149 LOGMESSAGE("Net client: Disconnected");
150 m_Connected = false;
152 m_IncomingMessages.push(event);
154 else if (event.type == ENET_EVENT_TYPE_RECEIVE)
155 m_IncomingMessages.push(event);
158 void CNetClientSession::Flush()
160 ENetPacket* packet;
161 while (m_OutgoingMessages.pop(packet))
162 if (enet_peer_send(m_Server, CNetHost::DEFAULT_CHANNEL, packet) < 0)
164 // Report the error, but do so silently if we know we are disconnected.
165 if (m_Connected)
166 LOGERROR("NetClient: Failed to send packet to server");
167 else
168 LOGMESSAGE("NetClient: Failed to send packet to server");
171 enet_host_flush(m_Host);
174 void CNetClientSession::ProcessPolledMessages()
176 ENetEvent event;
177 while(m_IncomingMessages.pop(event))
179 if (event.type == ENET_EVENT_TYPE_CONNECT)
180 m_Client.HandleConnect();
181 else if (event.type == ENET_EVENT_TYPE_DISCONNECT)
183 // This deletes the session, so we must break;
184 m_Client.HandleDisconnect(event.data);
185 break;
187 else if (event.type == ENET_EVENT_TYPE_RECEIVE)
189 CNetMessage* msg = CNetMessageFactory::CreateMessage(event.packet->data, event.packet->dataLength, m_Client.GetScriptInterface());
190 if (msg)
192 LOGMESSAGE("Net client: Received message %s of size %lu from server", msg->ToString().c_str(), (unsigned long)msg->GetSerializedLength());
194 m_Client.HandleMessage(msg);
196 // Thread-safe
197 enet_packet_destroy(event.packet);
202 bool CNetClientSession::SendMessage(const CNetMessage* message)
204 ENSURE(m_Host && m_Server);
206 // Thread-safe.
207 ENetPacket* packet = CNetHost::CreatePacket(message);
208 if (!packet)
209 return false;
211 if (!m_OutgoingMessages.push(packet))
213 LOGERROR("NetClient: Failed to push message on the outgoing queue.");
214 return false;
217 return true;
220 u32 CNetClientSession::GetLastReceivedTime() const
222 if (!m_Server)
223 return 0;
225 return m_LastReceivedTime;
228 u32 CNetClientSession::GetMeanRTT() const
230 if (!m_Server)
231 return 0;
233 return m_MeanRTT;
236 CNetServerSession::CNetServerSession(CNetServerWorker& server, ENetPeer* peer) :
237 m_Server(server), m_FileTransferer(this), m_Peer(peer), m_HostID(0), m_GUID(), m_UserName()
241 u32 CNetServerSession::GetIPAddress() const
243 return m_Peer->address.host;
246 u32 CNetServerSession::GetLastReceivedTime() const
248 if (!m_Peer)
249 return 0;
251 return enet_time_get() - m_Peer->lastReceiveTime;
254 u32 CNetServerSession::GetMeanRTT() const
256 if (!m_Peer)
257 return 0;
259 return m_Peer->roundTripTime;
262 void CNetServerSession::Disconnect(NetDisconnectReason reason)
264 if (reason == NDR_UNKNOWN)
265 LOGWARNING("Disconnecting client without communicating the disconnect reason!");
267 Update((uint)NMT_CONNECTION_LOST, NULL);
269 enet_peer_disconnect(m_Peer, static_cast<enet_uint32>(reason));
272 void CNetServerSession::DisconnectNow(NetDisconnectReason reason)
274 if (reason == NDR_UNKNOWN)
275 LOGWARNING("Disconnecting client without communicating the disconnect reason!");
277 enet_peer_disconnect_now(m_Peer, static_cast<enet_uint32>(reason));
280 bool CNetServerSession::SendMessage(const CNetMessage* message)
282 return m_Server.SendMessage(m_Peer, message);