mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-05 05:07:48 +00:00
GEEKING
This commit is contained in:
@@ -0,0 +1,968 @@
|
||||
//// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||
//// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
//// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||
//// PARTICULAR PURPOSE.
|
||||
////
|
||||
//// Copyright (c) Microsoft Corporation. All rights reserved
|
||||
#include "pch.h"
|
||||
#include "MeshPacketManager.h"
|
||||
#include "Utils.h"
|
||||
#include "MeshManager.h"
|
||||
|
||||
using namespace Concurrency;
|
||||
|
||||
namespace Microsoft {
|
||||
namespace Xbox {
|
||||
namespace Samples {
|
||||
namespace NetworkMesh {
|
||||
|
||||
MeshPacketManager::MeshPacketManager(
|
||||
uint8 localConsoleId,
|
||||
unsigned short transportLevelPortNumber,
|
||||
MeshManager^ meshManager,
|
||||
bool dropOutOfOrderPackets ) :
|
||||
m_localConsoleId( localConsoleId ),
|
||||
m_debugTimeSincePacketReceive( 0.0f ),
|
||||
m_debugTimeSincePacketSend( 0.0f ),
|
||||
m_debugInsideWSAReceive( false ),
|
||||
m_debugInsideWSASend( false ),
|
||||
m_packetMessageId ( 0 ),
|
||||
m_previousPacketMessageId(0),
|
||||
m_dropOutOfOrderPackets(dropOutOfOrderPackets),
|
||||
m_heartbeatMessageSize(DEFAULT_HEARTBEAT_SIZE)
|
||||
{
|
||||
// Note: this library requires the NetworkConnectivityLevel to be one of the following:
|
||||
// XboxLiveAccess
|
||||
// InternetAccess
|
||||
// LocalAccess
|
||||
// Depending on what the parent application is doing, only certain connectivty levels will work.
|
||||
// This is clearly a matter for the parent application, so enforcement or lack thereof is best
|
||||
// left to the parent, rather than down here in the library.
|
||||
|
||||
m_meshManager = Platform::WeakReference(meshManager);
|
||||
m_meshPacketStatistics = ref new MeshPacketStatistics();
|
||||
|
||||
memset(m_bufferForWSARecv, 0, sizeof(WSARECV_BUFFER_SIZE));
|
||||
|
||||
WSADATA wsadata;
|
||||
int result = WSAStartup( MAKEWORD( 2, 2 ), &wsadata );
|
||||
if( result != 0 )
|
||||
{
|
||||
LogMeshPacketManagerComment( L"InitializeNetworkLayer failed with WSAError" );
|
||||
throw ref new Platform::COMException( HRESULT_FROM_WIN32(result) );
|
||||
}
|
||||
|
||||
m_localSocket = WSASocket(
|
||||
AF_INET6,
|
||||
SOCK_DGRAM,
|
||||
IPPROTO_UDP,
|
||||
NULL,
|
||||
0,
|
||||
WSA_FLAG_OVERLAPPED
|
||||
);
|
||||
|
||||
if ( m_localSocket == INVALID_SOCKET )
|
||||
{
|
||||
result = WSAGetLastError();
|
||||
LogMeshPacketManagerComment( L"Error: Failed creating a socket" );
|
||||
throw ref new Platform::COMException( HRESULT_FROM_WIN32((unsigned int)result) );
|
||||
}
|
||||
|
||||
// set sockets options for exclusive IPv6.
|
||||
int v6only = 0;
|
||||
result = setsockopt(
|
||||
m_localSocket,
|
||||
IPPROTO_IPV6,
|
||||
IPV6_V6ONLY,
|
||||
(char*) &v6only,
|
||||
sizeof( v6only )
|
||||
);
|
||||
|
||||
if ( result != 0 )
|
||||
{
|
||||
result = WSAGetLastError();
|
||||
LogMeshPacketManagerComment( L"Error: setsockopt() failed" );
|
||||
throw ref new Platform::COMException( HRESULT_FROM_WIN32(result) );
|
||||
}
|
||||
|
||||
// set sockets to non-blocking
|
||||
unsigned long nonBlockingValue = 1;
|
||||
result = ioctlsocket(m_localSocket, FIONBIO, &nonBlockingValue);
|
||||
if ( result != 0 )
|
||||
{
|
||||
result = WSAGetLastError();
|
||||
LogMeshPacketManagerComment( L"Error: ioctlsocket() failed" );
|
||||
throw ref new Platform::COMException( HRESULT_FROM_WIN32(result) );
|
||||
}
|
||||
|
||||
ZeroMemory( &m_localSockAddress, sizeof( m_localSockAddress ) );
|
||||
m_localSockAddress.sin6_family = AF_INET6;
|
||||
m_localSockAddress.sin6_port = transportLevelPortNumber;
|
||||
LogMeshPacketManagerComment( L"Binding to port " + transportLevelPortNumber.ToString() );
|
||||
|
||||
unsigned long reuse = 1;
|
||||
result = setsockopt( m_localSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse) );
|
||||
if ( result != 0 )
|
||||
{
|
||||
result = WSAGetLastError();
|
||||
LogMeshPacketManagerComment( L"Error: setsockopt(SO_REUSEADDR) failed" );
|
||||
}
|
||||
|
||||
// bind IPv6 socket.
|
||||
result = bind(
|
||||
m_localSocket,
|
||||
(SOCKADDR*) &m_localSockAddress,
|
||||
sizeof( m_localSockAddress )
|
||||
);
|
||||
if ( result != 0 )
|
||||
{
|
||||
result = WSAGetLastError();
|
||||
LogMeshPacketManagerComment( L"Error: bind() failed" );
|
||||
throw ref new Platform::COMException( HRESULT_FROM_WIN32(result) );
|
||||
}
|
||||
|
||||
LogMeshPacketManagerComment( L"Starting thread to listening for network traffic" );
|
||||
int32 threadAffinityMask = ~0x04; // Means to this thread can run all everything except core 3 (which is reserved for graphics for example).
|
||||
m_socketReceiveThread = ref new MeshThread(0, threadAffinityMask, NORMAL_PRIORITY_CLASS);
|
||||
m_socketReceiveThread->OnDoWork += ref new Windows::Foundation::EventHandler<ProcessThreadsEventArgs^>( [this]( Platform::Object^, ProcessThreadsEventArgs^ args )
|
||||
{
|
||||
SocketReceiveWorkerThreadDoWork(args);
|
||||
});
|
||||
|
||||
LogMeshPacketManagerComment( L"Starting thread to send network traffic" );
|
||||
threadAffinityMask = ~0x04; // Means to this thread can run all everything except core 3 (which is reserved for graphics for example).
|
||||
m_socketSendThread = ref new MeshThread(INFINITE, threadAffinityMask, NORMAL_PRIORITY_CLASS); // 0xFFFFFFFF == INFINITE. This thread will only wake up when the code tells it to, or upon shutdown
|
||||
m_socketSendThread->OnDoWork += ref new Windows::Foundation::EventHandler<ProcessThreadsEventArgs^>( [this]( Platform::Object^, ProcessThreadsEventArgs^ args )
|
||||
{
|
||||
SocketSendWorkerThreadDoWork(args);
|
||||
});
|
||||
|
||||
m_timerLastSendReliablePacketsUntilACK.QuadPart = 0;
|
||||
if (!QueryPerformanceFrequency(&m_timerFrequency))
|
||||
{
|
||||
THROW_HR( E_UNEXPECTED );
|
||||
}
|
||||
}
|
||||
|
||||
uint8 MeshPacketManager::GetLocalConsoleId()
|
||||
{
|
||||
return m_localConsoleId;
|
||||
}
|
||||
|
||||
void MeshPacketManager::Shutdown()
|
||||
{
|
||||
if (m_socketSendThread != nullptr)
|
||||
{
|
||||
m_socketSendThread->Shutdown();
|
||||
m_socketSendThread = nullptr;
|
||||
}
|
||||
|
||||
if (m_socketReceiveThread != nullptr)
|
||||
{
|
||||
m_socketReceiveThread->Shutdown();
|
||||
m_socketReceiveThread = nullptr;
|
||||
}
|
||||
|
||||
// close socket after the send and receive threads are shutdown
|
||||
// otherwise the threads will attempt to use an invalid socket and throw exceptions
|
||||
if (m_localSocket != INVALID_SOCKET )
|
||||
{
|
||||
shutdown( m_localSocket, SD_BOTH );
|
||||
closesocket( m_localSocket );
|
||||
m_localSocket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
void MeshPacketManager::SendHeartbeatMessageAsync(
|
||||
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
|
||||
uint8 consoleId
|
||||
)
|
||||
{
|
||||
size_t packetSize = sizeof(MeshPacketHeader) + m_heartbeatMessageSize;
|
||||
|
||||
std::shared_ptr<MESH_PACKET_INFO> packetInfo( new MESH_PACKET_INFO );
|
||||
packetInfo->association = association;
|
||||
|
||||
GetPacketWithHeader(packetSize, (uint8)MessageTypeEnum::GAME_HEARTBEAT_DATA, packetInfo->packetBuffer, false);
|
||||
QueuePacketToSend( packetInfo );
|
||||
|
||||
MeshHeartbeatStatisticsForConnection^ stats;
|
||||
|
||||
stats = m_meshPacketStatistics->GetStatForConnection(consoleId);
|
||||
stats->SetLastHeartbeatSent(Utils::GetCurrentTime());
|
||||
}
|
||||
|
||||
void MeshPacketManager::SendHelloMessage(
|
||||
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
|
||||
Platform::String^ consoleName,
|
||||
bool respondingToHello
|
||||
)
|
||||
{
|
||||
size_t consoleNameSizeInChars = consoleName->Length(); // WCHAR, one element already there so handy null terminator
|
||||
size_t consoleNameSizeInBytes = consoleNameSizeInChars * 2;
|
||||
size_t packetSize = sizeof(MeshPacketHeader) + sizeof(MeshPacketHelloMessageHeader) + consoleNameSizeInBytes;
|
||||
|
||||
std::shared_ptr<MESH_PACKET_INFO> packetInfo( new MESH_PACKET_INFO );
|
||||
packetInfo->association = association;
|
||||
|
||||
GetPacketWithHeader(packetSize, (uint8)MessageTypeEnum::GAME_HELLO_DATA, packetInfo->packetBuffer, false);
|
||||
|
||||
// Fill out a MeshPacketHelloMessageHeader struct, which appears after the MeshPacketHeader
|
||||
BYTE* meshPacketHelloMessageDataPtr = packetInfo->packetBuffer.data() + sizeof(MeshPacketHeader);
|
||||
MeshPacketHelloMessageHeader& meshPacketHelloMessageData = (MeshPacketHelloMessageHeader&)*meshPacketHelloMessageDataPtr;
|
||||
meshPacketHelloMessageData.respondingToHello = respondingToHello;
|
||||
meshPacketHelloMessageData.consoleNameLength = (uint16)(consoleNameSizeInChars);
|
||||
|
||||
// Fill out a console name string, which appears after the MeshPacketHelloMessageHeader
|
||||
BYTE* consoleNamePtr = packetInfo->packetBuffer.data() + sizeof(MeshPacketHeader) + sizeof(MeshPacketHelloMessageHeader);
|
||||
memcpy_s(consoleNamePtr, packetSize - sizeof(MeshPacketHelloMessageHeader) - sizeof(MeshPacketHeader), consoleName->Data(), consoleNameSizeInBytes);
|
||||
|
||||
QueuePacketToSend( packetInfo );
|
||||
}
|
||||
|
||||
void MeshPacketManager::SendChatMessage(
|
||||
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
|
||||
Windows::Storage::Streams::IBuffer^ buffer,
|
||||
bool sendReliable
|
||||
)
|
||||
{
|
||||
size_t packetSize = sizeof(MeshPacketHeader) + buffer->Length;
|
||||
|
||||
std::shared_ptr<MESH_PACKET_INFO> packetInfo( new MESH_PACKET_INFO );
|
||||
packetInfo->association = association;
|
||||
|
||||
GetPacketWithHeader(packetSize, (uint8)MessageTypeEnum::GAME_CHAT_DATA, packetInfo->packetBuffer, sendReliable);
|
||||
|
||||
BYTE* byteBufferPointer;
|
||||
Utils::GetBufferBytes(buffer, &byteBufferPointer);
|
||||
BYTE* bufferPacketPointer = packetInfo->packetBuffer.data() + sizeof(MeshPacketHeader);
|
||||
memcpy_s(bufferPacketPointer, packetSize - sizeof(MeshPacketHeader), byteBufferPointer, buffer->Length);
|
||||
|
||||
QueuePacketToSend( packetInfo );
|
||||
}
|
||||
|
||||
void MeshPacketManager::SendAckMessage(
|
||||
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
|
||||
uint16 messageIdToAck
|
||||
)
|
||||
{
|
||||
size_t packetSize = sizeof(MeshPacketHeader);
|
||||
std::shared_ptr<MESH_PACKET_INFO> packetInfo( new MESH_PACKET_INFO );
|
||||
packetInfo->association = association;
|
||||
|
||||
GetPacketWithHeader(packetSize, (uint8)MessageTypeEnum::GAME_ACK, packetInfo->packetBuffer, false);
|
||||
|
||||
// The ACK packet treats MeshPacketHeader's messageId as the message that's being ACK'd
|
||||
BYTE* packetBufferPtr = packetInfo->packetBuffer.data();
|
||||
MeshPacketHeader& packet = (MeshPacketHeader&)*packetBufferPtr;
|
||||
packet.messageId = messageIdToAck;
|
||||
|
||||
QueuePacketToSend( packetInfo );
|
||||
}
|
||||
|
||||
void MeshPacketManager::SendCustomMessage(
|
||||
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
|
||||
uint8 messageType,
|
||||
Windows::Storage::Streams::IBuffer^ buffer,
|
||||
bool sendReliable
|
||||
)
|
||||
{
|
||||
size_t packetSize = sizeof(MeshPacketHeader) + buffer->Length;
|
||||
|
||||
uint8 baseIndexOfGameCustomData = (uint8)MessageTypeEnum::GAME_CUSTOM_DATA; // eg. 64
|
||||
uint16 maxIndex = 256 - baseIndexOfGameCustomData;
|
||||
if( messageType >= maxIndex ) // eg. 192 = maxIndex
|
||||
{
|
||||
LogMeshPacketManagerComment( L"Can not send custom message type that is greater or equal to " + maxIndex.ToString() );
|
||||
throw ref new Platform::InvalidArgumentException();
|
||||
}
|
||||
messageType += baseIndexOfGameCustomData;
|
||||
|
||||
std::shared_ptr<MESH_PACKET_INFO> packetInfo( new MESH_PACKET_INFO );
|
||||
packetInfo->association = association;
|
||||
|
||||
GetPacketWithHeader(packetSize, messageType, packetInfo->packetBuffer, sendReliable);
|
||||
|
||||
BYTE* byteBufferPointer;
|
||||
Utils::GetBufferBytes(buffer, &byteBufferPointer);
|
||||
BYTE* bufferPacketPointer = packetInfo->packetBuffer.data() + sizeof(MeshPacketHeader);
|
||||
memcpy_s(bufferPacketPointer, packetSize - sizeof(MeshPacketHeader), byteBufferPointer, buffer->Length);
|
||||
|
||||
QueuePacketToSend( packetInfo );
|
||||
}
|
||||
|
||||
void
|
||||
MeshPacketManager::RecordMessageIfSendingReliable(
|
||||
std::shared_ptr<MESH_PACKET_INFO> packetInfo
|
||||
)
|
||||
{
|
||||
// Check if the top bit of the messageId is set
|
||||
BYTE* messageBufferPtr = packetInfo->packetBuffer.data();
|
||||
MeshPacketHeader& meshPacketHeader = reinterpret_cast<MeshPacketHeader&>(messageBufferPtr);
|
||||
uint16 sendReliableBit = 1 << 15;
|
||||
uint16 sendReliableBitSet = (meshPacketHeader.messageId & sendReliableBit);
|
||||
bool wasSendReliableBitSet = (sendReliableBitSet != 0);
|
||||
uint16 packetMessageId = meshPacketHeader.messageId & ~sendReliableBit; // remove the sendReliable bit from the message ID
|
||||
|
||||
if( wasSendReliableBitSet )
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_meshPacketsThatNeedAckLock);
|
||||
bool matchFound = false;
|
||||
for each (std::shared_ptr<MESH_PACKET_THAT_NEEDS_ACK> meshPacketThatNeedAck in m_meshPacketsThatNeedAck)
|
||||
{
|
||||
if( meshPacketThatNeedAck->messageId == packetMessageId )
|
||||
{
|
||||
matchFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !matchFound )
|
||||
{
|
||||
std::shared_ptr<MESH_PACKET_THAT_NEEDS_ACK> meshPacketThatNeedAck(new MESH_PACKET_THAT_NEEDS_ACK());
|
||||
meshPacketThatNeedAck->messageId = packetMessageId;
|
||||
meshPacketThatNeedAck->packetInfo = packetInfo;
|
||||
m_meshPacketsThatNeedAck.push_back( meshPacketThatNeedAck );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MeshPacketManager::QueuePacketToSend(
|
||||
std::shared_ptr<MESH_PACKET_INFO> packetInfo
|
||||
)
|
||||
{
|
||||
RecordMessageIfSendingReliable( packetInfo );
|
||||
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_sendLock);
|
||||
m_packetsToSend.push( packetInfo );
|
||||
}
|
||||
m_socketSendThread->WakeupThread();
|
||||
}
|
||||
|
||||
MeshPacketStatistics^ MeshPacketManager::GetMeshPacketStatistics()
|
||||
{
|
||||
return m_meshPacketStatistics;
|
||||
}
|
||||
|
||||
void MeshPacketManager::GetPacketWithHeader(
|
||||
size_t packetSize,
|
||||
uint8 messageType,
|
||||
std::vector<BYTE>& packetBuffer,
|
||||
bool sendReliable
|
||||
)
|
||||
{
|
||||
// Create a packet buffer which std::vector will clean up automatically
|
||||
packetBuffer.resize(packetSize, 0x33); // 0x33 for debug testing
|
||||
BYTE* messageBufferPtr = packetBuffer.data();
|
||||
|
||||
// Fill out MeshPacketHeader
|
||||
MeshPacketHeader& packet = (MeshPacketHeader&)*messageBufferPtr;
|
||||
packet.messageType = messageType;
|
||||
packet.consoleId = m_localConsoleId;
|
||||
|
||||
packet.messageId = IncrementPacketMessageId();
|
||||
|
||||
if( sendReliable )
|
||||
{
|
||||
// The top bit of the messageId indicates if the remote machine should send back a GAME_ACK message with this messageId
|
||||
uint16 sendReliableBit = 1 << 15;
|
||||
packet.messageId |= sendReliableBit;
|
||||
}
|
||||
packet.messageSize = (uint16)(packetSize);
|
||||
}
|
||||
|
||||
void MeshPacketManager::SocketSendWorkerThreadDoWork( Microsoft::Xbox::Samples::NetworkMesh::ProcessThreadsEventArgs^ args )
|
||||
{
|
||||
std::shared_ptr<MESH_PACKET_INFO> packetInfo;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
packetInfo = nullptr;
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_sendLock);
|
||||
if( !m_packetsToSend.empty() )
|
||||
{
|
||||
packetInfo = m_packetsToSend.front();
|
||||
m_packetsToSend.pop();
|
||||
}
|
||||
}
|
||||
|
||||
if( packetInfo == nullptr )
|
||||
{
|
||||
// Nothing to do, so ignore
|
||||
break;
|
||||
}
|
||||
|
||||
ProcessSendPacket(packetInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshPacketManager::ProcessSendPacket( std::shared_ptr<MESH_PACKET_INFO> packetInfo )
|
||||
{
|
||||
if (packetInfo->association == nullptr)
|
||||
{
|
||||
LogMeshPacketManagerComment( L"Invalid association to SendPacket" );
|
||||
return;
|
||||
}
|
||||
|
||||
if (packetInfo->packetBuffer.size() == 0)
|
||||
{
|
||||
LogMeshPacketManagerComment( L"No data for SendPacket" );
|
||||
return;
|
||||
}
|
||||
|
||||
if (INVALID_SOCKET == m_localSocket)
|
||||
{
|
||||
LogMeshPacketManagerComment( L"Can't send data if the socket has not been initialized" );
|
||||
return;
|
||||
}
|
||||
|
||||
const BYTE* messageBufferPtr = packetInfo->packetBuffer.data();
|
||||
MeshPacketHeader& meshPacketHeader = (MeshPacketHeader&)*messageBufferPtr;
|
||||
|
||||
static bool logFirstTimeOnly = true;
|
||||
if( logFirstTimeOnly )
|
||||
{
|
||||
logFirstTimeOnly = false;
|
||||
LogMeshPacketManagerComment( Utils::GetThreadDescription(L"THREAD: WSASendTo") );
|
||||
}
|
||||
|
||||
// Get the remote IPv6 socket addresses from the peerDeviceAssociation
|
||||
SOCKADDR_STORAGE remoteSocketAddress = {0};
|
||||
Platform::ArrayReference<BYTE> remoteSocketAddressBytes(
|
||||
(BYTE*) &remoteSocketAddress,
|
||||
sizeof(remoteSocketAddress)
|
||||
);
|
||||
packetInfo->association->GetRemoteSocketAddressBytes(remoteSocketAddressBytes);
|
||||
|
||||
// Collect stats on it before sending it out
|
||||
m_meshPacketStatistics->InspectPacket(meshPacketHeader, true);
|
||||
|
||||
WSABUF wsabuf;
|
||||
wsabuf.len = meshPacketHeader.messageSize;
|
||||
wsabuf.buf = (CHAR*)&meshPacketHeader;
|
||||
DWORD numBytesSent = 0;
|
||||
|
||||
SetDebugInsideWSASend(true); // for debugging purposes only
|
||||
|
||||
int result = WSASendTo(
|
||||
m_localSocket,
|
||||
&wsabuf,
|
||||
1,
|
||||
&numBytesSent,
|
||||
0,
|
||||
(SOCKADDR*) &remoteSocketAddress,
|
||||
sizeof(remoteSocketAddress),
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
|
||||
INT lastError = WSAGetLastError();
|
||||
|
||||
SetDebugInsideWSASend(false); // for debugging purposes only
|
||||
SetDebugTimeSincePacketSend( 0.0f ); // for debugging purposes only
|
||||
|
||||
if(result != 0 || numBytesSent != meshPacketHeader.messageSize)
|
||||
{
|
||||
// Ignore and log failure
|
||||
LogMeshPacketManagerComment(
|
||||
Utils::FormatString(L"WSASendTo. ErrorCode: %d. BytesSent: %d. DesiredBytesSent: %d", lastError, numBytesSent,meshPacketHeader.messageSize )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshPacketManager::SocketReceiveWorkerThreadDoWork( Microsoft::Xbox::Samples::NetworkMesh::ProcessThreadsEventArgs^ args )
|
||||
{
|
||||
// This gets called by the MeshThead class over and over again
|
||||
DWORD flags = 0;
|
||||
DWORD numberBytesReceived = 0;
|
||||
SOCKADDR_STORAGE senderSocketAddress;
|
||||
int senderSocketAddressSize = sizeof(senderSocketAddress);
|
||||
|
||||
WSABUF wsabuf;
|
||||
wsabuf.len = WSARECV_BUFFER_SIZE;
|
||||
wsabuf.buf = (char*) m_bufferForWSARecv;
|
||||
|
||||
SetDebugInsideWSAReceive(true); // for debugging purposes only
|
||||
|
||||
static bool logFirstTimeOnly = true;
|
||||
if( logFirstTimeOnly )
|
||||
{
|
||||
logFirstTimeOnly = false;
|
||||
LogMeshPacketManagerComment( Utils::GetThreadDescription(L"THREAD: WSARecvFrom") );
|
||||
}
|
||||
|
||||
int result = WSARecvFrom(
|
||||
m_localSocket,
|
||||
&wsabuf,
|
||||
1,
|
||||
&numberBytesReceived,
|
||||
&flags,
|
||||
(SOCKADDR*) &senderSocketAddress,
|
||||
&senderSocketAddressSize,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
SetDebugInsideWSAReceive(false); // for debugging purposes only
|
||||
SetDebugTimeSincePacketReceive( 0.0f );
|
||||
|
||||
if (result != 0 || numberBytesReceived == 0 )
|
||||
{
|
||||
INT lastError = WSAGetLastError();
|
||||
if (lastError != ERROR_SUCCESS)
|
||||
{
|
||||
// Ignore but log receive errors
|
||||
LogMeshPacketManagerComment(
|
||||
Utils::FormatString(L"WSARecvFrom. ErrorCode: %d. BytesReceived: %d", result, numberBytesReceived )
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MeshConnection^ meshConnection = GetMeshConnection( senderSocketAddress );
|
||||
if( meshConnection != nullptr )
|
||||
{
|
||||
if( meshConnection->GetConnectionStatus() == ConnectionStatus::Disconnected ||
|
||||
meshConnection->GetConnectionStatus() == ConnectionStatus::Pending )
|
||||
{
|
||||
LogMeshPacketManagerComment( L"ERROR: Receiving data from console who isn't connected. " + meshConnection->GetConsoleName() );
|
||||
LogMeshPacketManagerComment( L"This can happen when the OnAssociationIncoming() event fires after the first hello packet" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD offset = 0;
|
||||
while( offset < numberBytesReceived )
|
||||
{
|
||||
BYTE* packetBuffer = m_bufferForWSARecv + offset;
|
||||
MeshPacketHeader& meshPacketHeader = reinterpret_cast<MeshPacketHeader&>(*packetBuffer);
|
||||
if( offset + meshPacketHeader.messageSize > numberBytesReceived)
|
||||
{
|
||||
// Invalid packet, so skip it
|
||||
LogMeshPacketManagerComment( L"ERROR: Invalid packet sent to us" );
|
||||
break;
|
||||
}
|
||||
|
||||
ProcessPacket(meshConnection, packetBuffer);
|
||||
offset += meshPacketHeader.messageSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMeshPacketManagerComment( L"ERROR: Receiving data from console who isn't known" );
|
||||
LogMeshPacketManagerComment( L"This can happen when the OnAssociationIncoming() event fires after the first hello packet" );
|
||||
}
|
||||
}
|
||||
|
||||
MeshConnection^ MeshPacketManager::GetMeshConnection( SOCKADDR_STORAGE senderSocketAddress )
|
||||
{
|
||||
MeshManager^ meshManager = m_meshManager.Resolve<MeshManager>();
|
||||
if(meshManager == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if we already know about this connection. If the console id is not 255, use it for
|
||||
// a "fast" lookup. If console id is 255, then intentionally skip the use of console id for
|
||||
// lookups and go with the "slow" lookup based on GetAssociationBySocketAddressBytes(). 255
|
||||
// was chosen since it outside the expected range of 0...63.
|
||||
MeshPacketHeader& meshPacketHeader = reinterpret_cast<MeshPacketHeader&>(*m_bufferForWSARecv);
|
||||
if (meshPacketHeader.consoleId != 0xFF)
|
||||
{
|
||||
MeshConnection^ meshConnection = meshManager->GetConnectionFromConsoleId(meshPacketHeader.consoleId);
|
||||
if (meshConnection != nullptr)
|
||||
{
|
||||
return meshConnection;
|
||||
}
|
||||
}
|
||||
|
||||
// Do a lookup of the association based on the socket address
|
||||
Platform::ArrayReference<BYTE> localSocketAddressBytes(
|
||||
(BYTE*) &m_localSockAddress,
|
||||
sizeof(m_localSockAddress));
|
||||
|
||||
Platform::ArrayReference<BYTE> senderSocketAddressBytes(
|
||||
(BYTE*) &senderSocketAddress,
|
||||
sizeof(senderSocketAddress));
|
||||
|
||||
Windows::Xbox::Networking::SecureDeviceAddress^ secureDeviceAddress = nullptr;
|
||||
try
|
||||
{
|
||||
auto receivedSecureDeviceAssociation = Windows::Xbox::Networking::SecureDeviceAssociation::GetAssociationBySocketAddressBytes(
|
||||
senderSocketAddressBytes,
|
||||
localSocketAddressBytes);
|
||||
secureDeviceAddress = receivedSecureDeviceAssociation->RemoteSecureDeviceAddress;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LogMeshPacketManagerComment( L"Failed getting GetAssociationBySocketAddressBytes" );
|
||||
try
|
||||
{
|
||||
secureDeviceAddress = Windows::Xbox::Networking::SecureDeviceAddress::FromBytes(senderSocketAddressBytes);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LogMeshPacketManagerComment( L"ERROR: Failed getting SecureDeviceAddress::FromBytes. Ignoring WSARecvFrom packet" );
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return meshManager->GetConnectionFromSecureDeviceAddress(secureDeviceAddress);
|
||||
}
|
||||
|
||||
|
||||
UINT16 MeshPacketManager::IncrementPacketMessageId()
|
||||
{
|
||||
UINT16 result = (UINT16)InterlockedIncrement(&m_packetMessageId);
|
||||
return result & 0xffff;
|
||||
}
|
||||
|
||||
bool MeshPacketManager::ShouldPacketBeDropped( Microsoft::Xbox::Samples::NetworkMesh::MeshPacketHeader& packet )
|
||||
{
|
||||
if( !m_dropOutOfOrderPackets )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
uint16 incomingPacketMessageId = packet.messageId;
|
||||
uint16 previousPacketMessageId = GetPreviousPacketMessageId();
|
||||
|
||||
uint16 skippedPercentage = ( SKIPPED_PERCENT * 0xffff) / 100;
|
||||
// If the incoming is greater than the previous and incoming is less than Prev + 10%, then we should keep it.
|
||||
// For e.g. Max = 100; Prev = 90; Incoming = 95; then anything from 90-100, we keep.
|
||||
if (incomingPacketMessageId > previousPacketMessageId && incomingPacketMessageId < (previousPacketMessageId + skippedPercentage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the incoming is less than the previous and less than the max wrapped packet (Prev + 10% wrapped around), then we should keep it.
|
||||
// For e.g. Max = 100; Prev = 95; Incoming = 4; then maxWrapped = (95+10)%100 = 5; Anything from 0-4, we keep.
|
||||
uint16 maxPacketNumberToDropWrapped = (previousPacketMessageId + skippedPercentage) % 0xffff;
|
||||
if (maxPacketNumberToDropWrapped > 0xffff)
|
||||
{
|
||||
// This is the wrap round check
|
||||
if(incomingPacketMessageId < previousPacketMessageId && incomingPacketMessageId < maxPacketNumberToDropWrapped)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// everything else is dropped.
|
||||
return true;
|
||||
}
|
||||
|
||||
void MeshPacketManager::ProcessPacket( MeshConnection^ sender, BYTE* packetBuffer )
|
||||
{
|
||||
MeshPacketHeader& meshPacketHeader = reinterpret_cast<MeshPacketHeader&>(*packetBuffer);
|
||||
|
||||
// The top bit of the messageId indicates if the remote machine should send back a GAME_ACK message with this messageId
|
||||
uint16 sendReliableBit = 1 << 15;
|
||||
uint16 sendReliableBitSet = (meshPacketHeader.messageId & sendReliableBit);
|
||||
bool wasSendReliableBitSet = (sendReliableBitSet != 0);
|
||||
meshPacketHeader.messageId &= ~sendReliableBit; // remove the sendReliable bit from the message ID
|
||||
|
||||
if( wasSendReliableBitSet )
|
||||
{
|
||||
// If this packet had the bit set, then send back an ACK packet to this sender
|
||||
SendAckMessage(sender->GetAssociation(), meshPacketHeader.messageId);
|
||||
}
|
||||
|
||||
// MessageTypeEnum::GAME_ACK is unique because the meshPacketHeader.messageId
|
||||
// is the message of the ID packet that's being ACK'd.
|
||||
// So ignore it for the drop packet feature
|
||||
if( meshPacketHeader.messageType != (uint8)MessageTypeEnum::GAME_ACK )
|
||||
{
|
||||
if(ShouldPacketBeDropped(meshPacketHeader))
|
||||
{
|
||||
m_meshPacketStatistics->InspectPacket(meshPacketHeader, false);
|
||||
m_meshPacketStatistics->PacketDropped(meshPacketHeader, 1);
|
||||
//LogMeshPacketManagerComment("Packets dropped. Curr: " + meshPacketHeader.messageId.ToString() + L" Prev: " + GetPreviousPacketMessageId().ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
int packetsSkipped = 0;
|
||||
uint16 incomingPacketMessageId = meshPacketHeader.messageId;
|
||||
uint16 previousPacketMessageId = GetPreviousPacketMessageId();
|
||||
|
||||
if(incomingPacketMessageId > previousPacketMessageId)
|
||||
{
|
||||
packetsSkipped = (incomingPacketMessageId - previousPacketMessageId) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we haven't dropped him, that means we must have wrapped.
|
||||
// For e.g. Max = 100; Prev = 95; incoming = 4;
|
||||
packetsSkipped = (incomingPacketMessageId + 0xffff) - previousPacketMessageId - 1;
|
||||
}
|
||||
|
||||
SetPreviousPacketMessageId(incomingPacketMessageId);
|
||||
m_meshPacketStatistics->InspectPacket(meshPacketHeader, false);
|
||||
if(packetsSkipped > 0)
|
||||
{
|
||||
//LogMeshPacketManagerComment("Packets skipped " + packetsSkipped.ToString());
|
||||
m_meshPacketStatistics->PacketSkipped(meshPacketHeader, packetsSkipped);
|
||||
}
|
||||
}
|
||||
|
||||
switch(meshPacketHeader.messageType)
|
||||
{
|
||||
case MessageTypeEnum::GAME_HEARTBEAT_DATA:
|
||||
{
|
||||
// Logging done in MeshManager::OnHeartbeatReceived
|
||||
auto args = ref new MeshHeartbeatReceivedEvent(meshPacketHeader.consoleId, sender);
|
||||
OnHeartbeatReceived(this, args);
|
||||
}
|
||||
break;
|
||||
|
||||
case MessageTypeEnum::GAME_HELLO_DATA:
|
||||
{
|
||||
// Logging done in MeshManager::OnHelloReceived
|
||||
BYTE* meshPacketHelloMessageDataPtr = packetBuffer + sizeof(MeshPacketHeader);
|
||||
MeshPacketHelloMessageHeader& meshPacketHelloMessageData = (MeshPacketHelloMessageHeader&)*meshPacketHelloMessageDataPtr;
|
||||
|
||||
BYTE* consoleNamePtr = packetBuffer + sizeof(MeshPacketHeader) + sizeof(MeshPacketHelloMessageHeader);
|
||||
Platform::String^ consoleName = ref new Platform::String((WCHAR*)consoleNamePtr, meshPacketHelloMessageData.consoleNameLength);
|
||||
|
||||
auto args = ref new MeshHelloReceivedEvent(
|
||||
meshPacketHeader.consoleId,
|
||||
sender,
|
||||
consoleName,
|
||||
meshPacketHelloMessageData.respondingToHello != 0
|
||||
);
|
||||
|
||||
OnHelloReceived(this, args);
|
||||
}
|
||||
break;
|
||||
|
||||
case MessageTypeEnum::GAME_CHAT_DATA:
|
||||
{
|
||||
BYTE* srcBufferPtr = packetBuffer + sizeof(MeshPacketHeader);
|
||||
uint32 srcBufferSizeInBytes = meshPacketHeader.messageSize - sizeof(MeshPacketHeader);
|
||||
|
||||
Windows::Storage::Streams::Buffer^ destBuffer = ref new Windows::Storage::Streams::Buffer( srcBufferSizeInBytes );
|
||||
destBuffer->Length = srcBufferSizeInBytes;
|
||||
BYTE* destBufferBytes = nullptr;
|
||||
Utils::GetBufferBytes( destBuffer, &destBufferBytes );
|
||||
memcpy_s(destBufferBytes, destBuffer->Length, srcBufferPtr, srcBufferSizeInBytes);
|
||||
|
||||
auto args = ref new MeshChatMessageReceivedEvent(
|
||||
meshPacketHeader.consoleId,
|
||||
sender,
|
||||
destBuffer
|
||||
);
|
||||
|
||||
OnChatMessageReceived(this, args);
|
||||
}
|
||||
break;
|
||||
|
||||
case MessageTypeEnum::GAME_ACK:
|
||||
{
|
||||
DeleteMeshPacketWhenGotAck( meshPacketHeader.messageId );
|
||||
|
||||
auto args = ref new MeshAckReceivedEvent(
|
||||
meshPacketHeader.consoleId,
|
||||
sender,
|
||||
meshPacketHeader.messageId
|
||||
);
|
||||
|
||||
OnAckReceived(this, args);
|
||||
}
|
||||
break;
|
||||
|
||||
case MessageTypeEnum::GAME_CUSTOM_DATA:
|
||||
default:
|
||||
{
|
||||
if( meshPacketHeader.messageType < (uint8)MessageTypeEnum::GAME_CUSTOM_DATA )
|
||||
{
|
||||
// Ignore invalid packets
|
||||
LogMeshPacketManagerComment( L"Invalid packet header: " + meshPacketHeader.messageType.ToString() );
|
||||
break;
|
||||
}
|
||||
|
||||
BYTE* srcBufferPtr = packetBuffer + sizeof(MeshPacketHeader);
|
||||
uint32 srcBufferSizeInBytes = meshPacketHeader.messageSize - sizeof(MeshPacketHeader);
|
||||
|
||||
Windows::Storage::Streams::Buffer^ destBuffer = ref new Windows::Storage::Streams::Buffer( srcBufferSizeInBytes );
|
||||
destBuffer->Length = srcBufferSizeInBytes;
|
||||
BYTE* destBufferBytes = nullptr;
|
||||
Utils::GetBufferBytes( destBuffer, &destBufferBytes );
|
||||
memcpy_s(destBufferBytes, destBuffer->Length, srcBufferPtr, srcBufferSizeInBytes);
|
||||
|
||||
auto args = ref new GameCustomMessageReceivedEvent(
|
||||
meshPacketHeader.consoleId,
|
||||
sender,
|
||||
meshPacketHeader.messageType - (uint8)MessageTypeEnum::GAME_CUSTOM_DATA,
|
||||
destBuffer
|
||||
);
|
||||
|
||||
OnGameCustomMessageReceived(this, args);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MeshPacketManager::LogMeshPacketManagerComment( Platform::String^ message )
|
||||
{
|
||||
DebugMessageEventArgs^ args = ref new DebugMessageEventArgs( message, S_OK );
|
||||
OnDebugMessage(this, args);
|
||||
}
|
||||
|
||||
void MeshPacketManager::LogMeshPacketManagerCommentWithError( Platform::String^ message, HRESULT hr )
|
||||
{
|
||||
DebugMessageEventArgs^ args = ref new DebugMessageEventArgs( message + Utils::GetErrorString(hr), hr );
|
||||
OnDebugMessage(this, args);
|
||||
}
|
||||
|
||||
void MeshPacketManager::UpdateDebugTimers( float timeDelta )
|
||||
{
|
||||
SetDebugTimeSincePacketReceive( GetDebugTimeSincePacketReceive() + timeDelta );
|
||||
SetDebugTimeSincePacketSend( GetDebugTimeSincePacketSend() + timeDelta );
|
||||
}
|
||||
|
||||
float MeshPacketManager::GetDebugTimeSincePacketReceive()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
return m_debugTimeSincePacketReceive;
|
||||
}
|
||||
|
||||
void MeshPacketManager::SetDebugTimeSincePacketReceive( float val )
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
m_debugTimeSincePacketReceive = val;
|
||||
}
|
||||
|
||||
float MeshPacketManager::GetDebugTimeSincePacketSend()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
return m_debugTimeSincePacketSend;
|
||||
}
|
||||
|
||||
void MeshPacketManager::SetDebugTimeSincePacketSend( float val )
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
m_debugTimeSincePacketSend = val;
|
||||
}
|
||||
|
||||
bool MeshPacketManager::GetDebugInsideWSAReceive()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
return m_debugInsideWSAReceive;
|
||||
}
|
||||
|
||||
void MeshPacketManager::SetDebugInsideWSAReceive( bool val )
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
m_debugInsideWSAReceive = val;
|
||||
}
|
||||
|
||||
bool MeshPacketManager::GetDebugInsideWSASend()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
return m_debugInsideWSASend;
|
||||
}
|
||||
|
||||
void MeshPacketManager::SetDebugInsideWSASend( bool val )
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
m_debugInsideWSASend = val;
|
||||
}
|
||||
|
||||
uint16 MeshPacketManager::GetPreviousPacketMessageId()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
return m_previousPacketMessageId;
|
||||
}
|
||||
|
||||
void MeshPacketManager::SetPreviousPacketMessageId( uint16 val )
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
m_previousPacketMessageId = val;
|
||||
}
|
||||
|
||||
void MeshPacketManager::SetDropOutOfOrderPackets( bool val )
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
m_dropOutOfOrderPackets = val;
|
||||
}
|
||||
|
||||
void MeshPacketManager::SetHeartbeatSize(UINT size)
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
m_heartbeatMessageSize = size;
|
||||
}
|
||||
|
||||
UINT MeshPacketManager::GetHeartbeatSize()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_debugStatsLock);
|
||||
return m_heartbeatMessageSize;
|
||||
}
|
||||
|
||||
void MeshPacketManager::SendReliablePacketsUntilACK()
|
||||
{
|
||||
LARGE_INTEGER timeNow;
|
||||
if (!QueryPerformanceCounter(&timeNow))
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
LONGLONG timeDelta = m_timerLastSendReliablePacketsUntilACK.QuadPart - timeNow.QuadPart;
|
||||
LONGLONG numberOfMillisecondsSinceLast = 1000 * timeDelta / m_timerFrequency.QuadPart;
|
||||
|
||||
if( numberOfMillisecondsSinceLast > 1000 )
|
||||
{
|
||||
m_timerLastSendReliablePacketsUntilACK = timeNow;
|
||||
SendReliablePacketsToConsolesWhoHaveNotAcked();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector< std::shared_ptr<MESH_PACKET_THAT_NEEDS_ACK> > MeshPacketManager::GetMeshPacketsThatNeedAckCopy()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_meshPacketsThatNeedAckLock);
|
||||
std::vector< std::shared_ptr<MESH_PACKET_THAT_NEEDS_ACK> > meshPacketsThatNeedAckCopy( m_meshPacketsThatNeedAck );
|
||||
return meshPacketsThatNeedAckCopy;
|
||||
}
|
||||
|
||||
void MeshPacketManager::SendReliablePacketsToConsolesWhoHaveNotAcked()
|
||||
{
|
||||
std::vector< std::shared_ptr<MESH_PACKET_THAT_NEEDS_ACK> > meshPacketsThatNeedAckCopy = GetMeshPacketsThatNeedAckCopy();
|
||||
for each (std::shared_ptr<MESH_PACKET_THAT_NEEDS_ACK> meshPacketThatNeedAck in meshPacketsThatNeedAckCopy)
|
||||
{
|
||||
QueuePacketToSend( meshPacketThatNeedAck->packetInfo );
|
||||
}
|
||||
}
|
||||
|
||||
void MeshPacketManager::DeleteMeshPacketWhenGotAck(uint16 messageId)
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_meshPacketsThatNeedAckLock);
|
||||
|
||||
bool found = false;
|
||||
auto iter = m_meshPacketsThatNeedAck.begin();
|
||||
for( ; iter != m_meshPacketsThatNeedAck.end(); iter++ )
|
||||
{
|
||||
std::shared_ptr<MESH_PACKET_THAT_NEEDS_ACK> iterMeshPacketThatNeedAck = *iter;
|
||||
if (iterMeshPacketThatNeedAck->messageId == messageId)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
m_meshPacketsThatNeedAck.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void MeshPacketManager::DeleteAllPendingAckMeshPackets()
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_meshPacketsThatNeedAckLock);
|
||||
m_meshPacketsThatNeedAck.clear();
|
||||
}
|
||||
|
||||
|
||||
}}}}
|
||||
Reference in New Issue
Block a user