1 /*
2 * OpenClonk, http://www.openclonk.org
3 *
4 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5 * Copyright (c) 2010-2016, The OpenClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16 #include "C4Include.h"
17 #include "network/C4Network2Client.h"
18
19 #include "editor/C4Console.h"
20 #include "gui/C4GameLobby.h" // fullscreen network lobby
21 #include "network/C4Network2.h"
22 #include "network/C4Network2Stats.h"
23 #include "player/C4PlayerList.h"
24
25 // *** C4Network2Client
26
C4Network2Client(C4Client * pClient)27 C4Network2Client::C4Network2Client(C4Client *pClient)
28 : pClient(pClient),
29 iAddrCnt(0),
30 eStatus(NCS_Ready),
31 iLastActivity(0),
32 pMsgConn(nullptr), pDataConn(nullptr),
33 iNextConnAttempt(0),
34 pNext(nullptr), pParent(nullptr), pstatPing(nullptr)
35 {
36 }
37
~C4Network2Client()38 C4Network2Client::~C4Network2Client()
39 {
40 ClearGraphs();
41 if (pMsgConn) { pMsgConn->Close(); pMsgConn->DelRef(); } pMsgConn = nullptr;
42 if (pDataConn) { pDataConn->Close(); pDataConn->DelRef(); } pDataConn = nullptr;
43 if (pClient) pClient->UnlinkNetClient();
44 }
45
hasConn(C4Network2IOConnection * pConn)46 bool C4Network2Client::hasConn(C4Network2IOConnection *pConn)
47 {
48 return pMsgConn == pConn || pDataConn == pConn;
49 }
50
SetMsgConn(C4Network2IOConnection * pConn)51 void C4Network2Client::SetMsgConn(C4Network2IOConnection *pConn)
52 {
53 // security
54 if (pConn != pMsgConn)
55 {
56 if (pMsgConn) pMsgConn->DelRef();
57 pMsgConn = pConn;
58 pMsgConn->AddRef();
59 }
60 if (!pDataConn) SetDataConn(pConn);
61 }
62
SetDataConn(C4Network2IOConnection * pConn)63 void C4Network2Client::SetDataConn(C4Network2IOConnection *pConn)
64 {
65 // security
66 if (pConn != pDataConn)
67 {
68 if (pDataConn) pDataConn->DelRef();
69 pDataConn = pConn;
70 pDataConn->AddRef();
71 }
72 if (!pMsgConn) SetMsgConn(pConn);
73 }
74
RemoveConn(C4Network2IOConnection * pConn)75 void C4Network2Client::RemoveConn(C4Network2IOConnection *pConn)
76 {
77 if (pConn == pMsgConn)
78 { pMsgConn->DelRef(); pMsgConn = nullptr; }
79 if (pConn == pDataConn)
80 { pDataConn->DelRef(); pDataConn = nullptr; }
81 if (pMsgConn && !pDataConn) SetDataConn(pMsgConn);
82 if (!pMsgConn && pDataConn) SetMsgConn(pDataConn);
83 }
84
85
CloseConns(const char * szMsg)86 void C4Network2Client::CloseConns(const char *szMsg)
87 {
88 C4PacketConnRe Pkt(false, false, szMsg);
89 C4Network2IOConnection *pConn;
90 while ((pConn = pMsgConn))
91 {
92 // send packet, close
93 if (pConn->isOpen())
94 {
95 pConn->Send(MkC4NetIOPacket(PID_ConnRe, Pkt));
96 pConn->Close();
97 }
98 // remove
99 RemoveConn(pConn);
100 }
101 }
102
SendMsg(C4NetIOPacket rPkt) const103 bool C4Network2Client::SendMsg(C4NetIOPacket rPkt) const
104 {
105 return getMsgConn() && getMsgConn()->Send(rPkt);
106 }
107
SendData(C4NetIOPacket rPkt) const108 bool C4Network2Client::SendData(C4NetIOPacket rPkt) const
109 {
110 return getDataConn() && getDataConn()->Send(rPkt);
111 }
112
DoConnectAttempt(C4Network2IO * pIO)113 bool C4Network2Client::DoConnectAttempt(C4Network2IO *pIO)
114 {
115 // local?
116 if (isLocal()) { iNextConnAttempt = 0; return true; }
117 // msg and data connected? Nothing to do
118 if (getMsgConn() != getDataConn()) { iNextConnAttempt = time(nullptr) + 10; return true; }
119 // too early?
120 if (iNextConnAttempt && iNextConnAttempt > time(nullptr)) return true;
121 // find address to try
122 int32_t iBestAddress = -1;
123 for (int32_t i = 0; i < iAddrCnt; i++)
124 // no connection for this protocol?
125 if ((!pDataConn || Addr[i].getProtocol() != pDataConn->getProtocol()) &&
126 (!pMsgConn || Addr[i].getProtocol() != pMsgConn->getProtocol()))
127 // protocol available?
128 if (pIO->getNetIO(Addr[i].getProtocol()))
129 // new best address?
130 if (iBestAddress < 0 || AddrAttempts[i] < AddrAttempts[iBestAddress])
131 iBestAddress = i;
132 // too many attempts or nothing found?
133 if (iBestAddress < 0 || AddrAttempts[iBestAddress] > C4NetClientConnectAttempts)
134 { iNextConnAttempt = time(nullptr) + 10; return true; }
135 // save attempt
136 AddrAttempts[iBestAddress]++; iNextConnAttempt = time(nullptr) + C4NetClientConnectInterval;
137 auto addr = Addr[iBestAddress].getAddr();
138 std::set<int> interfaceIDs;
139 if (addr.IsLocal())
140 interfaceIDs = Network.Clients.GetLocal()->getInterfaceIDs();
141 else
142 interfaceIDs = {0};
143 for (auto id : interfaceIDs)
144 {
145 addr.SetScopeId(id);
146 // log
147 LogSilentF("Network: connecting client %s on %s...", getName(), addr.ToString().getData());
148 // connect
149 if (pIO->Connect(addr, Addr[iBestAddress].getProtocol(), pClient->getCore()))
150 return true;
151 }
152 return false;
153 }
154
hasAddr(const C4Network2Address & addr) const155 bool C4Network2Client::hasAddr(const C4Network2Address &addr) const
156 {
157 // Note that the host only knows its own address as 0.0.0.0, so if the real address is being added, that can't be sorted out.
158 for (int32_t i = 0; i < iAddrCnt; i++)
159 if (Addr[i] == addr)
160 return true;
161 return false;
162 }
163
ClearAddr()164 void C4Network2Client::ClearAddr()
165 {
166 iAddrCnt = 0;
167 }
168
AddAddr(const C4Network2Address & addr,bool fAnnounce)169 bool C4Network2Client::AddAddr(const C4Network2Address &addr, bool fAnnounce)
170 {
171 // checks
172 if (iAddrCnt + 1 >= C4ClientMaxAddr) return false;
173 if (hasAddr(addr)) return true;
174 // add
175 Addr[iAddrCnt] = addr; AddrAttempts[iAddrCnt] = 0;
176 iAddrCnt++;
177 // attempt to use this one
178 if (!iNextConnAttempt) iNextConnAttempt = time(nullptr);
179 // announce
180 if (fAnnounce)
181 if (!pParent->BroadcastMsgToConnClients(MkC4NetIOPacket(PID_Addr, C4PacketAddr(getID(), addr))))
182 return false;
183 // done
184 return true;
185 }
186
AddLocalAddrs(int16_t iPortTCP,int16_t iPortUDP)187 void C4Network2Client::AddLocalAddrs(int16_t iPortTCP, int16_t iPortUDP)
188 {
189 C4NetIO::addr_t addr;
190
191 for (auto& ha : C4NetIO::GetLocalAddresses())
192 {
193 addr.SetAddress(ha);
194 if (iPortTCP)
195 {
196 addr.SetPort(iPortTCP);
197 AddAddr(C4Network2Address(addr, P_TCP), false);
198 }
199 if (iPortUDP)
200 {
201 addr.SetPort(iPortUDP);
202 AddAddr(C4Network2Address(addr, P_UDP), false);
203 }
204 if (addr.GetScopeId())
205 InterfaceIDs.insert(addr.GetScopeId());
206 }
207 }
208
SendAddresses(C4Network2IOConnection * pConn)209 void C4Network2Client::SendAddresses(C4Network2IOConnection *pConn)
210 {
211 // send all addresses
212 for (int32_t i = 0; i < iAddrCnt; i++)
213 {
214 if (Addr[i].getAddr().GetScopeId() && (!pConn || pConn->getPeerAddr().GetScopeId() != Addr[i].getAddr().GetScopeId()))
215 continue;
216 C4Network2Address addr(Addr[i]);
217 addr.getAddr().SetScopeId(0);
218 C4NetIOPacket Pkt = MkC4NetIOPacket(PID_Addr, C4PacketAddr(getID(), addr));
219 if (pConn)
220 pConn->Send(Pkt);
221 else
222 pParent->BroadcastMsgToConnClients(Pkt);
223 }
224
225 }
226
CreateGraphs()227 void C4Network2Client::CreateGraphs()
228 {
229 // del prev
230 ClearGraphs();
231 // get client color
232 static const DWORD ClientDefColors[] = {0xff0000, 0x00ff00, 0xffff00, 0x7f7fff, 0xffffff, 0x00ffff, 0xff00ff, 0x7f7f7f, 0xff7f7f, 0x7fff7f, 0x0000ff};
233 int32_t iClientColorNum = sizeof(ClientDefColors)/sizeof(DWORD);
234 DWORD dwClientClr = ClientDefColors[std::max<int32_t>(getID(), 0) % iClientColorNum];
235 // create graphs
236 pstatPing = new C4TableGraph(C4TableGraph::DefaultBlockLength, Game.pNetworkStatistics ? Game.pNetworkStatistics->SecondCounter : 0);
237 pstatPing->SetColorDw(dwClientClr);
238 pstatPing->SetTitle(getName());
239 // register into stat module
240 if (Game.pNetworkStatistics) Game.pNetworkStatistics->statPings.AddGraph(pstatPing);
241 }
242
ClearGraphs()243 void C4Network2Client::ClearGraphs()
244 {
245 // del all assigned graphs
246 if (pstatPing)
247 {
248 if (Game.pNetworkStatistics) Game.pNetworkStatistics->statPings.RemoveGraph(pstatPing);
249 delete pstatPing;
250 pstatPing = nullptr;
251 }
252 }
253
254 // *** C4Network2ClientList
255
C4Network2ClientList(C4Network2IO * pIO)256 C4Network2ClientList::C4Network2ClientList(C4Network2IO *pIO)
257 : pIO(pIO), pFirst(nullptr), pLocal(nullptr)
258 {
259
260 }
261
~C4Network2ClientList()262 C4Network2ClientList::~C4Network2ClientList()
263 {
264 Clear();
265 }
266
GetClientByID(int32_t iID) const267 C4Network2Client *C4Network2ClientList::GetClientByID(int32_t iID) const
268 {
269 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->pNext)
270 if (pClient->getID() == iID)
271 return pClient;
272 return nullptr;
273 }
274
GetNextClientAfterID(int32_t iSmallerClientID) const275 C4Network2Client *C4Network2ClientList::GetNextClientAfterID(int32_t iSmallerClientID) const
276 {
277 // return client with smallest ID > iSmallerClientID
278 C4Network2Client *pBest = nullptr;
279 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->pNext)
280 if (pClient->getID() > iSmallerClientID)
281 if (!pBest || pBest->getID() > pClient->getID())
282 pBest = pClient;
283 return pBest;
284 }
285
GetClient(const char * szName) const286 C4Network2Client *C4Network2ClientList::GetClient(const char *szName) const
287 {
288 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->pNext)
289 if (SEqual(pClient->getName(), szName))
290 return pClient;
291 return nullptr;
292 }
293
GetClient(C4Network2IOConnection * pConn) const294 C4Network2Client *C4Network2ClientList::GetClient(C4Network2IOConnection *pConn) const
295 {
296 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->pNext)
297 if (pClient->hasConn(pConn))
298 return pClient;
299 return nullptr;
300 }
301
GetClient(const C4ClientCore & CCore,int32_t iMaxDiffLevel)302 C4Network2Client *C4Network2ClientList::GetClient(const C4ClientCore &CCore, int32_t iMaxDiffLevel)
303 {
304 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->pNext)
305 if (pClient->getCore().getDiffLevel(CCore) <= iMaxDiffLevel)
306 return pClient;
307 return nullptr;
308 }
309
Count()310 unsigned int C4Network2ClientList::Count()
311 {
312 unsigned int ret(0);
313 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->pNext) ret++;
314 return ret;
315 }
316
GetHost()317 C4Network2Client *C4Network2ClientList::GetHost()
318 {
319 return GetClientByID(C4ClientIDHost);
320 }
321
GetNextClient(C4Network2Client * pClient)322 C4Network2Client *C4Network2ClientList::GetNextClient(C4Network2Client *pClient)
323 {
324 return pClient ? pClient->pNext : pFirst;
325 }
326
Init(C4ClientList * pnClientList,bool fnHost)327 void C4Network2ClientList::Init(C4ClientList *pnClientList, bool fnHost)
328 {
329 // save flag
330 fHost = fnHost;
331 // initialize
332 pClientList = pnClientList;
333 pClientList->InitNetwork(this);
334 }
335
RegClient(C4Client * pClient)336 C4Network2Client *C4Network2ClientList::RegClient(C4Client *pClient)
337 {
338 // security
339 if (pClient->getNetClient())
340 return pClient->getNetClient();
341 // find insert position
342 C4Network2Client *pPos = pFirst, *pLast = nullptr;
343 for (; pPos; pLast = pPos, pPos = pPos->getNext())
344 if (pPos->getID() > pClient->getID())
345 break;
346 assert(!pLast || pLast->getID() != pClient->getID());
347 // create new client
348 C4Network2Client *pNetClient = new C4Network2Client(pClient);
349 // add to list
350 pNetClient->pNext = pPos;
351 (pLast ? pLast->pNext : pFirst) = pNetClient;
352 pNetClient->pParent = this;
353 // local?
354 if (pClient->isLocal())
355 pLocal = pNetClient;
356 else
357 // set auto-accept
358 pIO->AddAutoAccept(pClient->getCore());
359 // add
360 return pNetClient;
361 }
362
DeleteClient(C4Network2Client * pClient)363 void C4Network2ClientList::DeleteClient(C4Network2Client *pClient)
364 {
365 // close connections
366 pClient->CloseConns("removing client");
367 // remove from list
368 if (pClient == pFirst)
369 pFirst = pClient->getNext();
370 else
371 {
372 C4Network2Client *pPrev;
373 for (pPrev = pFirst; pPrev && pPrev->getNext(); pPrev = pPrev->getNext())
374 if (pPrev->getNext() == pClient)
375 break;
376 if (pPrev && pPrev->getNext() == pClient)
377 pPrev->pNext = pClient->getNext();
378 }
379 // remove auto-accept
380 pIO->RemoveAutoAccept(pClient->getCore());
381 // delete
382 delete pClient;
383 }
384
Clear()385 void C4Network2ClientList::Clear()
386 {
387 // remove link to main client list
388 if (pClientList)
389 {
390 C4ClientList *poClientList = pClientList;
391 pClientList = nullptr;
392 poClientList->ClearNetwork();
393 }
394 // delete clients
395 while (pFirst)
396 {
397 DeleteClient(pFirst);
398 }
399 pLocal = nullptr;
400 }
401
BroadcastMsgToConnClients(const C4NetIOPacket & rPkt)402 bool C4Network2ClientList::BroadcastMsgToConnClients(const C4NetIOPacket &rPkt)
403 {
404 // Send a msg to all clients that are currently directly reachable.
405
406 // lock
407 pIO->BeginBroadcast(false);
408 // select connections for broadcast
409 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext())
410 if (pClient->isConnected())
411 pClient->getMsgConn()->SetBroadcastTarget(true);
412 // broadcast
413 bool fSuccess = pIO->Broadcast(rPkt);
414 // unlock
415 pIO->EndBroadcast();
416 // finished
417 return fSuccess;
418 }
419
BroadcastMsgToClients(const C4NetIOPacket & rPkt)420 bool C4Network2ClientList::BroadcastMsgToClients(const C4NetIOPacket &rPkt)
421 {
422 // Send a msg to all clients, including clients that are not connected to
423 // this computer (will get forwarded by host).
424
425 C4PacketFwd Fwd; Fwd.SetListType(true);
426 // lock
427 pIO->BeginBroadcast(false);
428 // select connections for broadcast
429 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext())
430 if (!pClient->isHost())
431 if (pClient->isConnected())
432 {
433 pClient->getMsgConn()->SetBroadcastTarget(true);
434 Fwd.AddClient(pClient->getID());
435 }
436 // broadcast
437 bool fSuccess = pIO->Broadcast(rPkt);
438 // unlock
439 pIO->EndBroadcast();
440 // clients: send forward request to host
441 if (!fHost)
442 {
443 Fwd.SetData(rPkt);
444 fSuccess &= SendMsgToHost(MkC4NetIOPacket(PID_FwdReq, Fwd));
445 }
446 return fSuccess;
447 }
448
SendMsgToHost(C4NetIOPacket rPkt)449 bool C4Network2ClientList::SendMsgToHost(C4NetIOPacket rPkt)
450 {
451 // find host
452 C4Network2Client *pHost = GetHost();
453 if (!pHost) return false;
454 // send message
455 if (!pHost->getMsgConn()) return false;
456 return pHost->SendMsg(rPkt);
457 }
458
SendMsgToClient(int32_t iClient,C4NetIOPacket && rPkt)459 bool C4Network2ClientList::SendMsgToClient(int32_t iClient, C4NetIOPacket &&rPkt)
460 {
461 // find client
462 C4Network2Client *pClient = GetClientByID(iClient);
463 if (!pClient) return false;
464 // connected? send directly
465 if (pClient->isConnected())
466 return pClient->SendMsg(rPkt);
467 // forward
468 C4PacketFwd Fwd; Fwd.SetListType(false);
469 Fwd.AddClient(iClient);
470 Fwd.SetData(rPkt);
471 return SendMsgToHost(MkC4NetIOPacket(PID_FwdReq, Fwd));
472 }
473
HandlePacket(char cStatus,const C4PacketBase * pBasePkt,C4Network2IOConnection * pConn)474 void C4Network2ClientList::HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn)
475 {
476 // find associated client
477 C4Network2Client *pClient = GetClient(pConn);
478 if (!pClient) return;
479
480 #define GETPKT(type, name) \
481 assert(pBasePkt); const type &name = \
482 static_cast<const type &>(*pBasePkt);
483
484 switch (cStatus)
485 {
486
487 case PID_Addr: // address propagation
488 {
489 GETPKT(C4PacketAddr, rPkt)
490 // find client
491 pClient = GetClientByID(rPkt.getClientID());
492 if (pClient)
493 {
494 C4Network2Address addr = rPkt.getAddr();
495 // IP zero? Set to IP from where the packet came
496 if (addr.isIPNull())
497 {
498 addr.SetIP(pConn->getPeerAddr());
499 }
500 // add (no announce)
501 if (pClient->AddAddr(addr, true))
502 // new address? Try to connect
503 pClient->DoConnectAttempt(pIO);
504 }
505 }
506 break;
507
508 }
509
510 #undef GETPKT
511 }
512
SendAddresses(C4Network2IOConnection * pConn)513 void C4Network2ClientList::SendAddresses(C4Network2IOConnection *pConn)
514 {
515 // send all client addresses known
516 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext())
517 pClient->SendAddresses(pConn);
518 }
519
DoConnectAttempts()520 void C4Network2ClientList::DoConnectAttempts()
521 {
522 // check interval
523 time_t t; time(&t);
524 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext())
525 if (!pClient->isLocal() && !pClient->isRemoved() && pClient->getNextConnAttempt() && pClient->getNextConnAttempt() <= t)
526 // attempt connect
527 pClient->DoConnectAttempt(pIO);
528 }
529
ResetReady()530 void C4Network2ClientList::ResetReady()
531 {
532 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext())
533 if (pClient->isWaitedFor())
534 pClient->SetStatus(NCS_NotReady);
535 }
536
AllClientsReady() const537 bool C4Network2ClientList::AllClientsReady() const
538 {
539 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext())
540 if (!pClient->isLocal() && pClient->isWaitedFor() && !pClient->isReady())
541 return false;
542 return true;
543 }
544
UpdateClientActivity()545 void C4Network2ClientList::UpdateClientActivity()
546 {
547 for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext())
548 if (pClient->isActivated())
549 if (::Players.GetAtClient(pClient->getID()))
550 pClient->SetLastActivity(Game.FrameCounter);
551 }
552
553 // *** C4PacketAddr
554
CompileFunc(StdCompiler * pComp)555 void C4PacketAddr::CompileFunc(StdCompiler *pComp)
556 {
557 pComp->Value(mkNamingAdapt(mkIntPackAdapt(iClientID), "ClientID", C4ClientIDUnknown));
558 pComp->Value(mkNamingAdapt(addr, "Addr"));
559 }
560
561