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"
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()
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
);
55 bool CNetClientSession::Connect(const CStr
& server
, const u16 port
, const bool isLocalClient
, ENetHost
* enetClient
)
62 if (enetClient
!= nullptr)
65 host
= enet_host_create(NULL
, 1, CHANNEL_COUNT
, 0, 0);
70 // Bind to specified host
73 if (enet_address_set_host(&addr
, server
.c_str()) < 0)
76 // Initiate connection to server
77 ENetPeer
* peer
= enet_host_connect(host
, &addr
, CHANNEL_COUNT
, 0);
84 // Prevent the local client of the host from timing out too quickly.
85 #if (ENET_VERSION >= ENET_VERSION_CREATE(1, 3, 4))
87 enet_peer_timeout(peer
, 1, MAXIMUM_HOST_TIMEOUT
, MAXIMUM_HOST_TIMEOUT
);
90 m_Stats
= new CNetStatsTable(m_Server
);
91 if (CProfileViewer::IsInitialised())
92 g_ProfileViewer
.AddRootTable(m_Stats
);
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
);
108 SAFE_DELETE(m_Stats
);
111 void CNetClientSession::Poll()
113 PROFILE3("net client poll");
115 ENSURE(m_Host
&& m_Server
);
117 m_FileTransferer
.Poll();
120 while (enet_host_service(m_Host
, &event
, 0) > 0)
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();
138 case ENET_EVENT_TYPE_DISCONNECT
:
140 ENSURE(event
.peer
== m_Server
);
142 LOGMESSAGE("Net client: Disconnected");
143 m_Client
.HandleDisconnect(event
.data
);
147 case ENET_EVENT_TYPE_RECEIVE
:
149 CNetMessage
* msg
= CNetMessageFactory::CreateMessage(event
.packet
->data
, event
.packet
->dataLength
, m_Client
.GetScriptInterface());
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
);
159 enet_packet_destroy(event
.packet
);
164 case ENET_EVENT_TYPE_NONE
:
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
192 return enet_time_get() - m_Server
->lastReceiveTime
;
195 u32
CNetClientSession::GetMeanRTT() const
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
220 return enet_time_get() - m_Peer
->lastReceiveTime
;
223 u32
CNetServerSession::GetMeanRTT() const
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
;
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
);