Merge 'remotes/trunk'
[0ad.git] / source / network / NetSession.cpp
blobf3a3fbc9ff211f3439895b64fe9e42c8981acbc1
1 /* Copyright (C) 2017 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"
19 #include "NetSession.h"
20 #include "NetClient.h"
21 #include "NetServer.h"
22 #include "NetMessage.h"
23 #include "NetStats.h"
24 #include "lib/external_libraries/enet.h"
25 #include "ps/CLogger.h"
26 #include "ps/Profile.h"
27 #include "scriptinterface/ScriptInterface.h"
29 const u32 NETWORK_WARNING_TIMEOUT = 2000;
31 const u32 MAXIMUM_HOST_TIMEOUT = std::numeric_limits<u32>::max();
33 static const int CHANNEL_COUNT = 1;
35 CNetClientSession::CNetClientSession(CNetClient& client) :
36 m_Client(client), m_FileTransferer(this), m_Host(NULL), m_Server(NULL), m_Stats(NULL)
40 CNetClientSession::~CNetClientSession()
42 delete m_Stats;
44 if (m_Host && m_Server)
46 // Disconnect immediately (we can't wait for acks)
47 enet_peer_disconnect_now(m_Server, NDR_SERVER_SHUTDOWN);
48 enet_host_destroy(m_Host);
50 m_Host = NULL;
51 m_Server = NULL;
55 bool CNetClientSession::Connect(const CStr& server, const u16 port, const bool isLocalClient, ENetHost* enetClient)
57 ENSURE(!m_Host);
58 ENSURE(!m_Server);
60 // Create ENet host
61 ENetHost* host;
62 if (enetClient != nullptr)
63 host = enetClient;
64 else
65 host = enet_host_create(NULL, 1, CHANNEL_COUNT, 0, 0);
67 if (!host)
68 return false;
70 // Bind to specified host
71 ENetAddress addr;
72 addr.port = port;
73 if (enet_address_set_host(&addr, server.c_str()) < 0)
74 return false;
76 // Initiate connection to server
77 ENetPeer* peer = enet_host_connect(host, &addr, CHANNEL_COUNT, 0);
78 if (!peer)
79 return false;
81 m_Host = host;
82 m_Server = peer;
84 // Prevent the local client of the host from timing out too quickly.
85 #if (ENET_VERSION >= ENET_VERSION_CREATE(1, 3, 4))
86 if (isLocalClient)
87 enet_peer_timeout(peer, 1, MAXIMUM_HOST_TIMEOUT, MAXIMUM_HOST_TIMEOUT);
88 #endif
90 m_Stats = new CNetStatsTable(m_Server);
91 if (CProfileViewer::IsInitialised())
92 g_ProfileViewer.AddRootTable(m_Stats);
94 return true;
97 void CNetClientSession::Disconnect(u32 reason)
99 ENSURE(m_Host && m_Server);
101 // TODO: ought to do reliable async disconnects, probably
102 enet_peer_disconnect_now(m_Server, reason);
103 enet_host_destroy(m_Host);
105 m_Host = NULL;
106 m_Server = NULL;
108 SAFE_DELETE(m_Stats);
111 void CNetClientSession::Poll()
113 PROFILE3("net client poll");
115 ENSURE(m_Host && m_Server);
117 m_FileTransferer.Poll();
119 ENetEvent event;
120 while (enet_host_service(m_Host, &event, 0) > 0)
122 switch (event.type)
124 case ENET_EVENT_TYPE_CONNECT:
126 ENSURE(event.peer == m_Server);
128 // Report the server address
129 char hostname[256] = "(error)";
130 enet_address_get_host_ip(&event.peer->address, hostname, ARRAY_SIZE(hostname));
131 LOGMESSAGE("Net client: Connected to %s:%u", hostname, (unsigned int)event.peer->address.port);
133 m_Client.HandleConnect();
135 break;
138 case ENET_EVENT_TYPE_DISCONNECT:
140 ENSURE(event.peer == m_Server);
142 LOGMESSAGE("Net client: Disconnected");
143 m_Client.HandleDisconnect(event.data);
144 return;
147 case ENET_EVENT_TYPE_RECEIVE:
149 CNetMessage* msg = CNetMessageFactory::CreateMessage(event.packet->data, event.packet->dataLength, m_Client.GetScriptInterface());
150 if (msg)
152 LOGMESSAGE("Net client: Received message %s of size %lu from server", msg->ToString().c_str(), (unsigned long)msg->GetSerializedLength());
154 m_Client.HandleMessage(msg);
156 delete msg;
159 enet_packet_destroy(event.packet);
161 break;
164 case ENET_EVENT_TYPE_NONE:
165 break;
171 void CNetClientSession::Flush()
173 PROFILE3("net client flush");
175 ENSURE(m_Host && m_Server);
177 enet_host_flush(m_Host);
180 bool CNetClientSession::SendMessage(const CNetMessage* message)
182 ENSURE(m_Host && m_Server);
184 return CNetHost::SendMessage(message, m_Server, "server");
187 u32 CNetClientSession::GetLastReceivedTime() const
189 if (!m_Server)
190 return 0;
192 return enet_time_get() - m_Server->lastReceiveTime;
195 u32 CNetClientSession::GetMeanRTT() const
197 if (!m_Server)
198 return 0;
200 return m_Server->roundTripTime;
205 CNetServerSession::CNetServerSession(CNetServerWorker& server, ENetPeer* peer) :
206 m_Server(server), m_FileTransferer(this), m_Peer(peer)
210 u32 CNetServerSession::GetIPAddress() const
212 return m_Peer->address.host;
215 u32 CNetServerSession::GetLastReceivedTime() const
217 if (!m_Peer)
218 return 0;
220 return enet_time_get() - m_Peer->lastReceiveTime;
223 u32 CNetServerSession::GetMeanRTT() const
225 if (!m_Peer)
226 return 0;
228 return m_Peer->roundTripTime;
231 void CNetServerSession::Disconnect(u32 reason)
233 Update((uint)NMT_CONNECTION_LOST, NULL);
235 enet_peer_disconnect(m_Peer, reason);
238 void CNetServerSession::DisconnectNow(u32 reason)
240 enet_peer_disconnect_now(m_Peer, reason);
243 bool CNetServerSession::SendMessage(const CNetMessage* message)
245 return m_Server.SendMessage(m_Peer, message);
248 bool CNetServerSession::IsLocalClient() const
250 return m_IsLocalClient;
253 void CNetServerSession::SetLocalClient(bool isLocalClient)
255 m_IsLocalClient = isLocalClient;
257 if (!isLocalClient)
258 return;
260 // Prevent the local client of the host from timing out too quickly
261 #if (ENET_VERSION >= ENET_VERSION_CREATE(1, 3, 4))
262 enet_peer_timeout(m_Peer, 0, MAXIMUM_HOST_TIMEOUT, MAXIMUM_HOST_TIMEOUT);
263 #endif