Documentation for the Unreal C++ Library
Loading...
Searching...
No Matches
PVROBeacon.cpp
Go to the documentation of this file.
1// Copyright(c) 2023 PixoVR, LLC. All Rights Reserved.
2
3#include "PVROBeacon.h"
4#include "Misc/FeedbackContext.h"
5#include "UObject/CoreNet.h"
6#include "OnlineSubsystem.h"
7#include "SocketSubsystem.h"
8#include "Sockets.h"
9#if ENGINE_MAJOR_VERSION >= 5
10#include "Online/NboSerializer.h"
11#else
12#include "NboSerializer.h"
13#endif
14
17 : ListenSocket(NULL),
18 SockAddr(ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr())
19{
20}
21
24{
25 ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
26 SocketSubsystem->DestroySocket(ListenSocket);
27}
28
31{
32 return (ListenSocket ? true : false);
33}
34
35bool FPVROBeacon::Init(int32 Port)
36{
37 ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
38 bool bSuccess = false;
39 // Set our broadcast address
40 BroadcastAddr = SocketSubsystem->CreateInternetAddr();
41 BroadcastAddr->SetBroadcastAddress();
42 BroadcastAddr->SetPort(Port);
43 // Now the listen address
44 ListenAddr = SocketSubsystem->GetLocalBindAddr(*GWarn);
45 ListenAddr->SetPort(Port);
46 // A temporary "received from" address
47 SockAddr = SocketSubsystem->CreateInternetAddr();
48 // Now create and set up our sockets (no VDP)
49 ListenSocket = SocketSubsystem->CreateSocket(NAME_DGram, TEXT("LAN beacon"), true);
50 if (ListenSocket != NULL)
51 {
52 ListenSocket->SetReuseAddr();
53 ListenSocket->SetNonBlocking();
54 ListenSocket->SetRecvErr();
55 // Bind to our listen port
56 if (ListenSocket->Bind(*ListenAddr))
57 {
58 // Set it to broadcast mode, so we can send on it
59 // NOTE: You must set this to broadcast mode on Xbox 360 or the
60 // secure layer will eat any packets sent
61 bSuccess = ListenSocket->SetBroadcast();
62 }
63 else
64 {
65 UE_LOG_ONLINE(Error, TEXT("Failed to bind listen socket to addr (%s) for LAN beacon"),
66 *ListenAddr->ToString(true));
67 }
68 }
69 else
70 {
71 UE_LOG_ONLINE(Error, TEXT("Failed to create listen socket for LAN beacon"));
72 }
73 return bSuccess && ListenSocket;
74}
75
76bool FPVROBeacon::InitHost(int32 Port)
77{
78 ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
79
80 ListenAddr = SocketSubsystem->GetLocalBindAddr(*GLog);
81 ListenAddr->SetPort(Port);
82 SockAddr = SocketSubsystem->CreateInternetAddr();
83
84 bool bSuccess = false;
85 ListenSocket = SocketSubsystem->CreateSocket(NAME_DGram, TEXT("Internet beacon"), true);
86 if (ListenSocket != nullptr)
87 {
88 ListenSocket->SetReuseAddr();
89 ListenSocket->SetNonBlocking();
90 ListenSocket->SetRecvErr();
91 if (ListenSocket->Bind(*ListenAddr))
92 {
93 // Set it to broadcast mode, so we can send on it
94 // NOTE: You must set this to broadcast mode on Xbox 360 or the
95 // secure layer will eat any packets sent
96 bSuccess = ListenSocket->SetBroadcast();
97 }
98 else
99 {
101 if(SocketSubsystem->BindNextPort(ListenSocket,*ListenAddr,8000,1) != 0)
102 {
103 bSuccess = true; // ListenSocket->SetBroadcast();
104
105 TriggerOnPortChangedDelegates(ListenAddr->GetPort());
106 }
107 else
108 {
109 UE_LOG(LogTemp, Error, TEXT("Failed to bind listen socket to addr (%s) for LAN beacon"), *ListenAddr->ToString(true));
110 }
111 }
112 }
113 else
114 {
115 UE_LOG(LogTemp, Error, TEXT("Failed to create listen socket for LAN beacon"));
116 }
117
118 return bSuccess && ListenSocket;
119}
120
121bool FPVROBeacon::InitClient(int32 IP, int32 Port, int32 ListenPort)
122{
123 ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
124
125 BroadcastAddr = SocketSubsystem->CreateInternetAddr();
126 BroadcastAddr->SetIp(IP);
127 BroadcastAddr->SetPort(Port);
128
129 ListenAddr = SocketSubsystem->GetLocalBindAddr(*GLog);
130 ListenAddr->SetPort(ListenPort);
131 SockAddr = SocketSubsystem->CreateInternetAddr();
132
133 bool bSuccess = false;
134 ListenSocket = SocketSubsystem->CreateSocket(NAME_DGram, TEXT("Internet beacon"), true);
135 if (ListenSocket != NULL)
136 {
137 ListenSocket->SetReuseAddr();
138 ListenSocket->SetNonBlocking();
139 ListenSocket->SetRecvErr();
140
141 //bSuccess = true;
142 if (ListenSocket->Bind(*ListenAddr))
143 {
144 // Set it to broadcast mode, so we can send on it
145 // NOTE: You must set this to broadcast mode on Xbox 360 or the
146 // secure layer will eat any packets sent
147
148 bSuccess = ListenSocket->SetBroadcast();
149 //bSuccess = true;
150 }
151 else
152 {
154 if (SocketSubsystem->BindNextPort(ListenSocket, *ListenAddr, 8000, 1) != 0)
155 {
156 bSuccess = true; // ListenSocket->SetBroadcast();
157
158 //ToDo: Make Output in Blueprint node so user knows which ports to open (also in else)
159 TriggerOnPortChangedDelegates(ListenAddr->GetPort());
160 }
161 else
162 {
163 UE_LOG(LogTemp, Error, TEXT("Failed to bind listen socket to addr (%s) for LAN beacon"), *ListenAddr->ToString(true));
164 }
165 }
166 }
167 else
168 {
169 UE_LOG(LogTemp, Error, TEXT("Failed to create listen socket for LAN beacon"));
170 }
171
172 UE_LOG(LogTemp, Warning, TEXT("FPVROBeacon:InitClient: %d %d"), IP, Port);
173
174 return bSuccess && ListenSocket;
175}
176
177int32 FPVROBeacon::ReceivePacket(uint8* PacketData, int32 BufferSize)
178{
179 check(PacketData && BufferSize);
180 // Default to no data being read
181 int32 BytesRead = 0;
182 if (ListenSocket != NULL)
183 {
184 // Read from the socket
185 ListenSocket->RecvFrom(PacketData, BufferSize, BytesRead, *SockAddr);
186 if (BytesRead > 0)
187 {
188 UE_LOG_ONLINE(Verbose, TEXT("Received %d bytes from %s"), BytesRead, *SockAddr->ToString(true));
189 }
190 }
191
192 return BytesRead;
193}
194
195bool FPVROBeacon::BroadcastPacket(uint8* Packet, int32 Length)
196{
197 int32 BytesSent = 0;
198 UE_LOG_ONLINE(Verbose, TEXT("Sending %d bytes to %s"), Length, *BroadcastAddr->ToString(true));
199 return ListenSocket->SendTo(Packet, Length, BytesSent, *BroadcastAddr) && (BytesSent == Length);
200}
201
202bool FPVROBeacon::BroadcastPacketFromSocket(uint8* Packet, int32 Length)
203{
204 int32 BytesSent = 16;
205 return ListenSocket->SendTo(Packet, Length, BytesSent, *SockAddr) && (BytesSent == Length);
206
207}
208
209bool FPVROSession::Host(FOnValidQueryPacketDelegate& QueryDelegate, int32 Port)
210{
211 if (LanBeacon != NULL)
212 {
214 }
215
216 // Bind a socket for LAN beacon activity
217 LanBeacon = new FPVROBeacon();
218
219 // If its LAN Connection
220 bool bSuccess = false;
221 if (Port == -1)
222 {
224 {
225
226 AddOnValidQueryPacketDelegate_Handle(QueryDelegate);
227 // We successfully created everything so mark the socket as needing polling
228 LanBeaconState = ELanBeaconState::Hosting;
229 bSuccess = true;
230 UE_LOG(LogTemp, Warning, TEXT("Listening for LAN beacon requests on %d"), LanAnnouncePort);
231 }
232 else
233 {
234 UE_LOG(LogTemp, VeryVerbose, TEXT("Failed to init LAN beacon %s"), ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetSocketError());
235 }
236 }
237 else
238 {
239 if (LanBeacon->InitHost(Port))
240 {
241
242 AddOnValidQueryPacketDelegate_Handle(QueryDelegate);
243 // We successfully created everything so mark the socket as needing polling
244 LanBeaconState = ELanBeaconState::Hosting;
245 bSuccess = true;
246 }
247 else
248 {
249 UE_LOG(LogTemp, Warning, TEXT("Failed to init Online beacon %s"), ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetSocketError());
250 }
251 }
252
253 return bSuccess;
254}
255
256bool FPVROSession::Search(FNboSerializeToBuffer& Packet, FOnValidResponsePacketDelegate& ResponseDelegate, FOnSearchingTimeoutDelegate& TimeoutDelegate)
257{
258 bool bSuccess = true;
259 if (LanBeacon != NULL)
260 {
261 UE_LOG(LogTemp, Warning, TEXT("LanBeacon Search LanBeacon already exists stopping session"));
263 }
264
265 // Bind a socket for LAN beacon activity
266 LanBeacon = new FPVROBeacon();
267 if (IsLANMatch)
268 {
269 // Bind a socket for LAN beacon activity
270 if (LanBeacon->Init(LanAnnouncePort) == false)
271 {
272 UE_LOG(LogTemp, Warning, TEXT("Failed to create socket for lan announce port %s"), ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetSocketError());
273 bSuccess = false;
274 }
275 }
276 else
277 {
278 // Bind a socket for Online beacon activity
280 {
281 UE_LOG(LogTemp, VeryVerbose, TEXT("Failed to create socket for lan announce port %s"), ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetSocketError());
282 bSuccess = false;
283 }
284 }
285
286 // If we have a socket and a nonce, broadcast a discovery packet
287 if (LanBeacon && bSuccess)
288 {
289#if ENGINE_MAJOR_VERSION >= 5
290 if (LanBeacon->BroadcastPacket(Packet.GetRawBuffer(0), Packet.GetByteCount()))
291#else
292 if (LanBeacon->BroadcastPacket(Packet, Packet.GetByteCount()))
293#endif
294 {
295 UE_LOG_ONLINE(Verbose, TEXT("Sent query packet..."));
296 // We need to poll for the return packets
297 LanBeaconState = ELanBeaconState::Searching;
298 // Set the timestamp for timing out a search
300
301 AddOnValidResponsePacketDelegate_Handle(ResponseDelegate);
302 AddOnSearchingTimeoutDelegate_Handle(TimeoutDelegate);
303 }
304 else
305 {
306 UE_LOG_ONLINE(Warning, TEXT("Failed to send discovery broadcast %s"), ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetSocketError());
307 bSuccess = false;
308 }
309 }
310 else
311 {
312 UE_LOG(LogTemp, VeryVerbose, TEXT("LanBeacon Search LanBeacon not valid or bSucces false"));
313 }
314
315 return bSuccess;
316}
317
320{
321 // Don't poll anymore since we are shutting it down
322 LanBeaconState = ELanBeaconState::NotUsingLanBeacon;
323
324 // Unbind the LAN beacon object
325 if (LanBeacon)
326 {
327 delete LanBeacon;
328 LanBeacon = NULL;
329 }
330
331 // Clear delegates
332 OnValidQueryPacketDelegates.Clear();
333 OnValidResponsePacketDelegates.Clear();
334 OnSearchingTimeoutDelegates.Clear();
335}
336
337void FPVROSession::Tick(float DeltaTime)
338{
339 if (LanBeaconState == ELanBeaconState::NotUsingLanBeacon)
340 {
341 return;
342 }
343
344 uint8 PacketData[LAN_BEACON_MAX_PACKET_SIZE];
345 bool bShouldRead = true;
346 // Read each pending packet and pass it out for processing
347 while (bShouldRead)
348 {
349 int32 NumRead = LanBeacon->ReceivePacket(PacketData, LAN_BEACON_MAX_PACKET_SIZE);
350 if (NumRead > 0)
351 {
352 // Check our mode to determine the type of allowed packets
353 if (LanBeaconState == ELanBeaconState::Hosting)
354 {
355 uint64 ClientNonce;
356 // We can only accept Server Query packets
357 if (IsValidLanQueryPacket(PacketData, NumRead, ClientNonce))
358 {
359 // Strip off the header
360 TriggerOnValidQueryPacketDelegates(&PacketData[PIXOVR_BEACON_PACKET_HEADER_SIZE], NumRead - PIXOVR_BEACON_PACKET_HEADER_SIZE, ClientNonce);
361 }
362 }
363 else if (LanBeaconState == ELanBeaconState::Searching)
364 {
365 // We can only accept Server Response packets
366 if (IsValidLanResponsePacket(PacketData, NumRead))
367 {
368 // Strip off the header
369 TriggerOnValidResponsePacketDelegates(&PacketData[PIXOVR_BEACON_PACKET_HEADER_SIZE], NumRead - PIXOVR_BEACON_PACKET_HEADER_SIZE);
370 }
371 }
372 }
373 else
374 {
375 if (LanBeaconState == ELanBeaconState::Searching)
376 {
377 // Decrement the amount of time remaining
378 LanQueryTimeLeft -= DeltaTime;
379 // Check for a timeout on the search packet
380 if (LanQueryTimeLeft <= 0.f)
381 {
382 TriggerOnSearchingTimeoutDelegates();
383 }
384 }
385 bShouldRead = false;
386 }
387 }
388}
389
390void FPVROSession::CreateHostResponsePacket(FNboSerializeToBuffer& Packet, uint64 ClientNonce)
391{
392 // Add the supported version
394 // Platform information
395 << (uint8)FPlatformProperties::IsLittleEndian()
396 // Game id to prevent cross game lan packets
398 // Add the packet type
400 // Append the client nonce as a uint64
401 << ClientNonce;
402}
403
404void FPVROSession::CreateClientQueryPacket(FNboSerializeToBuffer& Packet, uint64 ClientNonce)
405{
406 // Build the discovery packet
408 // Platform information
409 << (uint8)FPlatformProperties::IsLittleEndian()
410 // Game id to prevent cross game lan packets
412 // Identify the packet type
414 // Append the nonce as a uint64
415 << ClientNonce;
416}
417
418bool FPVROSession::BroadcastPacket(uint8* Packet, int32 Length)
419{
420 bool bSuccess = false;
421 if (LanBeacon)
422 {
423 bSuccess = LanBeacon->BroadcastPacket(Packet, Length);
424 if (!bSuccess)
425 {
426 UE_LOG_ONLINE(Warning, TEXT("Failed to send broadcast packet %d"), (int32)ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLastErrorCode());
427 }
428 }
429
430 return bSuccess;
431}
432
433bool FPVROSession::BroadcastPacketFromSocket(uint8* Packet, int32 Length)
434{
435 bool bSuccess = false;
436 if (LanBeacon)
437 {
438 bSuccess = LanBeacon->BroadcastPacketFromSocket(Packet, Length);
439 if (!bSuccess)
440 {
441 UE_LOG(LogTemp, VeryVerbose, TEXT("Failed to send broadcast packet %d"), (int32)ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->GetLastErrorCode());
442 }
443 }
444
445 return bSuccess;
446}
447
448bool FPVROSession::IsValidLanQueryPacket(const uint8* Packet, uint32 Length, uint64& ClientNonce)
449{
450 ClientNonce = 0;
451 bool bIsValid = false;
452 // Serialize out the data if the packet is the right size
454 {
455 FNboSerializeFromBuffer PacketReader(Packet,Length);
456 uint8 Version = 0;
457 PacketReader >> Version;
458 // Do the versions match?
459 if (Version == PIXOVR_BEACON_PACKET_VERSION)
460 {
461 uint8 Platform = 255;
462 PacketReader >> Platform;
463 // Can we communicate with this platform?
464 if (Platform & LanPacketPlatformMask)
465 {
466 int32 GameId = -1;
467 PacketReader >> GameId;
468 // Is this our game?
469 if (GameId == LanGameUniqueId)
470 {
471 uint8 SQ1 = 0;
472 PacketReader >> SQ1;
473 uint8 SQ2 = 0;
474 PacketReader >> SQ2;
475 // Is this a server query?
476 bIsValid = (SQ1 == PIXOVR_SERVER_QUERY1 && SQ2 == PIXOVR_SERVER_QUERY2);
477 // Read the client nonce as the outvalue
478 PacketReader >> ClientNonce;
479 }
480 }
481 }
482 }
483 return bIsValid;
484}
485
486bool FPVROSession::IsValidLanResponsePacket(const uint8* Packet, uint32 Length)
487{
488 bool bIsValid = false;
489 // Serialize out the data if the packet is the right size
491 {
492 FNboSerializeFromBuffer PacketReader(Packet,Length);
493 uint8 Version = 0;
494 PacketReader >> Version;
495 // Do the versions match?
496 if (Version == PIXOVR_BEACON_PACKET_VERSION)
497 {
498 uint8 Platform = 255;
499 PacketReader >> Platform;
500 // Can we communicate with this platform?
501 if (Platform & LanPacketPlatformMask)
502 {
503 int32 GameId = -1;
504 PacketReader >> GameId;
505 // Is this our game?
506 if (GameId == LanGameUniqueId)
507 {
508 uint8 SQ1 = 0;
509 PacketReader >> SQ1;
510 uint8 SQ2 = 0;
511 PacketReader >> SQ2;
512 // Is this a server response?
514 {
515 uint64 Nonce = 0;
516 PacketReader >> Nonce;
517 bIsValid = (Nonce == LanNonce);
518 }
519 }
520 }
521 }
522 }
523 return bIsValid;
524}
#define Error(pmt,...)
#define Verbose(pmt,...)
#define PIXOVR_SERVER_QUERY2
Definition PVROBeacon.h:16
#define PIXOVR_SERVER_RESPONSE2
Definition PVROBeacon.h:18
#define PIXOVR_SERVER_RESPONSE1
Definition PVROBeacon.h:17
#define PIXOVR_BEACON_PACKET_HEADER_SIZE
Definition PVROBeacon.h:14
FOnValidResponsePacket::FDelegate FOnValidResponsePacketDelegate
Definition PVROBeacon.h:27
FOnSearchingTimeout::FDelegate FOnSearchingTimeoutDelegate
Definition PVROBeacon.h:30
FOnValidQueryPacket::FDelegate FOnValidQueryPacketDelegate
Definition PVROBeacon.h:24
#define PIXOVR_SERVER_QUERY1
Definition PVROBeacon.h:15
#define PIXOVR_BEACON_PACKET_VERSION
Definition PVROBeacon.h:13
TSharedRef< class FInternetAddr > SockAddr
Definition PVROBeacon.h:47
TSharedPtr< class FInternetAddr > BroadcastAddr
Definition PVROBeacon.h:41
bool InitClient(int32 IP, int32 Port, int32 ListenPort)
bool Init(int32 Port)
bool InitHost(int32 Port)
bool IsListenSocketValid() const
int32 ReceivePacket(uint8 *PacketData, int32 BufferSize)
bool BroadcastPacketFromSocket(uint8 *Packet, int32 Length)
class FSocket * ListenSocket
Definition PVROBeacon.h:43
bool BroadcastPacket(uint8 *Packet, int32 Length)
virtual ~FPVROBeacon()
TSharedPtr< class FInternetAddr > ListenAddr
Definition PVROBeacon.h:45
void CreateClientQueryPacket(FNboSerializeToBuffer &Packet, uint64 ClientNonce)
void CreateHostResponsePacket(FNboSerializeToBuffer &Packet, uint64 ClientNonce)
ELanBeaconState::Type LanBeaconState
Definition PVROBeacon.h:176
bool IsValidLanResponsePacket(const uint8 *Packet, uint32 Length)
bool BroadcastPacket(uint8 *Packet, int32 Length)
void Tick(float DeltaTime)
uint64 LanNonce
Definition PVROBeacon.h:179
int32 ClientSessionPort
Definition PVROBeacon.h:159
bool Search(class FNboSerializeToBuffer &Packet, FOnValidResponsePacketDelegate &ResponseDelegate, FOnSearchingTimeoutDelegate &TimeoutDelegate)
int32 LanAnnouncePort
Definition PVROBeacon.h:153
class FPVROBeacon * LanBeacon
Definition PVROBeacon.h:173
bool IsValidLanQueryPacket(const uint8 *Packet, uint32 Length, uint64 &ClientNonce)
float LanQueryTimeout
Definition PVROBeacon.h:170
int32 HostSessionPort
Definition PVROBeacon.h:157
bool Host(FOnValidQueryPacketDelegate &QueryDelegate, int32 Port=-1)
void StopLANSession()
float LanQueryTimeLeft
Definition PVROBeacon.h:182
int32 LanGameUniqueId
Definition PVROBeacon.h:164
bool BroadcastPacketFromSocket(uint8 *Packet, int32 Length)
int32 LanPacketPlatformMask
Definition PVROBeacon.h:167
int32 HostSessionAddress
Definition PVROBeacon.h:155