1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2011-2017, The OpenClonk Team and contributors
6 * Copyright (c) 2017-2019, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18#include "C4Include.h"
19#include "C4Network2Discover.h"
20
21// *** C4Network2IODiscover
22
23//
24// Quick multicast discovery guide by Luchs:
25//
26// All engines in network mode join a multicast group (defined by C4NetDiscoveryAddress).
27//
28// Engines searching for a game ("client") send a single byte c = 3 to that multicast group. This
29// happens while on the network list on each refresh.
30//
31// Engines hosting a game (when going into the lobby) send a byte c = 4 plus their reference server
32// port to the multicast group. Additionally, they listen for the c = 3 bytes and will reply with
33// another multicast answer.
34
35struct C4Network2IODiscoverReply
36{
37 char c;
38 std::uint16_t Port;
39};
40
41void C4Network2IODiscover::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
42{
43 // discovery?
44 if (fEnabled && rPacket.getSize() == 1 && rPacket.getStatus() == 3)
45 Announce();
46}
47
48bool C4Network2IODiscover::Init(uint16_t iPort)
49{
50 // Reuse address
51 C4NetIOSimpleUDP::SetReUseAddress(true);
52 // Regular init (bind to port)
53 if (!C4NetIOSimpleUDP::Init(iPort))
54 return false;
55 // Set callback
56 C4NetIOSimpleUDP::SetCallback(this);
57 // Build broadcast address
58 DiscoveryAddr.SetAddress(addr: C4NetDiscoveryAddress);
59 DiscoveryAddr.SetPort(iPort);
60 // Initialize broadcast
61 if (!C4NetIOSimpleUDP::InitBroadcast(pBroadcastAddr: &DiscoveryAddr))
62 return false;
63 // Enable multicast loopback
64 return C4NetIOSimpleUDP::SetMCLoopback(true);
65}
66
67bool C4Network2IODiscover::Announce()
68{
69 // Announce our presence
70 C4Network2IODiscoverReply Reply = { .c: 4, .Port: iRefServerPort };
71 return Send(rPacket: C4NetIOPacket(&Reply, sizeof(Reply), false, DiscoveryAddr));
72}
73
74// *** C4Network2IODiscoverClient
75
76void C4Network2IODiscoverClient::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
77{
78 // discovery?
79 if (rPacket.getSize() == sizeof(C4Network2IODiscoverReply) && rPacket.getStatus() == 4)
80 {
81 // save discovered address
82 if (iDiscoverCount < C4NetMaxDiscover)
83 {
84 const C4Network2IODiscoverReply *pReply = reinterpret_cast<const C4Network2IODiscoverReply *>(rPacket.getData());
85 Discovers[iDiscoverCount] = rPacket.getAddr();
86 Discovers[iDiscoverCount].SetPort(pReply->Port);
87 iDiscoverCount++;
88 }
89 }
90}
91
92bool C4Network2IODiscoverClient::Init(uint16_t iPort)
93{
94 // Reuse address
95 C4NetIOSimpleUDP::SetReUseAddress(true);
96 // Bind to port
97 if (!C4NetIOSimpleUDP::Init(iPort))
98 return false;
99 // Set callback
100 C4NetIOSimpleUDP::SetCallback(this);
101 // Build broadcast address
102 DiscoveryAddr.SetAddress(addr: C4NetDiscoveryAddress);
103 DiscoveryAddr.SetPort(iPort);
104 // Initialize broadcast
105 if (!C4NetIOSimpleUDP::InitBroadcast(pBroadcastAddr: &DiscoveryAddr))
106 return false;
107 // Enable multicast loopback
108 return C4NetIOSimpleUDP::SetMCLoopback(true);
109}
110
111bool C4Network2IODiscoverClient::StartDiscovery()
112{
113 // Multicast discovery byte
114 char c = 3;
115 return Send(rPacket: C4NetIOPacket(&c, sizeof(c), false, DiscoveryAddr));
116}
117
118bool C4Network2IODiscoverClient::PopDiscover(C4NetIO::addr_t &Discover)
119{
120 // Discovers left?
121 if (!getDiscoverCount())
122 return false;
123 // Return one
124 Discover = Discovers[--iDiscoverCount];
125 return true;
126}
127