Files
UnrealEngine/Engine/Plugins/Online/OnlineBase/Source/Public/Online/LANBeacon.h
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

317 lines
8.9 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Misc/ConfigCacheIni.h"
#include "Misc/Guid.h"
#define UE_API ONLINEBASE_API
/**
* Generates a random nonce (number used once) of the desired length
*
* @param Nonce the buffer that will get the randomized data
* @param Length the number of bytes to generate random values for
*/
inline void GenerateNonce(uint8* Nonce, uint32 Length)
{
//@todo joeg -- switch to CryptGenRandom() if possible or something equivalent
// Loop through generating a random value for each byte
for (uint32 NonceIndex = 0; NonceIndex < Length; NonceIndex++)
{
Nonce[NonceIndex] = (uint8)(FMath::Rand() & 255);
}
}
/**
* This value indicates which packet version the server is sending. Clients with
* differing versions will ignore these packets. This prevents crashing when
* changing the packet format and there are existing servers on the network
* Current format:
*
* <Ver byte><Platform byte><Game unique 4 bytes><packet type 2 bytes><nonce 8 bytes><guid 16 bytes><payload>
*/
enum class ELANBeaconVersionHistory : uint8
{
HISTORY_INITIAL = 11,
HISTORY_BEACON_GUID = 12, // Lan response packets now contain an instance guid
// -----<new versions can be added before this line>-------------------------------------------------
HISTORY_PLUS_ONE,
HISTORY_LATEST = HISTORY_PLUS_ONE - 1
};
static const uint8 LAN_BEACON_PACKET_VERSION = (uint8)ELANBeaconVersionHistory::HISTORY_LATEST;
/** The size of the header for validation */
#define LAN_BEACON_PACKET_HEADER_SIZE 32
// Offsets for various fields
#define LAN_BEACON_VER_OFFSET 0
#define LAN_BEACON_PLATFORM_OFFSET 1
#define LAN_BEACON_GAMEID_OFFSET 2
#define LAN_BEACON_PACKETTYPE1_OFFSET 6
#define LAN_BEACON_PACKETTYPE2_OFFSET 7
#define LAN_BEACON_NONCE_OFFSET 8
#define LAN_BEACON_GUID_OFFSET 16
// Packet types in 2 byte readable form
#define LAN_SERVER_QUERY1 (uint8)'S'
#define LAN_SERVER_QUERY2 (uint8)'Q'
#define LAN_SERVER_RESPONSE1 (uint8)'S'
#define LAN_SERVER_RESPONSE2 (uint8)'R'
class FInternetAddr;
class FNboSerializeToBuffer;
// LAN Session Delegates
DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnValidQueryPacket, uint8*, int32, uint64);
typedef FOnValidQueryPacket::FDelegate FOnValidQueryPacketDelegate;
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnValidResponsePacket, uint8*, int32);
typedef FOnValidResponsePacket::FDelegate FOnValidResponsePacketDelegate;
DECLARE_MULTICAST_DELEGATE(FOnSearchingTimeout);
typedef FOnSearchingTimeout::FDelegate FOnSearchingTimeoutDelegate;
/** Enum indicating the state the LAN beacon is in */
namespace ELanBeaconState
{
enum Type
{
/** The lan beacon is disabled */
NotUsingLanBeacon,
/** The lan beacon is responding to client requests for information */
Hosting,
/** The lan beacon is querying servers for information */
Searching
};
/** @return the stringified version of the enum passed in */
inline const TCHAR* ToString(ELanBeaconState::Type EnumVal)
{
switch (EnumVal)
{
case NotUsingLanBeacon:
{
return TEXT("NotUsingLanBeacon");
}
case Hosting:
{
return TEXT("Hosting");
}
case Searching:
{
return TEXT("Searching");
}
}
return TEXT("");
}
}
/**
* Class responsible for sending/receiving UDP broadcasts for LAN match
* discovery
*/
class FLanBeacon
{
/** Builds the broadcast address and caches it */
TSharedPtr<class FInternetAddr> BroadcastAddr;
/** The socket to listen for requests on */
class FSocket* ListenSocket;
/** The address in bound requests come in on */
TSharedPtr<class FInternetAddr> ListenAddr;
/** Temporary address when receiving packets*/
TSharedRef<class FInternetAddr> SockAddr;
public:
/** Sets the broadcast address for this object */
UE_API FLanBeacon();
/** Frees the broadcast socket */
UE_API ~FLanBeacon();
/** Return true if there is a valid ListenSocket */
UE_API bool IsListenSocketValid() const;
/**
* Initializes the socket
*
* @param Port the port to listen on
*
* @return true if both socket was created successfully, false otherwise
*/
UE_API bool Init(int32 Port);
/**
* Called to poll the socket for pending data. Any data received is placed
* in the specified packet buffer
*
* @param PacketData the buffer to get the socket's packet data
* @param BufferSize the size of the packet buffer
*
* @return the number of bytes read (0 if none or an error)
*/
UE_API int32 ReceivePacket(uint8* PacketData, int32 BufferSize);
/**
* Uses the cached broadcast address to send packet to a subnet
*
* @param Packet the packet to send
* @param Length the size of the packet to send
*/
UE_API bool BroadcastPacket(uint8* Packet, int32 Length);
};
#define LAN_ANNOUNCE_PORT 14001
#define LAN_UNIQUE_ID 9999
#define LAN_QUERY_TIMEOUT 5
#define LAN_QUERY_RETRY_TIME 1
#define LAN_PLATFORMMASK 0xffffffff
/**
* Encapsulate functionality related to LAN broadcast data
*/
class FLANSession
{
protected:
/**
* Determines if the packet header is valid or not
*
* @param Packet the packet data to check
* @param Length the size of the packet buffer
*
* @return true if the header is valid, false otherwise
*/
UE_API bool IsValidLanQueryPacket(const uint8* Packet, uint32 Length, uint64& ClientNonce);
/**
* Determines if the packet header is valid or not
*
* @param Packet the packet data to check
* @param Length the size of the packet buffer
* @param ResponseGuid the beacon guid from the response packet
*
* @return true if the header is valid, false otherwise
*/
UE_API bool IsValidLanResponsePacket(const uint8* Packet, uint32 Length, FGuid& ResponseGuid);
public:
/** Port to listen on for LAN queries/responses */
int32 LanAnnouncePort;
/** Unique id to keep UE games from seeing each others' LAN packets */
int32 LanGameUniqueId;
/** Mask containing which platforms can cross communicate */
int32 LanPacketPlatformMask;
/** The amount of time to wait before timing out a LAN query request */
float LanQueryTimeout;
/** The amount of time to wait before resending a LAN query request */
float LanQueryRetryTime;
/** LAN beacon for packet broadcast */
class FLanBeacon* LanBeacon;
/** State of the LAN beacon */
ELanBeaconState::Type LanBeaconState;
/** Used by a client to uniquely identify itself during LAN match discovery */
uint64 LanNonce;
/** The amount of time before the LAN query is considered done */
float LanQueryTimeLeft;
/** The amount of time before the LAN query retry */
float LanQueryRetryTimeLeft;
/** Cached search packet data for retries */
TArray<uint8> RetryData;
/** Unique identifier for this hosting/searching LAN session */
FGuid LanBeaconGuid;
/** Cached guids already received during current search */
TSet<FGuid> CachedResponseGuids;
FLANSession() :
LanAnnouncePort(LAN_ANNOUNCE_PORT),
LanGameUniqueId(LAN_UNIQUE_ID),
LanPacketPlatformMask(LAN_PLATFORMMASK),
LanQueryTimeout(LAN_QUERY_TIMEOUT),
LanQueryRetryTime(LAN_QUERY_RETRY_TIME),
LanBeacon(nullptr),
LanBeaconState(ELanBeaconState::NotUsingLanBeacon),
LanNonce(0),
LanQueryTimeLeft(0.0f),
LanQueryRetryTimeLeft(0.0f)
{
if (!GConfig->GetInt(TEXT("LANSession"), TEXT("LanAnnouncePort"), LanAnnouncePort, GEngineIni))
{
LanAnnouncePort = LAN_ANNOUNCE_PORT;
}
if (!GConfig->GetInt(TEXT("LANSession"), TEXT("LanGameUniqueId"), LanGameUniqueId, GEngineIni))
{
LanGameUniqueId = LAN_UNIQUE_ID;
}
}
virtual ~FLANSession()
{
StopLANSession();
}
/**
* Creates the LAN beacon for queries/advertising servers
*
* @param QueryDelegate delegate to fire when a client query is received
*/
UE_API bool Host(FOnValidQueryPacketDelegate& QueryDelegate);
/**
* Creates the LAN beacon for queries/advertising servers
*
* @param Packet packet to be search when broadcasting the search
* @param ResponseDelegate delegate to fire when a server response is received
* @param TimeoutDelegate delegate to fire if we exceed maximum search time
*
* @return true if search was started successfully, false otherwise
*/
UE_API bool Search(class FNboSerializeToBuffer& Packet, FOnValidResponsePacketDelegate& ResponseDelegate, FOnSearchingTimeoutDelegate& TimeoutDelegate);
/**
* Stops the LAN beacon from accepting broadcasts
*/
UE_API void StopLANSession();
UE_API void Tick(float DeltaTime);
/** create packet of MAX size */
UE_API void CreateHostResponsePacket(FNboSerializeToBuffer& Packet, uint64 ClientNonce);
UE_API void CreateClientQueryPacket(FNboSerializeToBuffer& Packet, uint64 ClientNonce);
/**
* Uses the cached broadcast address to send packet to a subnet
*
* @param Packet the packet to send
* @param Length the size of the packet to send
*/
UE_API bool BroadcastPacket(uint8* Packet, int32 Length);
ELanBeaconState::Type GetBeaconState() const
{
return LanBeaconState;
}
FOnValidQueryPacket OnValidQueryPacketDelegates;
FOnValidResponsePacket OnValidResponsePacketDelegates;
FOnSearchingTimeout OnSearchTimeoutDelegates;
};
#undef UE_API