This commit is contained in:
watrabi
2025-09-18 17:55:52 -04:00
commit 977f1ff4b8
15030 changed files with 17324420 additions and 0 deletions
@@ -0,0 +1,231 @@
#include "pch.h"
#include "MeshConnection.h"
#include "MeshManager.h"
using namespace Microsoft::Xbox::Samples::NetworkMesh;
using namespace Windows::Foundation;
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
MeshConnection::MeshConnection( Windows::Xbox::Networking::SecureDeviceAddress^ secureDeviceAddress, MeshManager^ manager ) :
m_secureDeviceAddress(secureDeviceAddress),
m_customProperty(nullptr),
m_assocationFoundInTemplate(false),
m_isInComingAssociation(false),
m_isConnectionDestroying(false),
m_isConnectionInProgress(false),
m_retryAttempts(0),
m_timerSinceLastAttempt(0.0f),
m_connectionStatus(ConnectionStatus::Disconnected),
m_consoleId(0xFF),
m_heartTimer(0.0f)
{
m_meshManager = Platform::WeakReference(manager);
m_userIdsToUserData = std::map<Platform::String^, UserMeshConnectionPropertyBag^>();
if(secureDeviceAddress == nullptr || manager == nullptr)
{
throw ref new Platform::InvalidArgumentException();
}
}
MeshConnection::~MeshConnection()
{
if (m_association != nullptr)
{
m_association->StateChanged -= m_associationStateChangeToken;
}
}
uint8 MeshConnection::GetConsoleId()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_consoleId;
}
void MeshConnection::SetConsoleId(uint8 consoleId)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_consoleId = consoleId;
}
Platform::String^ MeshConnection::GetConsoleName()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
Platform::String^ consoleName = L"n/a";
if( !m_consoleName->IsEmpty() )
{
consoleName = m_consoleName;
}
return consoleName;
}
void MeshConnection::SetConsoleName(Platform::String^ consoleName)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_consoleName = consoleName;
}
Platform::Object^ MeshConnection::GetCustomProperty()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_customProperty;
}
void MeshConnection::SetCustomProperty(Platform::Object^ object)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_customProperty = object;
}
int MeshConnection::GetNumberOfRetryAttempts()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_retryAttempts;
}
void MeshConnection::SetNumberOfRetryAttempts(int retryAttempt)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_retryAttempts = retryAttempt;
}
ConnectionStatus MeshConnection::GetConnectionStatus()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_connectionStatus;
}
void MeshConnection::SetConnectionStatus(ConnectionStatus status)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_connectionStatus = status;
}
bool MeshConnection::GetAssocationFoundInTemplate()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_assocationFoundInTemplate;
}
void MeshConnection::SetAssocationFoundInTemplate(bool val)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_assocationFoundInTemplate = val;
}
Windows::Xbox::Networking::SecureDeviceAssociation^ MeshConnection::GetAssociation()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_association;
}
void MeshConnection::SetAssociation( Windows::Xbox::Networking::SecureDeviceAssociation^ association )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
if (m_association != nullptr)
{
m_association->StateChanged -= m_associationStateChangeToken;
}
m_association = association;
if(association != nullptr)
{
// This is needed to know if the association is ever dropped.
TypedEventHandler<Windows::Xbox::Networking::SecureDeviceAssociation^, Windows::Xbox::Networking::SecureDeviceAssociationStateChangedEventArgs^>^ stateChangeEvent =
ref new TypedEventHandler<Windows::Xbox::Networking::SecureDeviceAssociation^, Windows::Xbox::Networking::SecureDeviceAssociationStateChangedEventArgs^>(
[this] (Windows::Xbox::Networking::SecureDeviceAssociation^ association, Windows::Xbox::Networking::SecureDeviceAssociationStateChangedEventArgs^ args)
{
HandleAssociationChangedEvent(association, args);
});
m_associationStateChangeToken = association->StateChanged += stateChangeEvent;
}
}
void MeshConnection::HandleAssociationChangedEvent(
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
Windows::Xbox::Networking::SecureDeviceAssociationStateChangedEventArgs^ args)
{
MeshManager^ meshManager = m_meshManager.Resolve<MeshManager>();
//if (meshManager)
meshManager->OnAssociationChange(args, association);
}
Windows::Xbox::Networking::SecureDeviceAddress^ MeshConnection::GetSecureDeviceAddress()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_secureDeviceAddress;
}
bool MeshConnection::IsInComingAssociation()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_isInComingAssociation;
}
void MeshConnection::SetInComingAssociation(bool val)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_isInComingAssociation = val;
}
bool MeshConnection::IsConnectionDestroying()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_isConnectionDestroying;
}
void MeshConnection::SetConnectionDestroying( bool val )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_isConnectionDestroying = val;
}
bool MeshConnection::IsConnectionInProgress()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_isConnectionInProgress;
}
void MeshConnection::SetConnectionInProgress( bool val )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_isConnectionInProgress = val;
}
void MeshConnection::SetHeartTimer( float timer )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_heartTimer = timer;
}
float MeshConnection::GetHeartTimer()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_heartTimer;
}
UserMeshConnectionPropertyBag^ MeshConnection::GetUserPropertyBag( Platform::String^ xboxUserId )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_userIdsToUserData[xboxUserId];
}
UserMeshConnectionPropertyBag^ MeshConnection::AddUserPropertyBag( Platform::String^ xboxUserId )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
UserMeshConnectionPropertyBag^ userMeshConnectionPropertyBag = ref new UserMeshConnectionPropertyBag(xboxUserId);
m_userIdsToUserData[xboxUserId] = userMeshConnectionPropertyBag;
return userMeshConnectionPropertyBag;
}
}}}}
@@ -0,0 +1,97 @@
#pragma once
#include "UserMeshConnectionPropertyBag.h"
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
ref class MeshManager;
public enum class ConnectionStatus
{
Disconnected,
Pending,
Connected,
PostHandshake
};
public ref class MeshConnection sealed
{
internal:
MeshConnection(Windows::Xbox::Networking::SecureDeviceAddress^ secureDeviceAddress, MeshManager^ manager);
public:
virtual ~MeshConnection();
uint8 GetConsoleId();
void SetConsoleId(uint8 consoleId);
Platform::String^ GetConsoleName();
void SetConsoleName(Platform::String^ consoleName);
Platform::Object^ GetCustomProperty();
void SetCustomProperty(Platform::Object^ object);
int GetNumberOfRetryAttempts();
void SetNumberOfRetryAttempts(int retryAttempt);
ConnectionStatus GetConnectionStatus();
void SetConnectionStatus(ConnectionStatus status);
bool GetAssocationFoundInTemplate();
void SetAssocationFoundInTemplate(bool bFound);
Windows::Xbox::Networking::SecureDeviceAssociation^ GetAssociation();
void SetAssociation(Windows::Xbox::Networking::SecureDeviceAssociation^ association);
bool IsInComingAssociation();
void SetInComingAssociation(bool val);
bool IsConnectionDestroying();
void SetConnectionDestroying(bool val);
bool IsConnectionInProgress();
void SetConnectionInProgress(bool val);
void SetHeartTimer( float timer );
float GetHeartTimer();
Windows::Xbox::Networking::SecureDeviceAddress^ MeshConnection::GetSecureDeviceAddress();
UserMeshConnectionPropertyBag^ GetUserPropertyBag(Platform::String^ xboxUserId);
UserMeshConnectionPropertyBag^ AddUserPropertyBag(Platform::String^ xboxUserId);
private:
Concurrency::critical_section m_stateLock;
Windows::Xbox::Networking::SecureDeviceAddress^ m_secureDeviceAddress;
Windows::Xbox::Networking::SecureDeviceAssociation^ m_association;
Windows::Foundation::EventRegistrationToken m_associationStateChangeToken;
Platform::WeakReference m_meshManager; // weak ref to MeshManager^
std::map<Platform::String^, UserMeshConnectionPropertyBag^> m_userIdsToUserData;
uint8 m_consoleId;
Platform::String^ m_consoleName;
Platform::Object^ m_customProperty;
ConnectionStatus m_connectionStatus;
bool m_isInComingAssociation;
bool m_isConnectionInProgress;
bool m_isConnectionDestroying;
bool m_assocationFoundInTemplate;
int m_retryAttempts;
float m_timerSinceLastAttempt;
float m_heartTimer;
void HandleAssociationChangedEvent(
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
Windows::Xbox::Networking::SecureDeviceAssociationStateChangedEventArgs^ args
);
};
}}}}
@@ -0,0 +1,207 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#pragma once
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
//
// Represents the base message that comes across
//
public ref class MeshHeartbeatReceivedEvent sealed
{
public:
property MeshConnection^ Sender { MeshConnection^ get() { return m_sender; } }
// Mesh unique identifier for the console
property uint8 ConsoleId { uint8 get() { return m_consoleId; } }
internal:
MeshHeartbeatReceivedEvent(
uint8 consoleId,
MeshConnection^ sender
) :
m_consoleId(consoleId),
m_sender(sender) {}
private:
uint8 m_consoleId;
MeshConnection^ m_sender;
};
//
// Received when a console connects
//
public ref class MeshHelloReceivedEvent sealed
{
public:
property MeshConnection^ Sender { MeshConnection^ get() { return m_sender; } }
// Mesh unique identifier for the console
property uint8 ConsoleId { uint8 get() { return m_consoleId; } }
// The console's debug name
property Platform::String^ ConsoleName { Platform::String^ get() { return m_consoleName; } }
property bool RespondingToHello { bool get() { return m_respondingToHello; } }
internal:
MeshHelloReceivedEvent(
uint8 consoleId,
MeshConnection^ sender,
Platform::String^ consoleName,
bool respondingToHello ) :
m_consoleId(consoleId),
m_sender(sender),
m_consoleName(consoleName),
m_respondingToHello(respondingToHello)
{}
private:
uint8 m_consoleId;
Platform::String^ m_consoleName;
MeshConnection^ m_sender;
bool m_respondingToHello;
};
//
// Received when a console connects
//
public ref class MeshAckReceivedEvent sealed
{
public:
property MeshConnection^ Sender { MeshConnection^ get() { return m_sender; } }
// Mesh unique identifier for the console
property uint8 ConsoleId { uint8 get() { return m_consoleId; } }
// The ID of the packet which was ACK'd
property uint16 MessageId { uint16 get() { return m_messageId; } }
internal:
MeshAckReceivedEvent(
uint8 consoleId,
MeshConnection^ sender,
uint16 messageId
) :
m_consoleId(consoleId),
m_sender(sender),
m_messageId(messageId)
{
}
private:
uint8 m_consoleId;
uint16 m_messageId;
MeshConnection^ m_sender;
};
//
// Received when a audio chat packet has been received
//
public ref class MeshChatMessageReceivedEvent sealed
{
public:
property MeshConnection^ Sender { MeshConnection^ get() { return m_sender; } }
// Mesh unique identifier for the console
property uint8 ConsoleId { uint8 get() { return m_consoleId; } }
// Buffer containing chat voice data
property Windows::Storage::Streams::IBuffer^ Buffer { Windows::Storage::Streams::IBuffer^ get() { return m_buffer; } }
internal:
MeshChatMessageReceivedEvent(
uint8 consoleId,
MeshConnection^ sender,
Windows::Storage::Streams::IBuffer^ buffer
) :
m_consoleId(consoleId),
m_sender(sender),
m_buffer(buffer)
{
}
private:
uint8 m_consoleId;
Windows::Storage::Streams::IBuffer^ m_buffer;
MeshConnection^ m_sender;
};
//
// Received when a game custom data packet has been received
//
public ref class GameCustomMessageReceivedEvent sealed
{
public:
property MeshConnection^ Sender { MeshConnection^ get() { return m_sender; } }
// Mesh unique identifier for the console
property uint8 MessageType { uint8 get() { return m_messageType; } }
// Mesh unique identifier for the console
property uint8 ConsoleId { uint8 get() { return m_consoleId; } }
// Buffer containing chat voice data
property Windows::Storage::Streams::IBuffer^ Buffer { Windows::Storage::Streams::IBuffer^ get() { return m_buffer; } }
internal:
GameCustomMessageReceivedEvent(
uint8 consoleId,
MeshConnection^ sender,
uint8 messageType,
Windows::Storage::Streams::IBuffer^ buffer
) :
m_consoleId(consoleId),
m_sender(sender),
m_messageType(messageType),
m_buffer(buffer)
{
}
private:
uint8 m_messageType;
uint8 m_consoleId;
Windows::Storage::Streams::IBuffer^ m_buffer;
MeshConnection^ m_sender;
};
//
// Event for the Mesh Controller to report diagnostic and error message information
//
public ref class DebugMessageEventArgs sealed
{
public:
property Platform::String^ Message { Platform::String^ get() { return m_message; } }
property int HResult { int get() { return m_hresult; } }
internal:
DebugMessageEventArgs(Platform::String^ message, int hr)
{
m_message = message;
m_hresult = hr;
}
private:
Platform::String^ m_message;
int m_hresult;
};
}}}}
@@ -0,0 +1,765 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#include "pch.h"
#include "MeshManager.h"
#include "Utils.h"
using namespace Concurrency;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Xbox::Networking;
using namespace Microsoft::Xbox::Samples::NetworkMesh;
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
MeshManager::MeshManager(
uint8 localConsoleId,
Platform::String^ secureDeviceAssociationTemplateName,
Platform::String^ localConsoleName,
bool dropOutOfOrderPackets ) :
m_localConsoleName(localConsoleName),
m_dropOutOfOrderPackets(dropOutOfOrderPackets)
{
// load template for Secure Device Association.
m_associationTemplate = Windows::Xbox::Networking::SecureDeviceAssociationTemplate::GetTemplateByName( secureDeviceAssociationTemplateName );
Initialize(localConsoleId);
RegisterMeshPacketEventHandlers();
}
void MeshManager::Initialize(uint8 localConsoleId)
{
m_connections.clear();
if(m_associationTemplate != nullptr)
{
DestroyAllTemplateAssociations();
// Listen to AssociationIncoming event
TypedEventHandler<Windows::Xbox::Networking::SecureDeviceAssociationTemplate^, Windows::Xbox::Networking::SecureDeviceAssociationIncomingEventArgs^>^ associationIncomingEvent =
ref new TypedEventHandler<Windows::Xbox::Networking::SecureDeviceAssociationTemplate^, Windows::Xbox::Networking::SecureDeviceAssociationIncomingEventArgs^>(
[this] (Windows::Xbox::Networking::SecureDeviceAssociationTemplate^ associationTemplate, Windows::Xbox::Networking::SecureDeviceAssociationIncomingEventArgs^ args)
{
OnAssociationIncoming( associationTemplate, args );
});
m_associationIncomingToken = m_associationTemplate->AssociationIncoming += associationIncomingEvent;
}
else
{
LogComment("Association template is NULL!");
return;
}
unsigned short sin6_port = htons(m_associationTemplate->AcceptorSocketDescription->BoundPortRangeLower);
m_meshPacketManager = ref new MeshPacketManager(localConsoleId, sin6_port, this, m_dropOutOfOrderPackets);
// Try to connect to anything we need to every second
int32 threadAffinityMask = ~0x04; // Means to this thread can run all everything except core 3 (which is reserved for graphics for example).
m_autoConnectThread = ref new MeshThread(1000, threadAffinityMask, NORMAL_PRIORITY_CLASS);
m_autoConnectThread->OnDoWork += ref new Windows::Foundation::EventHandler<ProcessThreadsEventArgs^>( [this]( Platform::Object^, ProcessThreadsEventArgs^ args )
{
OnAutoConnectWorkerThreadDoWork(args);
});
// Send heartbeats and hellos every 2 seconds
threadAffinityMask = ~0x04; // Means to this thread can run all everything except core 3 (which is reserved for graphics for example).
m_heartbeatThread = ref new MeshThread(DEFAULT_HEARTBEAT_PERIOD_MILLISECONDS, threadAffinityMask, NORMAL_PRIORITY_CLASS);
m_heartbeatThread->OnDoWork += ref new Windows::Foundation::EventHandler<ProcessThreadsEventArgs^>( [this]( Platform::Object^, ProcessThreadsEventArgs^ args )
{
OnHeartbeatWorkerThreadDoWork(args);
});
}
SecureDeviceAssociationTemplate^ MeshManager::GetSecureDeviceAssociationTemplate()
{
return m_associationTemplate;
}
// This should only be called from ConnectTo or OnIncomingAssociation.
MeshConnection^ MeshManager::AddConnection(
Windows::Xbox::Networking::SecureDeviceAddress^ secureDeviceAddress,
Windows::Xbox::Networking::SecureDeviceAssociation^ secureDeviceAssociation,
bool inComingAssociation,
ConnectionStatus connectionStatus
)
{
if(secureDeviceAddress == nullptr)
{
return nullptr;
}
if(GetConnectionFromSecureDeviceAddress(secureDeviceAddress) != nullptr)
{
// The secure device address already exists.
return nullptr;
}
MeshConnection^ meshConnection = ref new MeshConnection(secureDeviceAddress, this);
meshConnection->SetInComingAssociation(inComingAssociation);
meshConnection->SetConnectionStatus(connectionStatus);
meshConnection->SetAssociation(secureDeviceAssociation);
// Add him to the list of mesh connections.
{
Concurrency::critical_section::scoped_lock lock(m_connectionsLock);
m_connections.push_back(meshConnection);
}
return meshConnection;
}
void MeshManager::ConnectToAddress(
Windows::Xbox::Networking::SecureDeviceAddress^ secureDeviceAddress,
Platform::String^ debugName
)
{
if (secureDeviceAddress == nullptr)
{
LogComment(L"Cannot pass a nullptr for the address to MeshManager::ConnectTo");
throw ref new InvalidArgumentException(L"Cannot pass a nullptr for the address to MeshManager::ConnectTo");
}
if(AreSecureDeviceAddressesEqual(SecureDeviceAddress::GetLocal(), secureDeviceAddress))
{
// Don't try to connect to local console.
return;
}
LogComment( "ConnectToAddress: Attempting to connect to remote console: " + debugName);
MeshConnection^ newMeshConnected = AddConnection(secureDeviceAddress, nullptr, false, ConnectionStatus::Disconnected);
if( newMeshConnected != nullptr )
{
// If this connection was new, then set the console name to be debug name. It will change once the handshake is done
newMeshConnected->SetConsoleName(debugName);
}
}
void MeshManager::OnAssociationIncoming(
Windows::Xbox::Networking::SecureDeviceAssociationTemplate^ associationTemplate,
Windows::Xbox::Networking::SecureDeviceAssociationIncomingEventArgs^ args
)
{
SecureDeviceAssociation^ association = args->Association;
if(association != nullptr)
{
LogCommentFormat( L"OnAssociationIncoming: Incoming connection %s", Utils::PrintSecureDeviceAssociation(association, false, true)->Data() );
MeshConnection^ newMeshConnected = AddConnection(association->RemoteSecureDeviceAddress, association, true, ConnectionStatus::Connected);
if(newMeshConnected != nullptr)
{
RefreshConnections();
}
}
}
void MeshManager::OnAutoConnectWorkerThreadDoWork( Microsoft::Xbox::Samples::NetworkMesh::ProcessThreadsEventArgs^ args )
{
// Try to connect to every outgoing connection that we are disconnected from.
SecureDeviceAssociationTemplate^ secureDeviceAssociationTemplate = GetSecureDeviceAssociationTemplate();
Windows::Foundation::Collections::IVectorView<MeshConnection^>^ allDisconnectedConnections = GetConnectionsByType(ConnectionStatus::Disconnected);
for each (MeshConnection^ meshConnection in allDisconnectedConnections)
{
if(meshConnection->IsInComingAssociation())
{
// Don't bother connecting to someone who connected to you
// because that would create bi-directional connection which cause failures.
continue;
}
// Ignore connections that have a CreateAssociationAsync in progress on them
if(meshConnection->IsConnectionInProgress())
{
continue;
}
// Ignore anyone we have now a connection with. Just in case anything changed since we got the list of disconnected connections above
if( meshConnection->GetAssociation() != nullptr )
{
continue;
}
LogComment( Utils::GetThreadDescription(L"THREAD: Calling CreateAssociationAsync") );
meshConnection->SetConnectionInProgress(true);
auto asyncOp = secureDeviceAssociationTemplate->CreateAssociationAsync(
meshConnection->GetSecureDeviceAddress(),
Windows::Xbox::Networking::CreateSecureDeviceAssociationBehavior::Default
);
create_task( asyncOp )
.then([this, meshConnection](task<SecureDeviceAssociation^> t)
{
LogComment( Utils::GetThreadDescription(L"THREAD: CreateAssociationAsync result") );
try
{
SecureDeviceAssociation^ association = t.get();
LogCommentFormat( L"CreateAssociationAsync success: %s %s", meshConnection->GetConsoleName()->Data(), Utils::PrintSecureDeviceAssociation(association, false, true)->Data() );
MeshConnection^ newMeshConnection = GetConnectionFromSecureDeviceAddress(association->RemoteSecureDeviceAddress);
if(newMeshConnection != nullptr)
{
LogCommentFormat( L"Created new connection for %s", meshConnection->GetConsoleName()->Data() );
newMeshConnection->SetAssociation(association);
newMeshConnection->SetConnectionStatus(ConnectionStatus::Connected);
}
}
catch(Platform::Exception^ ex)
{
//We will auto-retry this again on next update.
LogCommentFormat(L"Connecting to remote machine failed %s. %s", meshConnection->GetConsoleName()->Data(), Utils::GetErrorString(ex->HResult)->Data() );
}
meshConnection->SetConnectionInProgress(false);
});
// This thread is waiting for CreateAssociationAsync to complete since this is happening
// inside a worker thread that does nothing but try to connect to other consoles
}
}
void MeshManager::OnHeartbeatWorkerThreadDoWork( Microsoft::Xbox::Samples::NetworkMesh::ProcessThreadsEventArgs^ args )
{
// This function is called every so often (eg. every 2 sec) by the MeshThread class
// First, refresh the connection list
RefreshConnections();
// Send hello to all connections that need it
Windows::Foundation::Collections::IVectorView<MeshConnection^>^ allconnected = GetConnectionsByType(ConnectionStatus::Connected);
for each (MeshConnection^ meshConnection in allconnected)
{
//Send hello to retry handshake.
Windows::Xbox::Networking::SecureDeviceAssociation^ association = meshConnection->GetAssociation();
if(association != nullptr)
{
bool isRespondingToHello = false;
Platform::String^ remoteName = Utils::PrintSecureDeviceAssociation(association, false, true);
LogCommentFormat( L"Sending hello to remote console: %s", remoteName->Data());
m_meshPacketManager->SendHelloMessage(association, GetLocalConsoleDisplayName(), isRespondingToHello);
}
}
// Send heartbeat to all connections that we have completed handshake with
Windows::Foundation::Collections::IVectorView<MeshConnection^>^ initializedConnections = GetConnectionsByType(ConnectionStatus::PostHandshake);
for each (MeshConnection^ meshConnection in initializedConnections)
{
Windows::Xbox::Networking::SecureDeviceAssociation^ association = meshConnection->GetAssociation();
if( association != nullptr )
{
m_meshPacketManager->SendHeartbeatMessageAsync( association, meshConnection->GetConsoleId() );
}
}
}
Platform::String^ MeshManager::GetLocalConsoleName()
{
Platform::String^ localConsoleName = L"Console";
{
Concurrency::critical_section::scoped_lock lock(m_connectionsLock);
localConsoleName = m_localConsoleName;
}
return localConsoleName;
}
void MeshManager::SetLocalConsoleName(Platform::String^ localConsoleName)
{
{
Concurrency::critical_section::scoped_lock lock(m_connectionsLock);
m_localConsoleName = localConsoleName;
}
}
Platform::String^ MeshManager::GetLocalConsoleDisplayName()
{
return Utils::FormatString(L"%s [%d]", GetLocalConsoleName()->Data(), m_meshPacketManager->GetLocalConsoleId() );
}
/// <summary>
/// </summary>
Windows::Foundation::Collections::IVectorView<MeshConnection^>^ MeshManager::GetConnections()
{
Concurrency::critical_section::scoped_lock lock(m_connectionsLock);
auto v = ref new Platform::Collections::Vector<MeshConnection^>(m_connections);
return v->GetView();
}
Windows::Foundation::Collections::IVectorView<MeshConnection^>^ MeshManager::GetConnectionsByType(ConnectionStatus type)
{
auto associationsByType = ref new Platform::Collections::Vector<MeshConnection^>();
Windows::Foundation::Collections::IVectorView<MeshConnection^>^ allConnections = GetConnections();
for each (MeshConnection^ meshConnection in allConnections)
{
if( meshConnection->GetConnectionStatus() == type )
{
associationsByType->Append(meshConnection);
}
}
return associationsByType->GetView();
}
void MeshManager::RefreshConnections()
{
// First, loop through all connections and mark all connections as not found in the template
Windows::Foundation::Collections::IVectorView<MeshConnection^>^ allConnections = GetConnections();
for each (MeshConnection^ meshConnection in allConnections)
{
meshConnection->SetAssocationFoundInTemplate(false);
}
// Now, Loop thought template and find the each connection and mark the ones that we found
// And take note of anything in the template that is not yet in our internal list of connections.
Windows::Foundation::Collections::IVectorView<Windows::Xbox::Networking::SecureDeviceAssociation^>^ associations = GetSecureDeviceAssociationTemplate()->Associations;
for each (Windows::Xbox::Networking::SecureDeviceAssociation^ associationInTemplate in associations)
{
MeshConnection^ meshConnection = GetConnectionFromSecureDeviceAddress(associationInTemplate->RemoteSecureDeviceAddress);
if(meshConnection != nullptr)
{
meshConnection->SetAssocationFoundInTemplate(true);
ConnectionStatus status = meshConnection->GetConnectionStatus();
// If the status is incorrect, then fix it log the error and fix it
if( status != ConnectionStatus::Connected &&
status != ConnectionStatus::PostHandshake )
{
LogCommentFormat( L"ERROR: Found association in template that we didn't CreateAssociationAsync or get an OnAssociationIncoming event for" );
LogCommentFormat( L"ERROR: Address: %s", Utils::PrintSecureDeviceAssociation(associationInTemplate, false, true)->Data() );
meshConnection->SetConnectionStatus(ConnectionStatus::Connected);
meshConnection->SetAssociation(associationInTemplate);
}
}
else
{
LogCommentFormat( L"ERROR: Mismatch occurred: Association in template not found in our internal list of connections" );
}
}
// First, loop through all connections again and for anything not found in the template, disconnect from it and tell the game.
// Get the latest list again as connections could have been deleted.
allConnections = GetConnections();
for each (MeshConnection^ meshConnection in allConnections)
{
if ( meshConnection != nullptr &&
false == meshConnection->GetAssocationFoundInTemplate() &&
meshConnection->GetConnectionStatus() != ConnectionStatus::Pending &&
meshConnection->GetConnectionStatus() != ConnectionStatus::Disconnected
)
{
// Do not delete him. We want the raise an event to the game and have the game call DestroyConnection explicitly.
LogCommentFormat( L"Disconnecting remote console %s who was not in template", meshConnection->GetConsoleName()->Data() );
LogCommentFormat( L"Address: %s", Utils::PrintSecureDeviceAssociation(meshConnection->GetAssociation(), false, true)->Data() );
meshConnection->SetConnectionStatus(ConnectionStatus::Disconnected);
OnDisconnected(this, meshConnection);
}
}
}
bool MeshManager::DoesConnectionExistInList(Windows::Foundation::Collections::IVectorView<MeshConnection^>^ list, MeshConnection^ connection)
{
if(connection == nullptr)
{
return false;
}
bool bfound = false;
for (unsigned int i = 0; i < list->Size; i++ )
{
if (list->GetAt(i) == connection)
{
bfound = true;
break;
}
}
return bfound;
}
void MeshManager::OnAssociationChange( Windows::Xbox::Networking::SecureDeviceAssociationStateChangedEventArgs^ args, Windows::Xbox::Networking::SecureDeviceAssociation^ association )
{
// Update this mesh member with the latest information
switch(args->NewState)
{
case Windows::Xbox::Networking::SecureDeviceAssociationState::DestroyingLocal:
case Windows::Xbox::Networking::SecureDeviceAssociationState::DestroyingRemote:
case Windows::Xbox::Networking::SecureDeviceAssociationState::Invalid:
{
bool bFound = false;
{
MeshConnection^ meshConnection = GetConnectionFromAssociation(association);
if(meshConnection != nullptr)
{
bFound = true;
// let the user know that this connection is being disconnected.
LogCommentFormat( L"Remote console is disconnecting %s", meshConnection->GetConsoleName()->Data() );
LogComment( Utils::GetThreadDescription(L"THREAD: Remote console disconnecting") );
meshConnection->SetConnectionDestroying(true);
meshConnection->SetConnectionStatus(ConnectionStatus::Disconnected);
OnDisconnected(this, meshConnection);
}
}
if (!bFound)
{
LogComment(L"Association Change event fired for the wrong association.");
}
}
default:
break;
}
}
MeshConnection^ MeshManager::GetConnectionFromAssociation(Windows::Xbox::Networking::SecureDeviceAssociation^ association)
{
if(association == nullptr)
{
return nullptr;
}
SecureDeviceAddress^ remoteSecureDeviceAddress = association->RemoteSecureDeviceAddress;
return GetConnectionFromSecureDeviceAddress(remoteSecureDeviceAddress);
}
MeshConnection^ MeshManager::GetConnectionFromSecureDeviceAddress(Windows::Xbox::Networking::SecureDeviceAddress^ address)
{
if(address == nullptr)
{
return nullptr;
}
Windows::Foundation::Collections::IVectorView<MeshConnection^>^ allConnections = GetConnections();
for each (MeshConnection^ meshConnection in allConnections)
{
SecureDeviceAddress^ remoteSecureDeviceAddress = meshConnection->GetSecureDeviceAddress();
if(AreSecureDeviceAddressesEqual(remoteSecureDeviceAddress, address))
{
return meshConnection;
}
}
return nullptr;
}
MeshConnection^ MeshManager::GetConnectionFromConsoleId(uint8 consoleId)
{
Windows::Foundation::Collections::IVectorView<MeshConnection^>^ allConnections = GetConnections();
for each (MeshConnection^ meshConnection in allConnections)
{
uint8 meshConsoleId = meshConnection->GetConsoleId();
if( meshConsoleId == consoleId )
{
return meshConnection;
}
}
return nullptr;
}
void MeshManager::DeleteConnection(MeshConnection^ connection)
{
if (connection == nullptr)
{
LogComment(L"Cannot pass a nullptr for the member to DeleteConnection");
throw ref new InvalidArgumentException(L"Cannot pass a nullptr for the member to DeleteConnection");
}
bool found = false;
{
Concurrency::critical_section::scoped_lock lock(m_connectionsLock);
auto iter = m_connections.begin();
for( ; iter != m_connections.end(); iter++ )
{
if ((*iter) == connection)
{
found = true;
break;
}
}
if (found)
{
m_connections.erase(iter);
}
}
}
void MeshManager::DestroyConnection( MeshConnection^ connection )
{
if(connection == nullptr)
{
return;
}
SecureDeviceAssociation^ meshAssociation = connection->GetAssociation();
if(meshAssociation != nullptr)
{
if(false == connection->IsConnectionDestroying())
{
connection->SetConnectionDestroying(true);
IAsyncAction^ asyncOp = meshAssociation->DestroyAsync();
create_task(asyncOp)
.then([this] (task<void> t)
{
try
{
t.get(); // if t.get fails, it will throw.
}
catch (Platform::COMException^ ex)
{
LogCommentFormat( L"MeshManager::DestroyConnection - DestroyAsync failed %s", Utils::GetErrorString(ex->HResult)->Data());
}
}).wait();
}
connection->SetAssociation(nullptr);
}
DeleteConnection(connection);
}
void MeshManager::DisconectFromAddress( Windows::Xbox::Networking::SecureDeviceAddress^ address )
{
MeshConnection^ connection = GetConnectionFromSecureDeviceAddress(address);
DestroyConnection(connection);
}
/// <summary>
/// </summary>
void MeshManager::DestroyAndDisconnectAll()
{
Windows::Foundation::Collections::IVectorView<MeshConnection^>^ allConnections = GetConnections();
for each (MeshConnection^ meshConnection in allConnections)
{
DestroyConnection(meshConnection);
}
{
Concurrency::critical_section::scoped_lock lock(m_connectionsLock);
m_connections.clear();
}
GetMeshPacketManager()->DeleteAllPendingAckMeshPackets();
}
void MeshManager::DestroyAllTemplateAssociations()
{
if(GetSecureDeviceAssociationTemplate() != nullptr)
{
Windows::Foundation::Collections::IVectorView<Windows::Xbox::Networking::SecureDeviceAssociation^>^ associations = GetSecureDeviceAssociationTemplate()->Associations;
if(associations == nullptr || associations->Size == 0)
{
return;
}
LogCommentFormat(L"We have %d associations on MeshManager::Initialize", associations->Size );
for each (Windows::Xbox::Networking::SecureDeviceAssociation^ associationInTemplate in associations)
{
IAsyncAction^ asyncOp = associationInTemplate->DestroyAsync();
create_task(asyncOp)
.then([this] (task<void> t)
{
try
{
t.get(); // if t.get fails, it will throw.
}
catch (Platform::COMException^ ex)
{
LogCommentFormat( L"MeshManager::DestroyAllTemplateAssociations - DestroyAsync failed %s", Utils::GetErrorString(ex->HResult)->Data());
}
}).wait();
}
}
}
void MeshManager::Shutdown()
{
LogComment( L"MeshManager::Shutdown");
DestroyAndDisconnectAll();
if (m_autoConnectThread != nullptr)
{
m_autoConnectThread->Shutdown();
m_autoConnectThread = nullptr;
}
if (m_heartbeatThread != nullptr)
{
m_heartbeatThread->Shutdown();
m_heartbeatThread = nullptr;
}
if( m_associationTemplate != nullptr )
{
m_associationTemplate->AssociationIncoming -= m_associationIncomingToken;
m_associationTemplate = nullptr;
}
if( m_meshPacketManager != nullptr )
{
m_meshPacketManager->OnHelloReceived -= m_onHelloReceivedToken;
m_meshPacketManager->OnHeartbeatReceived -= m_onHeartbeatReceivedToken;
m_meshPacketManager->OnDebugMessage -= m_onDebugMessageReceivedToken;
m_meshPacketManager->Shutdown();
m_meshPacketManager = nullptr;
}
}
bool MeshManager::AreSecureDeviceAddressesEqual( Windows::Xbox::Networking::SecureDeviceAddress^ secureDeviceAddress1, Windows::Xbox::Networking::SecureDeviceAddress^ secureDeviceAddress2 )
{
if( secureDeviceAddress1 != nullptr &&
secureDeviceAddress2 != nullptr &&
secureDeviceAddress1->Compare(secureDeviceAddress2) == 0 )
{
return true;
}
return false;
}
Microsoft::Xbox::Samples::NetworkMesh::MeshPacketManager^ MeshManager::GetMeshPacketManager()
{
return m_meshPacketManager;
}
UINT MeshManager::GetHeartbeatPeriod()
{
if (m_heartbeatThread == nullptr)
return 0;
return m_heartbeatThread->GetSendPeriod();
}
void MeshManager::SetHeartbeatPeriod(UINT periodInMilliseconds)
{
if (m_heartbeatThread != nullptr)
m_heartbeatThread->SetSendPeriod(periodInMilliseconds);
}
void MeshManager::OnHeartbeatReceived( Microsoft::Xbox::Samples::NetworkMesh::MeshHeartbeatReceivedEvent^ args )
{
if(args != nullptr)
{
MeshConnection^ meshConnection = args->Sender;
if(meshConnection != nullptr)
{
OnHeartbeat( this, args->Sender );
}
}
}
void MeshManager::OnHelloReceived( Microsoft::Xbox::Samples::NetworkMesh::MeshHelloReceivedEvent^ args )
{
if(args != nullptr)
{
LogCommentFormat( L"OnHelloReceived: Remote console: %s [%d]. RespondingToHello: %d", args->ConsoleName->Data(), args->ConsoleId, (int)args->RespondingToHello );
MeshConnection^ meshConnection = args->Sender;
if(meshConnection != nullptr )
{
Windows::Xbox::Networking::SecureDeviceAssociation^ secureDeviceAssociation = meshConnection->GetAssociation();
if( secureDeviceAssociation == nullptr )
{
return;
}
if (args->ConsoleId != 0)
{
meshConnection->SetConsoleId(args->ConsoleId);
}
if (!args->ConsoleName->IsEmpty())
{
meshConnection->SetConsoleName(args->ConsoleName);
}
// When the remote console sends us a Hello that wasn't a response from us, respond back with Hello
if( args->RespondingToHello == false )
{
LogCommentFormat( L"OnHelloReceived: Responding to hello to %s", Utils::PrintSecureDeviceAssociation(secureDeviceAssociation, false, true)->Data() );
m_meshPacketManager->SendHelloMessage( secureDeviceAssociation, GetLocalConsoleDisplayName(), true );
}
// If the connection to them was not Initialized, mark it and fire OnPostHandshake
ConnectionStatus connectionStatus = meshConnection->GetConnectionStatus();
if( connectionStatus != ConnectionStatus::PostHandshake )
{
meshConnection->SetConnectionStatus(ConnectionStatus::PostHandshake);
LogCommentFormat( L"Remote console %s", Utils::PrintSecureDeviceAssociation(secureDeviceAssociation, false, true)->Data() );
LogCommentFormat( L"is now known as %s", meshConnection->GetConsoleName()->Data() );
OnPostHandshake( this, meshConnection );
}
}
}
}
void MeshManager::OnDebugMessageReceived( Microsoft::Xbox::Samples::NetworkMesh::DebugMessageEventArgs^ args )
{
DebugMessageEventArgs^ eventArgs = ref new DebugMessageEventArgs( args->Message, args->HResult);
OnDebugMessage(this,eventArgs);
}
void MeshManager::RegisterMeshPacketEventHandlers()
{
if(m_meshPacketManager == nullptr)
{
LogComment(L"Could not register for events as MeshPacketManager may have failed initializing.");
return;
}
EventHandler<MeshHelloReceivedEvent^>^ onHelloReceivedEvent = ref new EventHandler<MeshHelloReceivedEvent^>(
[this] (Platform::Object^, MeshHelloReceivedEvent^ eventArgs)
{
OnHelloReceived(eventArgs);
});
m_onHelloReceivedToken = m_meshPacketManager->OnHelloReceived += onHelloReceivedEvent;
EventHandler<MeshHeartbeatReceivedEvent^>^ onHeartbeatReceivedEvent = ref new EventHandler<MeshHeartbeatReceivedEvent^>(
[this] (Platform::Object^, MeshHeartbeatReceivedEvent^ eventArgs)
{
OnHeartbeatReceived(eventArgs);
});
m_onHeartbeatReceivedToken = m_meshPacketManager->OnHeartbeatReceived += onHeartbeatReceivedEvent;
EventHandler<DebugMessageEventArgs^>^ onDebugMessageEvent = ref new EventHandler<DebugMessageEventArgs^>(
[this] (Platform::Object^, DebugMessageEventArgs^ eventArgs)
{
OnDebugMessageReceived(eventArgs);
});
m_onDebugMessageReceivedToken = m_meshPacketManager->OnDebugMessage += onDebugMessageEvent;
}
void MeshManager::LogCommentFormat( LPCWSTR strMsg, ... )
{
WCHAR strBuffer[2048];
va_list args;
va_start(args, strMsg);
_vsnwprintf_s( strBuffer, 2048, _TRUNCATE, strMsg, args );
strBuffer[2047] = L'\0';
va_end(args);
LogComment(ref new Platform::String(strBuffer));
}
void MeshManager::LogComment( Platform::String^ strText )
{
DebugMessageEventArgs^ eventArgs = ref new DebugMessageEventArgs( strText, S_OK);
OnDebugMessageReceived(eventArgs);
}
}}}}
@@ -0,0 +1,203 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#pragma once
#include "macros.h"
#include "XboxNetworkMeshDiagnosticsTraceLevel.h"
#include "MeshThread.h"
#include "MeshPacketManager.h"
#include "MeshConnection.h"
#include "MeshEvents.h"
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
ref class MeshPacketManager;
#define DEFAULT_HEARTBEAT_PERIOD_MILLISECONDS 2048
public ref class MeshManager sealed
{
public:
/// <summary>
/// Pass in the template for the title's SDA and kick off the WSA initialization
/// </summary>
/// <param name="secureDeviceAssociationTemplateName"></param>
MeshManager(
uint8 localConsoleId,
Platform::String^ secureDeviceAssociationTemplateName,
Platform::String^ localConsoleName,
bool dropOutOfOrderPackets
);
Platform::String^ GetLocalConsoleDisplayName();
Platform::String^ GetLocalConsoleName();
void SetLocalConsoleName(Platform::String^ consoleName);
/// <summary>
/// Return the template being used by local mesh manager
/// </summary>
Windows::Xbox::Networking::SecureDeviceAssociationTemplate^ GetSecureDeviceAssociationTemplate();
/// <summary>
/// Pass in the SDA you want to connect to.
/// </summary>
/// <param name="securedeviceAddress"></param>
void ConnectToAddress(Windows::Xbox::Networking::SecureDeviceAddress^ secureDeviceAddress, Platform::String^ debugName );
/// <summary>
/// </summary>
Windows::Foundation::Collections::IVectorView<MeshConnection^>^ GetConnections();
/// <summary>
/// </summary>
Windows::Foundation::Collections::IVectorView<MeshConnection^>^ GetConnectionsByType(ConnectionStatus type);
/// <summary>
/// </summary>
MeshConnection^ GetConnectionFromAssociation(Windows::Xbox::Networking::SecureDeviceAssociation^ association);
/// <summary>
/// </summary>
MeshConnection^ GetConnectionFromSecureDeviceAddress(Windows::Xbox::Networking::SecureDeviceAddress^ address);
MeshConnection^ GetConnectionFromConsoleId(uint8 consoleId);
/// <summary>
/// This will destroy the association and remove the connection from the list of all connections.
/// </summary>
void DestroyConnection( MeshConnection^ connection);
/// <summary>
/// Given a secure device address, this will destroy the association
/// and remove the connection from the list of all connections.
/// </summary>
void DisconectFromAddress( Windows::Xbox::Networking::SecureDeviceAddress^ address);
/// <summary>
/// Deletes all connections and destroys all associations.
/// </summary>
void DestroyAndDisconnectAll();
/// <summary>
/// Cleanup winsock
/// </summary>
void Shutdown();
/// <summary>
/// </summary>
Microsoft::Xbox::Samples::NetworkMesh::MeshPacketManager^ GetMeshPacketManager();
/// <summary>
/// Gets the Heartbeat period (in milliseconds)
/// </summary>
UINT GetHeartbeatPeriod();
/// <summary>
/// sets the Heartbeat period (in milliseconds)
/// </summary>
void SetHeartbeatPeriod(UINT periodInMilliseconds);
/// <summary>
/// // Warn the game that this connection is being disconnected. The game would have to then explicitly
/// call DisconnectFromAddress or DisconnectFromConnection to remove him from the connection list, if needed.
/// Otherwise, we will retry connecting to him.
/// </summary>
event Windows::Foundation::EventHandler<MeshConnection^>^ OnDisconnected;
/// <summary>
/// This event is triggered when a proper handshake has been established between 2 connections.
/// </summary>
event Windows::Foundation::EventHandler<MeshConnection^>^ OnPostHandshake;
/// <summary>
/// This event is triggered upon receiving heartbeats from remote connection.
/// </summary>
event Windows::Foundation::EventHandler<MeshConnection^>^ OnHeartbeat;
event Windows::Foundation::EventHandler<Microsoft::Xbox::Samples::NetworkMesh::DebugMessageEventArgs^>^ OnDebugMessage;
internal:
void OnAssociationChange(
Windows::Xbox::Networking::SecureDeviceAssociationStateChangedEventArgs^ args,
Windows::Xbox::Networking::SecureDeviceAssociation^ association );
/// <summary>
/// Return the template being used by local mesh manager
/// </summary>
void RefreshConnections();
private:
Concurrency::critical_section m_connectionsLock;
Microsoft::Xbox::Samples::NetworkMesh::MeshPacketManager^ m_meshPacketManager;
Platform::String^ m_localConsoleName;
MeshThread^ m_autoConnectThread;
Windows::Xbox::Networking::SecureDeviceAssociationTemplate^ m_associationTemplate;
std::vector<MeshConnection^> m_connections;
bool m_dropOutOfOrderPackets;
MeshThread^ m_heartbeatThread;
void Initialize(uint8 localConsoleId);
void RegisterMeshPacketEventHandlers();
MeshConnection^ AddConnection(
Windows::Xbox::Networking::SecureDeviceAddress^ secureDeviceAddress,
Windows::Xbox::Networking::SecureDeviceAssociation^ secureDeviceAssociation,
bool inComingAssociation,
ConnectionStatus connectionStatus
);
bool AreSecureDeviceAddressesEqual(
Windows::Xbox::Networking::SecureDeviceAddress^ secureDeviceAddress1,
Windows::Xbox::Networking::SecureDeviceAddress^ secureDeviceAddress2
);
/// <summary>
/// Deletes the connection from the list of all connections.
/// </summary>
void DeleteConnection(MeshConnection^ connection);
void DestroyAllTemplateAssociations();
bool DoesConnectionExistInList(Windows::Foundation::Collections::IVectorView<MeshConnection^>^ list, MeshConnection^ connection);
///////////////////////
// Events
Windows::Foundation::EventRegistrationToken m_associationIncomingToken;
Windows::Foundation::EventRegistrationToken m_onHelloReceivedToken;
Windows::Foundation::EventRegistrationToken m_onHeartbeatReceivedToken;
Windows::Foundation::EventRegistrationToken m_onDebugMessageReceivedToken;
void OnHeartbeatReceived( Microsoft::Xbox::Samples::NetworkMesh::MeshHeartbeatReceivedEvent^ args );
void OnHelloReceived( Microsoft::Xbox::Samples::NetworkMesh::MeshHelloReceivedEvent^ args );
void OnDebugMessageReceived( Microsoft::Xbox::Samples::NetworkMesh::DebugMessageEventArgs^ args );
void OnAutoConnectWorkerThreadDoWork ( Microsoft::Xbox::Samples::NetworkMesh::ProcessThreadsEventArgs^ args );
void OnAssociationIncoming(
Windows::Xbox::Networking::SecureDeviceAssociationTemplate^ associationTemplate,
Windows::Xbox::Networking::SecureDeviceAssociationIncomingEventArgs^ args
);
void OnHeartbeatWorkerThreadDoWork( Microsoft::Xbox::Samples::NetworkMesh::ProcessThreadsEventArgs^ args );
///////////////////////
// Logging
void LogComment( Platform::String^ strText );
void LogCommentFormat( LPCWSTR strMsg, ... );
};
}}}}
@@ -0,0 +1,30 @@
#include "pch.h"
#include "UserMeshConnectionPropertyBag.h"
using namespace Microsoft::Xbox::Samples::NetworkMesh;
using namespace Windows::Foundation;
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
UserMeshConnectionPropertyBag::UserMeshConnectionPropertyBag(Platform::String^ xboxUserId) :
m_xboxUserid(xboxUserId),
m_isUserAckedReceived(false)
{
}
bool UserMeshConnectionPropertyBag::IsUserAckedReceived()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_isUserAckedReceived;
}
void UserMeshConnectionPropertyBag::SetUserAckedReceived(bool val)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_isUserAckedReceived = val;
}
}}}}
@@ -0,0 +1,31 @@
//// 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
#pragma once
#include "MeshPacketStructs.h"
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
public ref class UserMeshConnectionPropertyBag sealed
{
internal:
UserMeshConnectionPropertyBag(Platform::String^ xboxUserId);
public:
bool IsUserAckedReceived();
void SetUserAckedReceived(bool val);
private:
Concurrency::critical_section m_stateLock;
Platform::String^ m_xboxUserid;
volatile bool m_isUserAckedReceived;
};
}}}}
@@ -0,0 +1,46 @@
//// 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 "MeshHeartbeatStatisticsForConnection.h"
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
MeshHeartbeatStatisticsForConnection::MeshHeartbeatStatisticsForConnection()
{
m_lastHeartbeatReceived.Duration = 0;
m_lastHeartbeatSent.Duration = 0;
}
TimeSpan MeshHeartbeatStatisticsForConnection::LastHeartbeatReceived::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_lastHeartbeatReceived;
}
TimeSpan MeshHeartbeatStatisticsForConnection::LastHeartbeatSent::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_lastHeartbeatSent;
}
void MeshHeartbeatStatisticsForConnection::SetLastHeartbeatReceived( TimeSpan val )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_lastHeartbeatReceived = val;
}
void MeshHeartbeatStatisticsForConnection::SetLastHeartbeatSent( TimeSpan val )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_lastHeartbeatSent = val;
}
}}}}
@@ -0,0 +1,36 @@
//// 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
#pragma once
#include "MeshPacketStructs.h"
using namespace Windows::Foundation;
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
public ref class MeshHeartbeatStatisticsForConnection sealed
{
public:
MeshHeartbeatStatisticsForConnection();
property TimeSpan LastHeartbeatReceived { TimeSpan get(); }
property TimeSpan LastHeartbeatSent { TimeSpan get(); }
internal:
void SetLastHeartbeatReceived(TimeSpan val);
void SetLastHeartbeatSent(TimeSpan val);
private:
Concurrency::critical_section m_stateLock;
TimeSpan m_lastHeartbeatReceived;
TimeSpan m_lastHeartbeatSent;
};
}}}}
@@ -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();
}
}}}}
@@ -0,0 +1,227 @@
//// 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
#pragma once
#include "MeshPacketStructs.h"
#include "MeshPacketStatistics.h"
#include "MeshConnection.h"
#include "MeshEvents.h"
#include "MeshThread.h"
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
static const int WSARECV_BUFFER_SIZE = 10000;
#define SKIPPED_PERCENT 5 // how much ahead do you want to skip to.
#define DEFAULT_HEARTBEAT_SIZE 0
struct MESH_PACKET_INFO
{
Windows::Xbox::Networking::SecureDeviceAssociation^ association;
std::vector<BYTE> packetBuffer;
};
struct MESH_PACKET_THAT_NEEDS_ACK
{
std::shared_ptr<MESH_PACKET_INFO> packetInfo;
uint16 messageId;
};
public ref class MeshPacketManager sealed
{
internal:
/// <summary>
/// To get MeshPacketManager, call MeshManger::GetMeshPacketManager()
/// </summary>
/// <param name="localConsoleId">Local console ID</param>
/// <param name="portNumberToBindTo">This is the sin6_port for the localSockAddress which is used to bind to the socket</param>
/// <param name="meshManager">Instance of the mesh manager</param>
MeshPacketManager(
uint8 localConsoleId,
unsigned short portNumberToBindTo,
MeshManager^ meshManager,
bool dropOutOfOrderPackets
);
public:
/// <summary>
/// Get local consoleId
/// </summary>
uint8 GetLocalConsoleId();
MeshPacketStatistics^ GetMeshPacketStatistics();
void UpdateDebugTimers( float timeDelta );
void SendChatMessage(
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
Windows::Storage::Streams::IBuffer^ buffer,
bool sendReliable
);
/// <summary>
/// gameDefinedMessageType is a 0 indexed number that the game can use to identify packet types
/// gameDefinedMessageType can be no greater than 192
/// </summary>
void SendCustomMessage(
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
uint8 gameDefinedMessageType,
Windows::Storage::Streams::IBuffer^ buffer,
bool sendReliable
);
/// <summary>
/// Resend packets until ack
/// </summary>
void SendReliablePacketsUntilACK();
float GetDebugTimeSincePacketReceive();
float GetDebugTimeSincePacketSend();
bool GetDebugInsideWSAReceive();
bool GetDebugInsideWSASend();
uint16 GetPreviousPacketMessageId();
event Windows::Foundation::EventHandler<Microsoft::Xbox::Samples::NetworkMesh::MeshChatMessageReceivedEvent^>^ OnChatMessageReceived;
event Windows::Foundation::EventHandler<Microsoft::Xbox::Samples::NetworkMesh::GameCustomMessageReceivedEvent^>^ OnGameCustomMessageReceived;
internal:
/// <summary>
/// The heartbeat is handled internally and shouldn't be called by the game
/// </summary>
void SendHeartbeatMessageAsync(
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
uint8 consoleId
);
/// <summary>
/// The hello handshake is handled internally and shouldn't be called by the game
/// </summary>
void SendHelloMessage(
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
Platform::String^ consoleName,
bool respondingToHello
);
/// <summary>
/// Sends an ACK for a message ID
/// </summary>
void SendAckMessage(
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
uint16 messageIdToAck
);
void DeleteAllPendingAckMeshPackets();
void SetDropOutOfOrderPackets(bool val);
void SetHeartbeatSize(UINT size);
UINT GetHeartbeatSize();
UINT16 IncrementPacketMessageId();
event Windows::Foundation::EventHandler<Microsoft::Xbox::Samples::NetworkMesh::MeshHeartbeatReceivedEvent^>^ OnHeartbeatReceived;
event Windows::Foundation::EventHandler<Microsoft::Xbox::Samples::NetworkMesh::MeshHelloReceivedEvent^>^ OnHelloReceived;
event Windows::Foundation::EventHandler<Microsoft::Xbox::Samples::NetworkMesh::MeshAckReceivedEvent^>^ OnAckReceived;
internal:
/// <summary>
/// The MeshManager ripples the debug event to its own OnDebugMessage so the caller can see debug events in the MeshPacketManager
/// </summary>
event Windows::Foundation::EventHandler<DebugMessageEventArgs^>^ OnDebugMessage;
/// <summary>
/// Called by the MeshManager when shutting down
/// </summary>
void Shutdown();
private:
void GetPacketWithHeader(
size_t packetSize,
uint8 messageType,
std::vector<BYTE>& packetBuffer,
bool sendReliable
);
void QueuePacketToSend(
std::shared_ptr<MESH_PACKET_INFO> packetInfo
);
void ProcessPacket(
Microsoft::Xbox::Samples::NetworkMesh::MeshConnection^ meshConnection,
BYTE* packetBuffer
);
Platform::Array<BYTE>^ ConvertSenderSocketAddressToArray(
BYTE* buffer,
int bufferSize
);
void LogMeshPacketManagerComment(
Platform::String^ message
);
void LogMeshPacketManagerCommentWithError(
Platform::String^ message,
HRESULT hr
);
bool ShouldPacketBeDropped( Microsoft::Xbox::Samples::NetworkMesh::MeshPacketHeader& packet );
void RecordMessageIfSendingReliable(
std::shared_ptr<MESH_PACKET_INFO> packetInfo
);
MeshConnection^ GetMeshConnection( SOCKADDR_STORAGE senderSocketAddress );
void SendReliablePacketsToConsolesWhoHaveNotAcked();
std::vector< std::shared_ptr<MESH_PACKET_THAT_NEEDS_ACK> > GetMeshPacketsThatNeedAckCopy();
void DeleteMeshPacketWhenGotAck(uint16 messageId);
void SocketReceiveWorkerThreadDoWork( Microsoft::Xbox::Samples::NetworkMesh::ProcessThreadsEventArgs^ args );
void SocketSendWorkerThreadDoWork( Microsoft::Xbox::Samples::NetworkMesh::ProcessThreadsEventArgs^ args );
void ProcessSendPacket( std::shared_ptr<MESH_PACKET_INFO> packetInfo );
void SetDebugTimeSincePacketReceive(float val);
void SetDebugTimeSincePacketSend(float val);
void SetDebugInsideWSAReceive(bool val);
void SetDebugInsideWSASend(bool val);
void SetPreviousPacketMessageId(uint16 val);
private:
SOCKET m_localSocket;
SOCKADDR_IN6 m_localSockAddress;
uint8 m_localConsoleId;
LONG m_packetMessageId;
uint16 m_previousPacketMessageId;
MeshPacketStatistics^ m_meshPacketStatistics;
Platform::WeakReference m_meshManager;
bool m_dropOutOfOrderPackets;
MeshThread^ m_socketReceiveThread;
BYTE m_bufferForWSARecv[WSARECV_BUFFER_SIZE];
MeshThread^ m_socketSendThread;
Concurrency::critical_section m_sendLock;
std::queue< std::shared_ptr<MESH_PACKET_INFO> > m_packetsToSend;
HANDLE m_sendWakeUpEventHandle;
Concurrency::critical_section m_debugStatsLock;
Concurrency::critical_section m_stateLock;
float m_debugTimeSincePacketReceive;
float m_debugTimeSincePacketSend;
bool m_debugInsideWSAReceive;
bool m_debugInsideWSASend;
UINT m_heartbeatMessageSize;
Concurrency::critical_section m_meshPacketsThatNeedAckLock;
std::vector< std::shared_ptr<MESH_PACKET_THAT_NEEDS_ACK> > m_meshPacketsThatNeedAck;
LARGE_INTEGER m_timerFrequency;
LARGE_INTEGER m_timerLastSendReliablePacketsUntilACK;
};
}}}}
@@ -0,0 +1,118 @@
//// 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 "MeshPacketStatistics.h"
#include "MeshPacketStructs.h"
#include "Utils.h"
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
MeshPacketStatistics::MeshPacketStatistics()
{
}
void MeshPacketStatistics::InspectPacket( Microsoft::Xbox::Samples::NetworkMesh::MeshPacketHeader& packet, bool sending )
{
MeshPacketStatisticsForPacketType^ stat = GetStatForPacketType(packet.messageType);
if( stat == nullptr )
{
stat = ref new MeshPacketStatisticsForPacketType();
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_messageTypeMap[packet.messageType] = stat;
}
}
if( sending )
{
stat->SetLargestPacketSent( max(packet.messageSize, stat->LargestPacketSent) );
stat->SetNumberPacketsSent( stat->NumberPacketsSent + 1 );
}
else
{
stat->SetLargestPacketReceived( max(packet.messageSize, stat->LargestPacketReceived) );
stat->SetNumberPacketsReceived( stat->NumberPacketsReceived + 1 );
}
if( (MessageTypeEnum) packet.messageType == MessageTypeEnum::GAME_HEARTBEAT_DATA )
{
MeshHeartbeatStatisticsForConnection^ hbstat = GetStatForConnection(packet.consoleId);
if( !sending )
{
hbstat->SetLastHeartbeatReceived( Utils::GetCurrentTime() );
}
}
}
void MeshPacketStatistics::PacketSkipped( Microsoft::Xbox::Samples::NetworkMesh::MeshPacketHeader& packet, int packetsSkipped )
{
MeshPacketStatisticsForPacketType^ stat = GetStatForPacketType(packet.messageType);
if( stat == nullptr )
{
stat = ref new MeshPacketStatisticsForPacketType();
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_messageTypeMap[packet.messageType] = stat;
}
}
stat->SetNumberPacketsSkipped( stat->NumberPacketsSkipped + packetsSkipped );
}
void MeshPacketStatistics::PacketDropped( Microsoft::Xbox::Samples::NetworkMesh::MeshPacketHeader& packet, int packetsDropped )
{
MeshPacketStatisticsForPacketType^ stat = GetStatForPacketType(packet.messageType);
if( stat == nullptr )
{
stat = ref new MeshPacketStatisticsForPacketType();
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_messageTypeMap[packet.messageType] = stat;
}
}
stat->SetNumberPacketsDropped( stat->NumberPacketsDropped + packetsDropped );
}
MeshPacketStatisticsForPacketType^ MeshPacketStatistics::GetStatForPacketType(uint8 messageType)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_messageTypeMap[messageType];
}
MeshHeartbeatStatisticsForConnection^ MeshPacketStatistics::GetStatForConnection(uint8 consoleId)
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
if( m_consoleIdMap.find(consoleId) == m_consoleIdMap.end() )
{
m_consoleIdMap[consoleId] = ref new MeshHeartbeatStatisticsForConnection();
m_consoleIdMap[consoleId]->SetLastHeartbeatReceived(Utils::GetCurrentTime());
m_consoleIdMap[consoleId]->SetLastHeartbeatSent(Utils::GetCurrentTime());
}
return m_consoleIdMap[consoleId];
}
void MeshPacketStatistics::ClearAllStatistics()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
for(std::map<uint8, MeshPacketStatisticsForPacketType^>::iterator iter = m_messageTypeMap.begin(); iter != m_messageTypeMap.end(); ++iter)
{
MeshPacketStatisticsForPacketType^ stat = iter->second;
if( stat != nullptr )
{
stat->ClearAllStatisticsForPacketType();
}
}
}
}}}}
@@ -0,0 +1,38 @@
//// 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
#pragma once
#include "MeshPacketStructs.h"
#include "MeshPacketStatisticsForPacketType.h"
#include "MeshHeartbeatStatisticsForConnection.h"
#include "MeshPacketStructs.h"
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
public ref class MeshPacketStatistics sealed
{
public:
MeshPacketStatistics();
MeshPacketStatisticsForPacketType^ GetStatForPacketType(uint8 messageType);
MeshHeartbeatStatisticsForConnection^ GetStatForConnection(uint8 consoleId);
void ClearAllStatistics();
internal:
void InspectPacket( Microsoft::Xbox::Samples::NetworkMesh::MeshPacketHeader& packet, bool sending );
void PacketDropped( Microsoft::Xbox::Samples::NetworkMesh::MeshPacketHeader& packet, int packetsDropped );
void PacketSkipped( Microsoft::Xbox::Samples::NetworkMesh::MeshPacketHeader& packet, int packetsSkipped );
private:
Concurrency::critical_section m_stateLock;
std::map<uint8, MeshPacketStatisticsForPacketType^> m_messageTypeMap;
std::map<uint8, MeshHeartbeatStatisticsForConnection^> m_consoleIdMap;
};
}}}}
@@ -0,0 +1,114 @@
//// 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 "MeshPacketStatisticsForPacketType.h"
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
MeshPacketStatisticsForPacketType::MeshPacketStatisticsForPacketType() :
m_numberPacketsReceived(0),
m_numberPacketsSent(0),
m_largestPacketReceived(0),
m_largestPacketSent(0),
m_numberPacketsDropped(0)
{
}
int32 MeshPacketStatisticsForPacketType::NumberPacketsReceived::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_numberPacketsReceived;
}
int32 MeshPacketStatisticsForPacketType::NumberPacketsSent::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_numberPacketsSent;
}
int32 MeshPacketStatisticsForPacketType::LargestPacketReceived::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_largestPacketReceived;
}
int32 MeshPacketStatisticsForPacketType::LargestPacketSent::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_largestPacketSent;
}
int32 MeshPacketStatisticsForPacketType::NumberPacketsDropped::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_numberPacketsDropped;
}
int32 MeshPacketStatisticsForPacketType::NumberPacketsSkipped::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_numberPacketsSkipped;
}
int32 MeshPacketStatisticsForPacketType::NumberPacketsLost::get()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
return m_numberPacketsSkipped - m_numberPacketsDropped;
}
void MeshPacketStatisticsForPacketType::SetNumberPacketsReceived( int32 val )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_numberPacketsReceived = val;
}
void MeshPacketStatisticsForPacketType::SetNumberPacketsSent( int32 val )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_numberPacketsSent = val;
}
void MeshPacketStatisticsForPacketType::SetLargestPacketReceived( int32 val )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_largestPacketReceived = val;
}
void MeshPacketStatisticsForPacketType::SetLargestPacketSent( int32 val )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_largestPacketSent = val;
}
void MeshPacketStatisticsForPacketType::SetNumberPacketsDropped( int32 val )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_numberPacketsDropped = val;
}
void MeshPacketStatisticsForPacketType::SetNumberPacketsSkipped( int32 val )
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_numberPacketsSkipped = val;
}
void MeshPacketStatisticsForPacketType::ClearAllStatisticsForPacketType()
{
Concurrency::critical_section::scoped_lock lock(m_stateLock);
m_numberPacketsReceived = 0;
m_numberPacketsSent = 0;
m_largestPacketReceived = 0;
m_largestPacketSent = 0;
m_numberPacketsDropped = 0;
}
}}}}
@@ -0,0 +1,49 @@
//// 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
#pragma once
#include "MeshPacketStructs.h"
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
public ref class MeshPacketStatisticsForPacketType sealed
{
public:
MeshPacketStatisticsForPacketType();
property int32 NumberPacketsReceived { int32 get(); }
property int32 NumberPacketsSent { int32 get(); }
property int32 LargestPacketReceived { int32 get(); }
property int32 LargestPacketSent { int32 get(); }
property int32 NumberPacketsDropped { int32 get(); }
property int32 NumberPacketsSkipped { int32 get(); }
property int32 NumberPacketsLost { int32 get(); }
internal:
void SetNumberPacketsReceived(int32 val);
void SetNumberPacketsSent(int32 val);
void SetLargestPacketReceived(int32 val);
void SetLargestPacketSent(int32 val);
void SetNumberPacketsDropped(int32 val);
void SetNumberPacketsSkipped(int32 val);
void ClearAllStatisticsForPacketType();
private:
Concurrency::critical_section m_stateLock;
long m_numberPacketsReceived;
long m_numberPacketsSent;
long m_largestPacketReceived;
long m_largestPacketSent;
long m_numberPacketsDropped;
long m_numberPacketsSkipped;
};
}}}}
@@ -0,0 +1,42 @@
//// 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
#pragma once
namespace Microsoft { namespace Xbox { namespace Samples { namespace NetworkMesh {
public enum class MessageTypeEnum
{
GAME_HEARTBEAT_DATA = 1, // Heartbeat
GAME_HELLO_DATA = 2, // First message sent between clients
GAME_CHAT_DATA = 3, // Sending chat data
GAME_ACK = 4, // Sending ACK packet
GAME_CUSTOM_DATA = 64 // Message type 64 or higher is custom data as defined by the game
};
// Set data alignment to be 1 byte
#pragma pack(push)
#pragma pack(1)
struct MeshPacketHeader
{
uint16 messageId; // To keep track of packets being sent/received to detect number of packets dropped.
uint8 messageType; // Type of message (MessageTypeEnum)
uint8 consoleId; // Mesh unique identifier of the console who created packet
uint16 messageSize; // Total number of bytes in the packet
};
struct MeshPacketHelloMessageHeader
{
uint8 respondingToHello; // Set to 1 if this packet is responding to a hello message
uint16 consoleNameLength; // Length is number of characters, NOT including any null termination
};
// Store data alignment
#pragma pack(pop)
}}}}
@@ -0,0 +1,371 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Profile|ARM">
<Configuration>Profile</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Durango">
<Configuration>Release</Configuration>
<Platform>Durango</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Profile|Durango">
<Configuration>Profile</Configuration>
<Platform>Durango</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Durango">
<Configuration>Debug</Configuration>
<Platform>Durango</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<RootNamespace>Microsoft.Xbox.Samples.NetworkMesh</RootNamespace>
<ProjectGuid>{9B399639-7A3F-44CF-82EF-D4C50718120E}</ProjectGuid>
<DefaultLanguage>en-US</DefaultLanguage>
<Keyword>Win32Proj</Keyword>
<ApplicationEnvironment>title</ApplicationEnvironment>
<!-- - - - -->
<PlatformToolset>v140</PlatformToolset>
<MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
<TargetRuntime>Native</TargetRuntime>
<SccProjectName>SAK</SccProjectName>
<SccAuxPath>SAK</SccAuxPath>
<SccLocalPath>SAK</SccLocalPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Durango'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<EmbedManifest>false</EmbedManifest>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<EmbedManifest>false</EmbedManifest>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Durango'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<EmbedManifest>false</EmbedManifest>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<EmbedManifest>false</EmbedManifest>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<EmbedManifest>false</EmbedManifest>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<EmbedManifest>false</EmbedManifest>
<GenerateManifest>false</GenerateManifest>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Durango'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Durango'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Profile|ARM'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
<IncludePath>$(Console_SdkIncludeRoot)</IncludePath>
<ExecutablePath>$(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH);</ExecutablePath>
<LinkIncremental>false</LinkIncremental>
<OutDir>$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)$(PlatformToolsetVersion)\obj\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
<IncludePath>$(Console_SdkIncludeRoot)</IncludePath>
<ExecutablePath>$(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH);</ExecutablePath>
<LinkIncremental>false</LinkIncremental>
<OutDir>$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)$(PlatformToolsetVersion)\obj\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Durango'">
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
<IncludePath>$(Console_SdkIncludeRoot)</IncludePath>
<ExecutablePath>$(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH);</ExecutablePath>
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)$(PlatformToolsetVersion)\obj\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Profile|ARM'">
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
<IncludePath>$(Console_SdkIncludeRoot)</IncludePath>
<ExecutablePath>$(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH);</ExecutablePath>
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)$(PlatformToolsetVersion)\obj\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'">
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
<IncludePath>$(Console_SdkIncludeRoot)</IncludePath>
<ExecutablePath>$(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH);</ExecutablePath>
<LinkIncremental>true</LinkIncremental>
<OutDir>$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)$(PlatformToolsetVersion)\obj\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<ReferencePath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</ReferencePath>
<LibraryPath>$(Console_SdkLibPath)</LibraryPath>
<LibraryWPath>$(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath)</LibraryWPath>
<IncludePath>$(Console_SdkIncludeRoot)</IncludePath>
<ExecutablePath>$(Console_SdkRoot)bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH);</ExecutablePath>
<LinkIncremental>true</LinkIncremental>
<OutDir>$(ProjectDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)$(PlatformToolsetVersion)\obj\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">
<Link>
<AdditionalDependencies>pixEvt.lib;ws2_32.lib;combase.lib;kernelx.lib;uuid.lib;</AdditionalDependencies>
<EntryPointSymbol>
</EntryPointSymbol>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
</Link>
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalUsingDirectories>$(Console_SdkPackagesRoot);$(Console_SdkWindowsMetadataPath);%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;__WRL_NO_DEFAULT_LIB__;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<CompileAsWinRT>true</CompileAsWinRT>
<AdditionalIncludeDirectories>Utils;Mesh;MeshPacket;Common</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">
<Link>
<AdditionalDependencies>pixEvt.lib;ws2_32.lib;combase.lib;kernelx.lib;uuid.lib;</AdditionalDependencies>
<EntryPointSymbol>
</EntryPointSymbol>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
</Link>
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalUsingDirectories>$(Console_SdkPackagesRoot);$(Console_SdkWindowsMetadataPath);%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;__WRL_NO_DEFAULT_LIB__;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<CompileAsWinRT>true</CompileAsWinRT>
<AdditionalIncludeDirectories>Utils;Mesh;MeshPacket;Common</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Profile|Durango'">
<Link>
<AdditionalDependencies>pixEvt.lib;ws2_32.lib;combase.lib;kernelx.lib;uuid.lib;</AdditionalDependencies>
<EntryPointSymbol>
</EntryPointSymbol>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
</Link>
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalUsingDirectories>$(Console_SdkPackagesRoot);$(Console_SdkWindowsMetadataPath);%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;__WRL_NO_DEFAULT_LIB__;Profile;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<CompileAsWinRT>true</CompileAsWinRT>
<AdditionalIncludeDirectories>Utils;Mesh;MeshPacket;Common</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Profile|ARM'">
<Link>
<AdditionalDependencies>pixEvt.lib;ws2_32.lib;combase.lib;kernelx.lib;uuid.lib;</AdditionalDependencies>
<EntryPointSymbol>
</EntryPointSymbol>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
</Link>
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalUsingDirectories>$(Console_SdkPackagesRoot);$(Console_SdkWindowsMetadataPath);%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<Optimization>MaxSpeed</Optimization>
<PreprocessorDefinitions>_WINRT_DLL;NDEBUG;__WRL_NO_DEFAULT_LIB__;Profile;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<CompileAsWinRT>true</CompileAsWinRT>
<AdditionalIncludeDirectories>Utils;Mesh;MeshPacket;Common</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'">
<Link>
<AdditionalDependencies>pixEvt.lib;ws2_32.lib;combase.lib;kernelx.lib;uuid.lib;</AdditionalDependencies>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
</Link>
<ClCompile>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeader>Use</PrecompiledHeader>
<MinimalRebuild>false</MinimalRebuild>
<AdditionalUsingDirectories>$(Console_SdkPackagesRoot);$(Console_SdkWindowsMetadataPath);%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_WINRT_DLL;WIN32_LEAN_AND_MEAN=1;ENABLE_INTSAFE_SIGNED_FUNCTIONS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<CompileAsWinRT>true</CompileAsWinRT>
<TreatWarningAsError>true</TreatWarningAsError>
<SDLCheck>
</SDLCheck>
<MultiProcessorCompilation />
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<CompileAsManaged>
</CompileAsManaged>
<AdditionalIncludeDirectories>Utils;Mesh;MeshPacket;Common</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">
<Link>
<AdditionalDependencies>pixEvt.lib;ws2_32.lib;combase.lib;kernelx.lib;uuid.lib;</AdditionalDependencies>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
<IgnoreAllDefaultLibraries>false</IgnoreAllDefaultLibraries>
</Link>
<ClCompile>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<PrecompiledHeader>Use</PrecompiledHeader>
<MinimalRebuild>false</MinimalRebuild>
<AdditionalUsingDirectories>$(Console_SdkPackagesRoot);$(Console_SdkWindowsMetadataPath);%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_WINRT_DLL;WIN32_LEAN_AND_MEAN=1;ENABLE_INTSAFE_SIGNED_FUNCTIONS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<CompileAsWinRT>true</CompileAsWinRT>
<TreatWarningAsError>true</TreatWarningAsError>
<SDLCheck>
</SDLCheck>
<MultiProcessorCompilation>
</MultiProcessorCompilation>
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
<CompileAsManaged>
</CompileAsManaged>
<AdditionalIncludeDirectories>Utils;Mesh;MeshPacket;Common</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="common\Configuration.h" />
<ClInclude Include="common\macros.h" />
<ClInclude Include="common\MeshThread.h" />
<ClInclude Include="common\XboxNetworkMeshDiagnosticsTraceLevel.h" />
<ClInclude Include="common\XboxServicesApiVer.h" />
<ClInclude Include="MeshPacket\MeshHeartbeatStatisticsForConnection.h" />
<ClInclude Include="MeshPacket\MeshPacketManager.h" />
<ClInclude Include="MeshPacket\MeshPacketStatistics.h" />
<ClInclude Include="MeshPacket\MeshPacketStatisticsForPacketType.h" />
<ClInclude Include="MeshPacket\MeshPacketStructs.h" />
<ClInclude Include="Mesh\MeshConnection.h" />
<ClInclude Include="Mesh\MeshEvents.h" />
<ClInclude Include="Mesh\MeshManager.h" />
<ClInclude Include="Mesh\UserMeshConnectionPropertyBag.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="Utils\Clock.h" />
<ClInclude Include="Utils\iso8601.h" />
<ClInclude Include="Utils\Utils.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="common\Configuration.cpp" />
<ClCompile Include="common\MeshThread.cpp" />
<ClCompile Include="MeshPacket\MeshHeartbeatStatisticsForConnection.cpp" />
<ClCompile Include="MeshPacket\MeshPacketManager.cpp" />
<ClCompile Include="MeshPacket\MeshPacketStatistics.cpp" />
<ClCompile Include="MeshPacket\MeshPacketStatisticsForPacketType.cpp" />
<ClCompile Include="Mesh\MeshConnection.cpp" />
<ClCompile Include="Mesh\MeshManager.cpp" />
<ClCompile Include="Mesh\UserMeshConnectionPropertyBag.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Durango'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Profile|Durango'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Profile|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Durango'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="Utils\iso8601.cpp" />
<ClCompile Include="Utils\Utils.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Common">
<UniqueIdentifier>{40668e48-5670-4c99-af05-8092b6e54c55}</UniqueIdentifier>
</Filter>
<Filter Include="MeshPacket">
<UniqueIdentifier>{7135be5d-6976-41d8-9597-2568527e2eaa}</UniqueIdentifier>
</Filter>
<Filter Include="Mesh">
<UniqueIdentifier>{de86f844-2817-4313-bf1a-59aaf166cb48}</UniqueIdentifier>
</Filter>
<Filter Include="Utils">
<UniqueIdentifier>{208aa7a1-7e56-4172-b031-b6a5c96d7331}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Mesh\MeshManager.cpp">
<Filter>Mesh</Filter>
</ClCompile>
<ClCompile Include="common\Configuration.cpp">
<Filter>Common</Filter>
</ClCompile>
<ClCompile Include="MeshPacket\MeshPacketManager.cpp">
<Filter>MeshPacket</Filter>
</ClCompile>
<ClCompile Include="Mesh\MeshConnection.cpp">
<Filter>Mesh</Filter>
</ClCompile>
<ClCompile Include="MeshPacket\MeshPacketStatistics.cpp">
<Filter>MeshPacket</Filter>
</ClCompile>
<ClCompile Include="MeshPacket\MeshPacketStatisticsForPacketType.cpp">
<Filter>MeshPacket</Filter>
</ClCompile>
<ClCompile Include="Utils\iso8601.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="Utils\Utils.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="common\MeshThread.cpp">
<Filter>Common</Filter>
</ClCompile>
<ClCompile Include="Mesh\UserMeshConnectionPropertyBag.cpp">
<Filter>Mesh</Filter>
</ClCompile>
<ClCompile Include="MeshPacket\MeshHeartbeatStatisticsForConnection.cpp">
<Filter>MeshPacket</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Mesh\MeshManager.h">
<Filter>Mesh</Filter>
</ClInclude>
<ClInclude Include="common\Configuration.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="common\macros.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="common\XboxNetworkMeshDiagnosticsTraceLevel.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="common\XboxServicesApiVer.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="MeshPacket\MeshPacketStructs.h">
<Filter>MeshPacket</Filter>
</ClInclude>
<ClInclude Include="MeshPacket\MeshPacketManager.h">
<Filter>MeshPacket</Filter>
</ClInclude>
<ClInclude Include="Mesh\MeshConnection.h">
<Filter>Mesh</Filter>
</ClInclude>
<ClInclude Include="Mesh\MeshEvents.h">
<Filter>Mesh</Filter>
</ClInclude>
<ClInclude Include="MeshPacket\MeshPacketStatistics.h">
<Filter>MeshPacket</Filter>
</ClInclude>
<ClInclude Include="MeshPacket\MeshPacketStatisticsForPacketType.h">
<Filter>MeshPacket</Filter>
</ClInclude>
<ClInclude Include="Utils\Clock.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="Utils\iso8601.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="pch.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="Utils\Utils.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="common\MeshThread.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="Mesh\UserMeshConnectionPropertyBag.h">
<Filter>Mesh</Filter>
</ClInclude>
<ClInclude Include="MeshPacket\MeshHeartbeatStatisticsForConnection.h">
<Filter>MeshPacket</Filter>
</ClInclude>
</ItemGroup>
</Project>
@@ -0,0 +1,119 @@
#pragma once
#include <synchapi.h>
class Clock
{
public:
Clock() : m_heartbeats(0), m_wakeUps(0)
{
m_timerFrequency.QuadPart = 0;
m_timerStart.QuadPart = 0;
m_countPerPeriod.QuadPart = 0;
}
~Clock()
{
}
void Initialize( UINT uPeriodMS )
{
if (!QueryPerformanceFrequency(&m_timerFrequency))
{
assert(false);
}
SetInterval(uPeriodMS);
}
void SetInterval( UINT uPeriodMS )
{
m_countPerPeriod.QuadPart = ( m_timerFrequency.QuadPart * uPeriodMS ) / c_uOneSecondInMS;
}
void SleepUntilNextHeartbeat()
{
LARGE_INTEGER timeOfNextHeartbeat = GetNextHeartbeat();
LARGE_INTEGER timeNow;
m_heartbeats++; // so that the next one will be 20ms (or whatever) later...
if (!QueryPerformanceCounter(&timeNow))
{
assert(false);
}
LONGLONG timeDelta = timeOfNextHeartbeat.QuadPart - timeNow.QuadPart;
if ( timeDelta > 0 )
{
LONGLONG numberOfMilliSecondsSinceLast = c_uOneSecondInMS * timeDelta / m_timerFrequency.QuadPart;
WCHAR text[200] = {0};
swprintf_s( text, L"I Slept: %lld", numberOfMilliSecondsSinceLast );
OutputDebugString( text );
DWORD dwSleepTime = static_cast<DWORD>(numberOfMilliSecondsSinceLast);
Sleep( dwSleepTime );
}
}
DWORD WaitForEventsOrHeartbeat(HANDLE hObject, HANDLE hObject2)
{
LARGE_INTEGER liExpected = GetNextHeartbeat();
LARGE_INTEGER liNow;
m_wakeUps = 0;
m_heartbeats++; // so that the next one will be 20ms (or whatever) later...
if (!QueryPerformanceCounter(&liNow))
{
assert(false);
}
LONGLONG llDiff = liExpected.QuadPart - liNow.QuadPart;
HANDLE aObjects[2];
aObjects[0] = hObject;
aObjects[1] = hObject2;
if ( llDiff > 0 )
{
DWORD dwSleepTime = static_cast<DWORD>(c_uOneSecondInMS * llDiff / m_timerFrequency.QuadPart);
m_wakeUps++;
return ::WaitForMultipleObjectsEx( 2, aObjects, false, dwSleepTime, FALSE );
}
// we're already late, no need to wait, but we still need to test the object
m_wakeUps++;
return ::WaitForMultipleObjectsEx( 2, aObjects, false, 0, FALSE );
}
UINT m_wakeUps;
private:
static const UINT c_uOneSecondInMS = 1000;
UINT m_heartbeats;
LARGE_INTEGER m_timerFrequency;
LARGE_INTEGER m_timerStart;
LARGE_INTEGER m_countPerPeriod;
LARGE_INTEGER GetNextHeartbeat()
{
LARGE_INTEGER timeExpected;
// Update Start
if ( m_timerStart.QuadPart == 0 )
{
if (!QueryPerformanceCounter( &m_timerStart ))
{
assert(false);
}
m_heartbeats = 1; // we are on the very first heartbeat
}
timeExpected.QuadPart = m_timerStart.QuadPart + (m_countPerPeriod.QuadPart * m_heartbeats);
return timeExpected;
}
};
@@ -0,0 +1,525 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#include "pch.h"
#include "Utils.h"
#include "ISO8601.h"
#include "Robuffer.h"
#include "Configuration.h"
// for conversion from seconds (int) to TimeSpan (perhaps use _XTIME_TICKS_PER_TIME_T instead)
#define TICKS_PER_SECOND 10000000i64
#define SECONDS_PER_DAY 86400
using namespace Microsoft::Xbox::Samples::NetworkMesh;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Storage::Streams;
std::wstring&
Utils::Replace(
__inout std::wstring& strSource,
__in PCWSTR pwszPattern,
__in_opt PCWSTR pwszReplacement,
__out_opt size_t* pnOccurrencesReplaced
)
{
THROW_INVALIDARGUMENT_IF( Utils::IsNullOrEmptyString( pwszPattern ));
const size_t nPatternLength = wcslen( pwszPattern );
return ReplaceSubstring<WCHAR>(
strSource,
pwszPattern,
nPatternLength,
( ( pwszReplacement != nullptr ) ? pwszReplacement : L"" ),
pnOccurrencesReplaced
);
}
std::vector<std::wstring>
Utils::StringSplit(
__in const std::wstring& string,
__in WCHAR seperator
)
{
std::vector<std::wstring> vSubStrings;
if ( !string.empty() )
{
size_t posStart = 0, posFound = 0;
while ( posFound != std::wstring::npos && posStart < string.length() )
{
posFound = string.find( seperator, posStart);
if ( posFound != std::wstring::npos )
{
if ( posFound != posStart )
{
// this substring is not empty
vSubStrings.push_back( string.substr( posStart, posFound - posStart ) );
}
posStart = posFound + 1;
}
else
{
vSubStrings.push_back( string.substr( posStart ) );
}
}
}
return vSubStrings;
}
Platform::String^
Utils::DateTimeToString(
__in Windows::Foundation::DateTime dateTime
)
{
// It's OK for us not be able to handle BC time.
THROW_HR_IF( dateTime.UniversalTime < 0, E_BOUNDS);
FILETIME fileTime;
fileTime.dwLowDateTime = (dateTime.UniversalTime & (DWORD)-1);
fileTime.dwHighDateTime = (dateTime.UniversalTime >> 32 & (DWORD)-1);
WCHAR dateString[ISO8601_MAX_CCH];
THROW_IF_HR_FAILED(
FILETIMEToISO8601W( &fileTime, false, dateString, ARRAYSIZE(dateString), FALSE)
);
return ref new String(dateString);
}
String^
Utils::RemoveBracesFromGuidString(
__in String^ guid
)
{
std::wstring strGuid = guid->ToString()->Data();
if(strGuid.length() > 0 && strGuid[0] == L'{')
{
// Remove the {
strGuid.erase(0, 1);
}
if(strGuid.length() > 0 && strGuid[strGuid.length() - 1] == L'}')
{
// Remove the }
strGuid.erase(strGuid.end() - 1, strGuid.end());
}
return ref new String(strGuid.c_str());
}
Windows::Foundation::TimeSpan
Utils::ConvertMillisecondsToTimeSpan(
__in uint64 milliseconds
)
{
Windows::Foundation::TimeSpan ts;
ts.Duration = (TICKS_PER_SECOND/1000) * milliseconds;
return ts;
}
Windows::Foundation::TimeSpan
Utils::ConvertSecondsToTimeSpan(
__in uint32 seconds
)
{
Windows::Foundation::TimeSpan ts;
ts.Duration = TICKS_PER_SECOND * seconds;
return ts;
}
uint32
Utils::ConvertTimeSpanToSeconds(
__in Windows::Foundation::TimeSpan timespan
)
{
int64 seconds = timespan.Duration / TICKS_PER_SECOND;
THROW_INVALIDARGUMENT_IF( seconds < 0 || seconds > UINT32_MAX );
return static_cast<uint32>(seconds);
}
int64
Utils::ConvertTimeSpanToMilliseconds(
__in Windows::Foundation::TimeSpan timespan
)
{
int64 milliseconds = timespan.Duration / (TICKS_PER_SECOND/1000);
return milliseconds;
}
uint32
Utils::ConvertTimeSpanToDays(
__in Windows::Foundation::TimeSpan timespan
)
{
int64 days = (timespan.Duration / TICKS_PER_SECOND) / SECONDS_PER_DAY;
THROW_INVALIDARGUMENT_IF( days < 0 || days > UINT32_MAX );
return static_cast<uint32>(days);
}
typedef union tagTU
{
FILETIME ft;
ULARGE_INTEGER ui;
} TU;
Windows::Foundation::TimeSpan Utils::GetCurrentTime()
{
SYSTEMTIME curTime = {0};
GetSystemTime(&curTime);
HRESULT hr;
TU ftTime = {0};
if (SystemTimeToFileTime(&curTime, &ftTime.ft))
{
Windows::Foundation::TimeSpan ts;
ts.Duration = ftTime.ui.QuadPart;
return ts;
}
else
{
hr = HRESULT_FROM_WIN32( GetLastError() );
THROW_HR(hr);
}
}
Platform::String^ Utils::ConvertHResultToString( HRESULT hr )
{
WCHAR tmp[ 1024 ];
_snwprintf_s( tmp, _countof( tmp ), _TRUNCATE, L"0x%0.8x", hr);
return ref new Platform::String(tmp);
}
Platform::String^ Utils::ConvertHResultToErrorName( HRESULT hr )
{
switch( hr )
{
// Generic errors
case S_OK: return L"S_OK";
case S_FALSE: return L"S_FALSE";
case E_OUTOFMEMORY: return L"E_OUTOFMEMORY";
case E_ACCESSDENIED: return L"E_ACCESSDENIED";
case E_INVALIDARG: return L"E_INVALIDARG";
case E_UNEXPECTED: return L"E_UNEXPECTED";
case E_ABORT: return L"E_ABORT";
case E_FAIL: return L"E_FAIL";
case E_NOTIMPL: return L"E_NOTIMPL";
case E_ILLEGAL_METHOD_CALL: return L"E_ILLEGAL_METHOD_CALL";
case 0x8007274C: return L"WSATIMEOUT";
// Authentication specific errors
case 0x87DD0003: return L"AM_E_XASD_UNEXPECTED";
case 0x87DD0004: return L"AM_E_XASU_UNEXPECTED";
case 0x87DD0005: return L"AM_E_XAST_UNEXPECTED";
case 0x87DD0006: return L"AM_E_XSTS_UNEXPECTED";
case 0x87DD0007: return L"AM_E_XDEVICE_UNEXPECTED";
case 0x87DD0008: return L"AM_E_DEVMODE_NOT_AUTHORIZED";
case 0x87DD0009: return L"AM_E_NOT_AUTHORIZED";
case 0x87DD000A: return L"AM_E_FORBIDDEN";
case 0x87DD000B: return L"AM_E_UNKNOWN_TARGET";
case 0x87DD000C: return L"AM_E_NSAL_READ_FAILED";
case 0x87DD000D: return L"AM_E_TITLE_NOT_AUTHENTICATED";
case 0x87DD000E: return L"AM_E_TITLE_NOT_AUTHORIZED";
case 0x87DD000F: return L"AM_E_DEVICE_NOT_AUTHENTICATED";
case 0x87DD0010: return L"AM_E_INVALID_USER_INDEX";
case 0x8015DC00: return L"XO_E_DEVMODE_NOT_AUTHORIZED";
case 0x8015DC01: return L"XO_E_SYSTEM_UPDATE_REQUIRED";
case 0x8015DC02: return L"XO_E_CONTENT_UPDATE_REQUIRED";
case 0x8015DC03: return L"XO_E_ENFORCEMENT_BAN";
case 0x8015DC04: return L"XO_E_THIRD_PARTY_BAN";
case 0x8015DC05: return L"XO_E_ACCOUNT_PARENTALLY_RESTRICTED";
case 0x8015DC06: return L"XO_E_DEVICE_SUBSCRIPTION_NOT_ACTIVATED";
case 0x8015DC08: return L"XO_E_ACCOUNT_BILLING_MAINTENANCE_REQUIRED";
case 0x8015DC09: return L"XO_E_ACCOUNT_CREATION_REQUIRED";
case 0x8015DC0A: return L"XO_E_ACCOUNT_TERMS_OF_USE_NOT_ACCEPTED";
case 0x8015DC0B: return L"XO_E_ACCOUNT_COUNTRY_NOT_AUTHORIZED";
case 0x8015DC0C: return L"XO_E_ACCOUNT_AGE_VERIFICATION_REQUIRED";
case 0x8015DC0D: return L"XO_E_ACCOUNT_CURFEW";
case 0x8015DC0E: return L"XO_E_ACCOUNT_ZEST_MAINTENANCE_REQUIRED";
case 0x8015DC0F: return L"XO_E_ACCOUNT_CSV_TRANSITION_REQUIRED";
case 0x8015DC10: return L"XO_E_ACCOUNT_MAINTENANCE_REQUIRED";
case 0x8015DC11: return L"XO_E_ACCOUNT_TYPE_NOT_ALLOWED";
case 0x8015DC12: return L"XO_E_CONTENT_ISOLATION (Verify SCID / Sandbox)";
case 0x8015DC13: return L"XO_E_ACCOUNT_NAME_CHANGE_REQUIRED";
case 0x8015DC14: return L"XO_E_DEVICE_CHALLENGE_REQUIRED";
case 0x8015DC20: return L"XO_E_EXPIRED_DEVICE_TOKEN";
case 0x8015DC21: return L"XO_E_EXPIRED_TITLE_TOKEN";
case 0x8015DC22: return L"XO_E_EXPIRED_USER_TOKEN";
case 0x8015DC23: return L"XO_E_INVALID_DEVICE_TOKEN";
case 0x8015DC24: return L"XO_E_INVALID_TITLE_TOKEN";
case 0x8015DC25: return L"XO_E_INVALID_USER_TOKEN";
// HTTP specific errors
case WEB_E_UNSUPPORTED_FORMAT: return L"WEB_E_UNSUPPORTED_FORMAT";
case WEB_E_INVALID_XML: return L"WEB_E_INVALID_XML";
case WEB_E_MISSING_REQUIRED_ELEMENT: return L"WEB_E_MISSING_REQUIRED_ELEMENT";
case WEB_E_MISSING_REQUIRED_ATTRIBUTE: return L"WEB_E_MISSING_REQUIRED_ATTRIBUTE";
case WEB_E_UNEXPECTED_CONTENT: return L"WEB_E_UNEXPECTED_CONTENT";
case WEB_E_RESOURCE_TOO_LARGE: return L"WEB_E_RESOURCE_TOO_LARGE";
case WEB_E_INVALID_JSON_STRING: return L"WEB_E_INVALID_JSON_STRING";
case WEB_E_INVALID_JSON_NUMBER: return L"WEB_E_INVALID_JSON_NUMBER";
case WEB_E_JSON_VALUE_NOT_FOUND: return L"WEB_E_JSON_VALUE_NOT_FOUND";
case HTTP_E_STATUS_UNEXPECTED: return L"HTTP_E_STATUS_UNEXPECTED";
case HTTP_E_STATUS_UNEXPECTED_REDIRECTION: return L"HTTP_E_STATUS_UNEXPECTED_REDIRECTION";
case HTTP_E_STATUS_UNEXPECTED_CLIENT_ERROR: return L"HTTP_E_STATUS_UNEXPECTED_CLIENT_ERROR";
case HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR: return L"HTTP_E_STATUS_UNEXPECTED_SERVER_ERROR";
case HTTP_E_STATUS_AMBIGUOUS: return L"HTTP_E_STATUS_AMBIGUOUS";
case HTTP_E_STATUS_MOVED: return L"HTTP_E_STATUS_MOVED";
case HTTP_E_STATUS_REDIRECT: return L"HTTP_E_STATUS_REDIRECT";
case HTTP_E_STATUS_REDIRECT_METHOD: return L"HTTP_E_STATUS_REDIRECT_METHOD";
case HTTP_E_STATUS_NOT_MODIFIED: return L"HTTP_E_STATUS_NOT_MODIFIED";
case HTTP_E_STATUS_USE_PROXY: return L"HTTP_E_STATUS_USE_PROXY";
case HTTP_E_STATUS_REDIRECT_KEEP_VERB: return L"HTTP_E_STATUS_REDIRECT_KEEP_VERB";
case HTTP_E_STATUS_BAD_REQUEST: return L"HTTP_E_STATUS_BAD_REQUEST";
case HTTP_E_STATUS_DENIED: return L"HTTP_E_STATUS_DENIED";
case HTTP_E_STATUS_PAYMENT_REQ: return L"HTTP_E_STATUS_PAYMENT_REQ";
case HTTP_E_STATUS_FORBIDDEN: return L"HTTP_E_STATUS_FORBIDDEN";
case HTTP_E_STATUS_NOT_FOUND: return L"HTTP_E_STATUS_NOT_FOUND";
case HTTP_E_STATUS_BAD_METHOD: return L"HTTP_E_STATUS_BAD_METHOD";
case HTTP_E_STATUS_NONE_ACCEPTABLE: return L"HTTP_E_STATUS_NONE_ACCEPTABLE";
case HTTP_E_STATUS_PROXY_AUTH_REQ: return L"HTTP_E_STATUS_PROXY_AUTH_REQ";
case HTTP_E_STATUS_REQUEST_TIMEOUT: return L"HTTP_E_STATUS_REQUEST_TIMEOUT";
case HTTP_E_STATUS_CONFLICT: return L"HTTP_E_STATUS_CONFLICT";
case HTTP_E_STATUS_GONE: return L"HTTP_E_STATUS_GONE";
case HTTP_E_STATUS_LENGTH_REQUIRED: return L"HTTP_E_STATUS_LENGTH_REQUIRED";
case HTTP_E_STATUS_PRECOND_FAILED: return L"HTTP_E_STATUS_PRECOND_FAILED";
case HTTP_E_STATUS_REQUEST_TOO_LARGE: return L"HTTP_E_STATUS_REQUEST_TOO_LARGE";
case HTTP_E_STATUS_URI_TOO_LONG: return L"HTTP_E_STATUS_URI_TOO_LONG";
case HTTP_E_STATUS_UNSUPPORTED_MEDIA: return L"HTTP_E_STATUS_UNSUPPORTED_MEDIA";
case HTTP_E_STATUS_RANGE_NOT_SATISFIABLE: return L"HTTP_E_STATUS_RANGE_NOT_SATISFIABLE";
case HTTP_E_STATUS_EXPECTATION_FAILED: return L"HTTP_E_STATUS_EXPECTATION_FAILED";
case HTTP_E_STATUS_SERVER_ERROR: return L"HTTP_E_STATUS_SERVER_ERROR";
case HTTP_E_STATUS_NOT_SUPPORTED: return L"HTTP_E_STATUS_NOT_SUPPORTED";
case HTTP_E_STATUS_BAD_GATEWAY: return L"HTTP_E_STATUS_BAD_GATEWAY";
case HTTP_E_STATUS_SERVICE_UNAVAIL: return L"HTTP_E_STATUS_SERVICE_UNAVAIL";
case HTTP_E_STATUS_GATEWAY_TIMEOUT: return L"HTTP_E_STATUS_GATEWAY_TIMEOUT";
case HTTP_E_STATUS_VERSION_NOT_SUP: return L"HTTP_E_STATUS_VERSION_NOT_SUP";
// WinINet specific errors
case INET_E_INVALID_URL: return L"INET_E_INVALID_URL";
case INET_E_NO_SESSION: return L"INET_E_NO_SESSION";
case INET_E_CANNOT_CONNECT: return L"INET_E_CANNOT_CONNECT";
case INET_E_RESOURCE_NOT_FOUND: return L"INET_E_RESOURCE_NOT_FOUND";
case INET_E_OBJECT_NOT_FOUND: return L"INET_E_OBJECT_NOT_FOUND";
case INET_E_DATA_NOT_AVAILABLE: return L"INET_E_DATA_NOT_AVAILABLE";
case INET_E_DOWNLOAD_FAILURE: return L"INET_E_DOWNLOAD_FAILURE";
case INET_E_AUTHENTICATION_REQUIRED: return L"INET_E_AUTHENTICATION_REQUIRED";
case INET_E_NO_VALID_MEDIA: return L"INET_E_NO_VALID_MEDIA";
case INET_E_CONNECTION_TIMEOUT: return L"INET_E_CONNECTION_TIMEOUT";
case INET_E_INVALID_REQUEST: return L"INET_E_INVALID_REQUEST";
case INET_E_UNKNOWN_PROTOCOL: return L"INET_E_UNKNOWN_PROTOCOL";
case INET_E_SECURITY_PROBLEM: return L"INET_E_SECURITY_PROBLEM";
case INET_E_CANNOT_LOAD_DATA: return L"INET_E_CANNOT_LOAD_DATA";
case INET_E_CANNOT_INSTANTIATE_OBJECT: return L"INET_E_CANNOT_INSTANTIATE_OBJECT";
case INET_E_INVALID_CERTIFICATE: return L"INET_E_INVALID_CERTIFICATE";
case INET_E_REDIRECT_FAILED: return L"INET_E_REDIRECT_FAILED";
case INET_E_REDIRECT_TO_DIR: return L"INET_E_REDIRECT_TO_DIR";
}
return L"Unknown error";
}
void
Utils::LogExceptionDebugInfo(
__in HRESULT hr,
__in_opt PCWSTR pwszFunction,
__in_opt PCWSTR pwszFile,
__in uint32 uLine
)
{
if( Configuration::IsAtDiagnosticsTraceLevel(XboxNetworkMeshDiagnosticsTraceLevel::Error) )
{
std::wstring info = L"[Exception]: HRESULT: ";
info += Utils::ConvertHResultToString(hr)->Data();
info += L"\n";
if( pwszFunction != nullptr )
{
info += L"\t\tFunction: ";
info += pwszFunction;
info += L"\n";
}
if( pwszFile != nullptr )
{
info += L"\t\tFile:";
info += pwszFile;
info += L"(";
info += uLine.ToString()->Data();
info += L")\n";
}
OutputDebugString( info.c_str() );
Configuration::RaiseDebugOutput( ref new Platform::String(info.c_str()) );
}
}
Platform::String^ Utils::FormatString( LPCWSTR strMsg, ... )
{
WCHAR strBuffer[2048];
va_list args;
va_start(args, strMsg);
_vsnwprintf_s( strBuffer, 2048, _TRUNCATE, strMsg, args );
strBuffer[2047] = L'\0';
va_end(args);
Platform::String^ str = ref new Platform::String(strBuffer);
return str;
}
Platform::String^ Utils::GetErrorString( HRESULT hr )
{
Platform::String^ str = Utils::FormatString(L" %s [0x%0.8x]", ConvertHResultToErrorName(hr)->Data(), hr );
return str;
}
void Utils::GetBufferBytes( __in Windows::Storage::Streams::IBuffer^ buffer, __out byte** ppOut )
{
if( ppOut == nullptr || buffer == nullptr )
{
throw ref new Platform::InvalidArgumentException();
}
*ppOut = nullptr;
Microsoft::WRL::ComPtr<IInspectable> srcBufferInspectable(reinterpret_cast<IInspectable*>( buffer ));
Microsoft::WRL::ComPtr<Windows::Storage::Streams::IBufferByteAccess> srcBufferByteAccess;
srcBufferInspectable.As(&srcBufferByteAccess);
srcBufferByteAccess->Buffer(ppOut);
}
Platform::String^ Utils::PrintSocketAddress(
_In_ UINT32 sockaddrSize,
_In_ const SOCKADDR* sockaddr
)
{
int result;
char hostname[256] = {0};
char port[64] = {0};
ZeroMemory(hostname, sizeof(hostname));
result = getnameinfo(sockaddr,
sockaddrSize,
hostname,
sizeof(hostname),
port,
sizeof( port ),
(NI_NUMERICHOST | NI_NUMERICSERV));
if (result == 0)
{
return Utils::FormatString(
L"[%hs]:%hs",
hostname,
port
);
}
else
{
result = WSAGetLastError();
return Utils::FormatString( L"PrintSocketAddress: %d", result );
}
}
Platform::String^ Utils::PrintSecureDeviceAssociation(
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
bool includeLocal,
bool includeRemote
)
{
if( association == nullptr )
{
return L"null";
}
try
{
Platform::String^ textLocal;
if( includeLocal )
{
SOCKADDR_STORAGE localSocketAddress;
Platform::ArrayReference<BYTE> localSocketAddressBytesFromAssociationInTemplate(
(BYTE*) &localSocketAddress,
sizeof(localSocketAddress));
association->GetLocalSocketAddressBytes(localSocketAddressBytesFromAssociationInTemplate);
textLocal = PrintSocketAddress( sizeof(localSocketAddress), (SOCKADDR*) &localSocketAddress );
}
Platform::String^ textRemote;
if( includeRemote )
{
SOCKADDR_STORAGE remoteSocketAddress;
Platform::ArrayReference<BYTE> remoteSocketAddressBytesFromAssociationInTemplate(
(BYTE*) &remoteSocketAddress,
sizeof(remoteSocketAddress));
association->GetRemoteSocketAddressBytes(remoteSocketAddressBytesFromAssociationInTemplate);
textRemote = PrintSocketAddress( sizeof(remoteSocketAddress), (SOCKADDR*) &remoteSocketAddress );
}
if( includeLocal && includeRemote )
{
return Utils::FormatString( L"Local:%s Remote:%s", textLocal->Data(), textRemote->Data());
}
else
{
return Utils::FormatString( L"%s%s", textLocal->Data(), textRemote->Data());
}
}
catch (...)
{
return L"Failure printing address";
}
}
Platform::String^ Utils::GetThreadDescription( Platform::String^ desc )
{
Platform::String^ apartmentType = GetApartmentTypeString();
DWORD dwThreadId = GetCurrentThreadId();
return Utils::FormatString( L"%s: ThreadID:%d [%s]", desc->Data(), dwThreadId, apartmentType->Data() );
}
Platform::String^ Utils::GetApartmentTypeString()
{
APTTYPE at;
APTTYPEQUALIFIER atq;
::CoGetApartmentType(&at, &atq);
Platform::String^ desc;
switch (at)
{
case APTTYPE_CURRENT: desc = L"CURRENT"; break;
case APTTYPE_STA: desc = L"STA"; break;
case APTTYPE_MTA: desc = L"MTA"; break;
case APTTYPE_NA: desc = L"NA"; break;
case APTTYPE_MAINSTA: desc = L"MAINSTA"; break;
default: desc = L"UNKNOWN"; break;
}
switch (atq)
{
case APTTYPEQUALIFIER_NONE: desc += L" NONE"; break;
case APTTYPEQUALIFIER_IMPLICIT_MTA: desc += L" IMPLICIT_MTA"; break;
case APTTYPEQUALIFIER_NA_ON_MTA: desc += L" NA_ON_MTA"; break;
case APTTYPEQUALIFIER_NA_ON_STA: desc += L" NA_ON_STA"; break;
case APTTYPEQUALIFIER_NA_ON_IMPLICIT_MTA: desc += L" NA_ON_IMPLICIT_MTA"; break;
case APTTYPEQUALIFIER_NA_ON_MAINSTA: desc += L" NA_ON_MAINSTA"; break;
default: desc += L" UNKNOWN"; break;
}
return desc;
}
@@ -0,0 +1,223 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#pragma once
namespace Microsoft { namespace Xbox { namespace Samples { namespace NetworkMesh {
enum WebErrorStatus
{
WebErrorStatus_Unknown = 0,
WebErrorStatus_CertificateCommonNameIsIncorrect = 1,
WebErrorStatus_CertificateExpired = 2,
WebErrorStatus_CertificateContainsErrors = 3,
WebErrorStatus_CertificateRevoked = 4,
WebErrorStatus_CertificateIsInvalid = 5,
WebErrorStatus_ServerUnreachable = 6,
WebErrorStatus_Timeout = 7,
WebErrorStatus_ErrorHttpInvalidServerResponse = 8,
WebErrorStatus_ConnectionAborted = 9,
WebErrorStatus_ConnectionReset = 10,
WebErrorStatus_Disconnected = 11,
WebErrorStatus_HttpToHttpsOnRedirection = 12,
WebErrorStatus_HttpsToHttpOnRedirection = 13,
WebErrorStatus_CannotConnect = 14,
WebErrorStatus_HostNameNotResolved = 15,
WebErrorStatus_OperationCanceled = 16,
WebErrorStatus_RedirectFailed = 17,
WebErrorStatus_UnexpectedStatusCode = 18,
WebErrorStatus_UnexpectedRedirection = 19,
WebErrorStatus_UnexpectedClientError = 20,
WebErrorStatus_UnexpectedServerError = 21,
WebErrorStatus_MultipleChoices = 300,
WebErrorStatus_MovedPermanently = 301,
WebErrorStatus_Found = 302,
WebErrorStatus_SeeOther = 303,
WebErrorStatus_NotModified = 304,
WebErrorStatus_UseProxy = 305,
WebErrorStatus_TemporaryRedirect = 307,
WebErrorStatus_BadRequest = 400,
WebErrorStatus_Unauthorized = 401,
WebErrorStatus_PaymentRequired = 402,
WebErrorStatus_Forbidden = 403,
WebErrorStatus_NotFound = 404,
WebErrorStatus_MethodNotAllowed = 405,
WebErrorStatus_NotAcceptable = 406,
WebErrorStatus_ProxyAuthenticationRequired = 407,
WebErrorStatus_RequestTimeout = 408,
WebErrorStatus_Conflict = 409,
WebErrorStatus_Gone = 410,
WebErrorStatus_LengthRequired = 411,
WebErrorStatus_PreconditionFailed = 412,
WebErrorStatus_RequestEntityTooLarge = 413,
WebErrorStatus_RequestUriTooLong = 414,
WebErrorStatus_UnsupportedMediaType = 415,
WebErrorStatus_RequestedRangeNotSatisfiable = 416,
WebErrorStatus_ExpectationFailed = 417,
WebErrorStatus_InternalServerError = 500,
WebErrorStatus_NotImplemented = 501,
WebErrorStatus_BadGateway = 502,
WebErrorStatus_ServiceUnavailable = 503,
WebErrorStatus_GatewayTimeout = 504,
WebErrorStatus_HttpVersionNotSupported = 505
};
private class Utils
{
public:
static inline bool IsNullOrEmptyString(__in_opt LPCWSTR pcwsz)
{
return (pcwsz == NULL) || (pcwsz[0] == L'\0');
}
// Searches for a pattern in the source string and replace all occurrences of it with another string.
// Pattern must be non-empty
// Replacement can be empty, in which case all occurrences of Pattern are deleted
static std::wstring&
Replace(
__inout std::wstring& strSource,
__in PCWSTR pwszPattern,
__in_opt PCWSTR pwszReplacement,
__out_opt size_t* pnOccurrencesReplaced = nullptr
);
static std::vector<std::wstring>
StringSplit(
__in const std::wstring& string,
__in WCHAR seperator
);
static Platform::String^
DateTimeToString(
__in Windows::Foundation::DateTime dateTime
);
static Platform::String^
RemoveBracesFromGuidString(
__in Platform::String^ guidString
);
static Windows::Foundation::TimeSpan
ConvertSecondsToTimeSpan(
__in uint32 seconds
);
static Windows::Foundation::TimeSpan
ConvertMillisecondsToTimeSpan(
__in uint64 milliseconds
);
static uint32
ConvertTimeSpanToSeconds(
__in Windows::Foundation::TimeSpan timespan
);
static int64
ConvertTimeSpanToMilliseconds(
__in Windows::Foundation::TimeSpan timespan
);
static uint32
ConvertTimeSpanToDays(
__in Windows::Foundation::TimeSpan timespan
);
static Windows::Foundation::TimeSpan GetCurrentTime();
static Platform::String^
ConvertHResultToString( HRESULT hr );
static Platform::String^
ConvertHResultToErrorName( HRESULT hr );
static void
LogExceptionDebugInfo(
__in HRESULT hr,
__in_opt PCWSTR pwszFunction,
__in_opt PCWSTR pwszFile,
__in uint32 uLine
);
static Platform::String^ FormatString( LPCWSTR strMsg, ... );
static Platform::String^ GetErrorString( HRESULT hr );
static void GetBufferBytes( __in Windows::Storage::Streams::IBuffer^ buffer, __out byte** ppOut );
static Platform::String^ PrintSocketAddress(
_In_ UINT32 sockaddrSize,
_In_ const SOCKADDR* sockaddr
);
static Platform::String^ PrintSecureDeviceAssociation(
Windows::Xbox::Networking::SecureDeviceAssociation^ association,
bool includeLocal,
bool includeRemote
);
static Platform::String^ GetThreadDescription( Platform::String^ desc );
private:
static Platform::String^ GetApartmentTypeString();
};
template<typename T>
static std::basic_string<T>&
ReplaceSubstring(
__inout std::basic_string<T>& strSource,
__in_ecount_z(nPatternLength+1) const T* pszPattern,
__in const size_t nPatternLength,
__in_z const T* pszReplacement,
__out_opt size_t* pnOccurrencesReplaced
)
{
THROW_INVALIDARGUMENT_IF_NULL( pszPattern );
THROW_INVALIDARGUMENT_IF_NULL( pszReplacement );
if ( pnOccurrencesReplaced != nullptr )
{
*pnOccurrencesReplaced = 0;
}
size_t nReplaced = 0;
if ( nPatternLength > 0 )
{
// Search the string backward for the given pattern first
size_t nPos = strSource.rfind( pszPattern );
while ( nPos != std::basic_string<T>::npos )
{
strSource.replace( nPos, nPatternLength, pszReplacement );
++nReplaced;
// Find the given pattern first
if ( nPos == 0 )
{
// There is nothing left to look at, break
break;
}
// Find the next match starting from the last replaced position
nPos = strSource.rfind( pszPattern, nPos - 1 );
}
}
if ( pnOccurrencesReplaced != nullptr )
{
*pnOccurrencesReplaced = nReplaced;
}
return strSource;
}
}}}}
@@ -0,0 +1,740 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
// Functions to convert dates back and forth to the ISO8601 format
// http://www.iso.org/iso/en/prods-services/popstds/datesandtime.html
#include "pch.h"
#include "ISO8601.h"
#define ISO8601_MAX_USED_CCH 26 // Max amount of characters when generating ISO 8601 strings: YYYY-MM-DDThh:mm:ss.ssssZ + terminating zero
// This table defines different "types" of characters for use as the columns
// of the state table:
// 0 - invalid character
// 1 - number
// 2 - '-'
// 3 - date-time separator ('T', 't' and ' ')
// 4 - ':'
// 5 - UTC zone ('Z' and 'z')
// 6 - '+'
// 7 - second-fraction separator ('.' and ',')
static const unsigned char iso8601chartable[256] =
{
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 (00 - 0f)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 (10 - 1f)
3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 2, 7, 0, // 2 (20 - 2f)
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 0, 0, 0, 0, // 3 (30 - 3f)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 (40 - 4f)
0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, // 5 (50 - 5f)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 (60 - 6f)
0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, // 7 (70 - 7f)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 (80 - 8f)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 (90 - 9f)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a (a0 - af)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // b (b0 - bf)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c (c0 - cf)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // d (d0 - df)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e (e0 - ef)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // f (f0 - ff)
};
// Parsing state table is made up of WORD values with this meaning:
//
// | action | | next state |
// |_|_|_|_|_|_|_|_| |_|_|_|_|_|_|_|_|
// HIBYTE LOBYTE
#define STENTRY WORD
#define S(a,b) MAKEWORD(b,a)
#define ACTION(a) HIBYTE(a)
#define STATE(a) LOBYTE(a)
// Actions
#define _OK_ 0x00 // valid character
#define _NXT 0x01 // end of a segment, move to next
#define _TZM 0x02 // starting '-' time zone offset
#define _TZP 0x03 // starting '+' time zone offset
#define _MSC 0x04 // millisecond digit
#define _TZU 0x05 // time zone UTC
#define _ERR 0x80 // error, invalid character
#define ___ERROR____ MAKEWORD(0x00, _ERR)
// State table
#define STATE_TABLE_DIM 8
static const STENTRY iso8601StateTable[][STATE_TABLE_DIM] =
{
// unknown number '-' 'T' ':' 'Z' '+' '.'
___ERROR____, S(_OK_,0x01), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x00 year
___ERROR____, S(_OK_,0x02), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x01
___ERROR____, S(_OK_,0x03), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x02
___ERROR____, S(_NXT,0x04), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x03
___ERROR____, S(_OK_,0x06), S(_OK_,0x05), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x04 month
___ERROR____, S(_OK_,0x06), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x05
___ERROR____, S(_NXT,0x07), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x06
___ERROR____, S(_OK_,0x09), S(_OK_,0x08), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x07 day
___ERROR____, S(_OK_,0x09), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x08
___ERROR____, S(_NXT,0x0a), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x09
___ERROR____, S(_OK_,0x0c), ___ERROR____, S(_OK_,0x0b), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0a hour
___ERROR____, S(_OK_,0x0c), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0b
___ERROR____, S(_NXT,0x0d), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0c
___ERROR____, S(_OK_,0x0f), S(_TZM,0x15), ___ERROR____, S(_OK_,0x0e), S(_TZU,0x1b), S(_TZP,0x15), ___ERROR____, //0x0d min
___ERROR____, S(_OK_,0x0f), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0e
___ERROR____, S(_NXT,0x10), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0f
___ERROR____, S(_OK_,0x12), S(_TZM,0x15), ___ERROR____, S(_OK_,0x11), S(_TZU,0x1b), S(_TZP,0x15), ___ERROR____, //0x10 sec
___ERROR____, S(_OK_,0x12), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x11
___ERROR____, S(_NXT,0x13), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x12
___ERROR____, ___ERROR____, S(_TZM,0x15), ___ERROR____, ___ERROR____, S(_TZU,0x1b), S(_TZP,0x15), S(_OK_,0x14), //0x13 '.' or 'Z' or '+/-'
___ERROR____, S(_MSC,0x14), S(_TZM,0x15), ___ERROR____, ___ERROR____, S(_TZU,0x1b), S(_TZP,0x15), ___ERROR____, //0x14 fragment of a second
___ERROR____, S(_OK_,0x16), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x15 TZ offset - hour
___ERROR____, S(_NXT,0x17), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x16
___ERROR____, S(_OK_,0x19), ___ERROR____, ___ERROR____, S(_OK_,0x18), ___ERROR____, ___ERROR____, ___ERROR____, //0x17 TZ offset - minute
___ERROR____, S(_OK_,0x19), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x18
___ERROR____, S(_NXT,0x1a), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x19
___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x1a offset done - parsing done
___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x1b UTC zone done - parsing done
};
#define IsLeapYear(YEARS) ( \
(((YEARS) % 400 == 0) || \
((YEARS) % 100 != 0) && ((YEARS) % 4 == 0)) ? \
TRUE \
: \
FALSE \
)
static HRESULT _GetNumDaysForYearMonth(
WORD wYear,
WORD wMonth,
__out WORD *pwDays)
{
HRESULT hr = S_OK;
if (NULL == pwDays ||
wMonth < 1 || wMonth > 12 ||
wYear < 1601 || wYear > 9999)
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
static const WORD s_wNumDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
*pwDays = s_wNumDays[wMonth - 1];
// Check for leap year
if ((wMonth == 2) && IsLeapYear(wYear))
{
(*pwDays)++;
}
}
return hr;
}
#define ONE_SECOND 10000000ui64 // number of 100-nanosecond intervals in a second
#define ONE_MINUTE (ONE_SECOND * 60) // number of 100-nanosecond intervals in a minute
#define ONE_HOUR (ONE_MINUTE * 60) // number of 100-nanosecond intervals in an hour
typedef union tagTU
{
FILETIME ft;
ULARGE_INTEGER ui;
} TU;
static HRESULT _AddTZOffset(
Iso8601ParsingStage ips,
WORD wValue,
int iTZDirection,
__inout SYSTEMTIME *pSysTime)
{
HRESULT hr = S_OK;
_ASSERTE((ips == IPS_TZHOUR && wValue < 24) || (ips == IPS_TZMINUTE && wValue < 60));
_ASSERTE(iTZDirection == 1 || iTZDirection == -1);
// Convert SYSTEMTIME to FILETIME to perform date-time arithmetic
TU ftTime = {0};
if (SystemTimeToFileTime(pSysTime, &ftTime.ft))
{
ULARGE_INTEGER ulOffset;
ulOffset.QuadPart = (ips == IPS_TZHOUR) ? ONE_HOUR * wValue : ONE_MINUTE * wValue;
if (iTZDirection > 0)
{
ftTime.ui.QuadPart += ulOffset.QuadPart;
}
else
{
ftTime.ui.QuadPart -= ulOffset.QuadPart;
}
if (!FileTimeToSystemTime(&ftTime.ft, pSysTime))
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
else
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
return hr;
}
static HRESULT _CheckValueAndAddToSysTime(
Iso8601ParsingStage ips,
WORD wValue,
WORD wMSDigits,
int iTZDirection,
__inout SYSTEMTIME *pSysTime)
{
HRESULT hr = S_OK;
WORD wHiLimit = 0;
WORD wLoLimit = 0;
WORD *pDateWord = NULL;
_ASSERTE((iTZDirection == 0 && ips != IPS_TZHOUR && ips != IPS_TZMINUTE) ||
(iTZDirection != 0 && (ips == IPS_TZHOUR || ips == IPS_TZMINUTE)));
_ASSERTE((wMSDigits == 0 && ips != IPS_MILLISECOND) ||
(wMSDigits > 0 && wMSDigits < 4 && ips == IPS_MILLISECOND));
switch (ips)
{
case IPS_YEAR:
wLoLimit = 1601;
wHiLimit = 9999;
pDateWord = (WORD *) &(pSysTime->wYear);
break;
case IPS_MONTH:
wLoLimit = 1;
wHiLimit = 12;
pDateWord = (WORD *) &(pSysTime->wMonth);
break;
case IPS_DAY:
wLoLimit = 1;
hr = _GetNumDaysForYearMonth(pSysTime->wYear, pSysTime->wMonth, &wHiLimit);
_ASSERTE(SUCCEEDED(hr)); // internal call, should never fail
pDateWord = (WORD *) &(pSysTime->wDay);
break;
case IPS_HOUR:
wLoLimit = 0;
wHiLimit = 23;
pDateWord = (WORD *) &(pSysTime->wHour);
break;
case IPS_MINUTE:
wLoLimit = 0;
wHiLimit = 59;
pDateWord = (WORD *) &(pSysTime->wMinute);
break;
case IPS_SECOND:
wLoLimit = 0;
wHiLimit = 59;
pDateWord = (WORD *) &(pSysTime->wSecond);
break;
case IPS_MILLISECOND:
wLoLimit = 0;
wHiLimit = 999;
while (wMSDigits++ < 3)
{
wValue *= 10;
}
pDateWord = (WORD *) &(pSysTime->wMilliseconds);
break;
case IPS_TZHOUR:
wLoLimit = 0;
wHiLimit = 23; // valid offsets appear to be -12 to +14, but we'll allow any valid hour value
pDateWord = (WORD *) &(pSysTime->wHour);
break;
case IPS_TZMINUTE:
wLoLimit = 0;
wHiLimit = 59;
pDateWord = (WORD *) &(pSysTime->wMinute);
break;
default:
hr = E_UNEXPECTED;
break;
}
if (SUCCEEDED(hr))
{
_ASSERTE(NULL != pDateWord);
if (wValue < wLoLimit || wValue > wHiLimit)
{
hr = E_ABORT;
}
else
{
_ASSERTE(*pDateWord == 0 || ips >= IPS_MILLISECOND);
if (ips != IPS_TZHOUR && ips != IPS_TZMINUTE)
{
*pDateWord = wValue;
}
else
{
_ASSERTE(iTZDirection == 1 || iTZDirection == -1);
// handle offset rollover - can affect entire date
hr = _AddTZOffset(ips, wValue, iTZDirection, pSysTime);
}
}
}
return hr;
}
static HRESULT _iso8601ToSysTime(
const char *pszisoDate,
__out SYSTEMTIME *pSysTime,
__out Iso8601ParsingStage *pips)
{
HRESULT hr = S_OK;
if (NULL == pszisoDate ||
NULL == pSysTime ||
NULL == pips ||
*pszisoDate == '\0')
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
ZeroMemory(pSysTime, sizeof(SYSTEMTIME));
*pips = IPS_INVALID;
BYTE action = 0;
BYTE state = 0;
WORD DateWord = 0;
Iso8601ParsingStage ips = IPS_YEAR;
int iTZDirection = 0;
WORD wMSDigits = 0;
// Main state machine loop
while(*pszisoDate && SUCCEEDED(hr))
{
unsigned char code = iso8601chartable[*pszisoDate];
// Prevent overflows - sanity check
if ((code >= STATE_TABLE_DIM) ||
(state >= ARRAYSIZE(iso8601StateTable)))
{
hr = E_UNEXPECTED;
break;
}
STENTRY stValue = iso8601StateTable[state][code];
state = STATE(stValue);
action = ACTION(stValue);
switch(action)
{
case _OK_: // input OK, valid character
case _NXT: // finish piece and advance to next stage, valid character
{
if (code == 1)
{
DateWord = (DateWord * 10) + (*pszisoDate - '0');
}
if (action == _NXT)
{
hr = _CheckValueAndAddToSysTime(ips, DateWord, 0 /*wMSDigits*/, iTZDirection, pSysTime);
DateWord = 0;
if (SUCCEEDED(hr))
{
*pips = ips;
ips = Iso8601ParsingStage(int(ips) + 1);
}
}
}
break;
case _TZM: // start '-' timezone offset
iTZDirection = 1;
DateWord = 0;
*pips = ips;
ips = IPS_TZHOUR;
break;
case _TZP: // start '+' timezone offset
iTZDirection = -1;
DateWord = 0;
*pips = ips;
ips = IPS_TZHOUR;
break;
case _MSC: // process millisecond digit
_ASSERTE(code == 1 && ips == IPS_MILLISECOND);
wMSDigits++;
if (wMSDigits < 4)
{
DateWord = (DateWord * 10) + (*pszisoDate - '0');
hr = _CheckValueAndAddToSysTime(ips, DateWord, wMSDigits, iTZDirection, pSysTime);
if (SUCCEEDED(hr))
{
*pips = IPS_MILLISECOND;
}
}
break;
case _TZU: // 'Z' for UTC zone
*pips = IPS_TZUTC;
ips = IPS_TZUTC;
break;
case _ERR: // error, invalid character
_ASSERTE(state == 0x00);
hr = E_ABORT;
break;
default:
hr = E_UNEXPECTED;
break;
}
pszisoDate++;
}
if (SUCCEEDED(hr) && *pszisoDate == '\0')
{
if (action != _NXT && action != _MSC && action != _TZU)
{
hr = E_ABORT;
}
}
if (hr != S_OK && hr != E_ABORT)
{
*pips = IPS_INVALID;
}
}
_ASSERTE(hr != E_UNEXPECTED);
return hr;
}
static HRESULT _iso8601ToFileTime(
const char *pszisoDate,
__out FILETIME *pftTime,
__out Iso8601ParsingStage *pips)
{
HRESULT hr = S_OK;
SYSTEMTIME stTime = {0};
hr = _iso8601ToSysTime(pszisoDate, &stTime, pips);
if (SUCCEEDED(hr))
{
if (!SystemTimeToFileTime(&stTime, pftTime))
{
hr = E_FAIL;
}
}
return hr;
}
static HRESULT _SysTimeToiso8601(
__in SYSTEMTIME *pstTime,
__in BOOL fGeneratePartial,
__out_ecount(cch) char *pszBuf,
__in ULONG cch,
__in BOOL fUseShortTimeFormat = FALSE
)
{
HRESULT hr = S_OK;
if (NULL == pstTime ||
NULL == pszBuf ||
cch < ISO8601_MAX_USED_CCH)
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
if (pstTime->wYear < 1601 || // FILETIME cannot handle less
pstTime->wYear > 9999 || // ISO8601 has four digits for a year
pstTime->wMonth > 12 ||
pstTime->wDay > 31 ||
pstTime->wHour > 24 ||
pstTime->wMinute > 59 ||
pstTime->wSecond > 59 ||
pstTime->wMilliseconds > 999)
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
WORD wDays = 0;
hr = _GetNumDaysForYearMonth(pstTime->wYear, pstTime->wMonth, &wDays);
if (SUCCEEDED(hr) && pstTime->wDay > wDays)
{
hr = E_INVALIDARG;
}
}
}
if (SUCCEEDED(hr))
{
pszBuf[0] = static_cast<char>((pstTime->wYear / 1000) + '0');
pszBuf[1] = static_cast<char>(((pstTime->wYear / 100) % 10) + '0');
pszBuf[2] = static_cast<char>(((pstTime->wYear / 10) % 10) + '0');
pszBuf[3] = static_cast<char>(((pstTime->wYear) % 10) + '0');
if (pstTime->wMonth > 0 ||
!fGeneratePartial)
{
pszBuf[4] = '-';
pszBuf[5] = static_cast<char>((pstTime->wMonth / 10) + '0');
pszBuf[6] = static_cast<char>((pstTime->wMonth % 10) + '0');
if (pstTime->wDay > 0 ||
!fGeneratePartial)
{
pszBuf[7] = '-';
pszBuf[8] = static_cast<char>((pstTime->wDay / 10) + '0');
pszBuf[9] = static_cast<char>((pstTime->wDay % 10) + '0');
if (pstTime->wHour != 0 ||
pstTime->wMinute != 0 ||
pstTime->wSecond != 0 ||
pstTime->wMilliseconds != 0 ||
!fGeneratePartial)
{
pszBuf[10] = 'T';
pszBuf[11] = static_cast<char>(pstTime->wHour / 10 + '0');
pszBuf[12] = static_cast<char>((pstTime->wHour % 10) + '0');
pszBuf[13] = ':';
pszBuf[14] = static_cast<char>(pstTime->wMinute / 10 + '0');
pszBuf[15] = static_cast<char>((pstTime->wMinute % 10) + '0');
pszBuf[16] = ':';
pszBuf[17] = static_cast<char>(pstTime->wSecond / 10 + '0');
pszBuf[18] = static_cast<char>((pstTime->wSecond % 10) + '0');
if ( !fUseShortTimeFormat && ( pstTime->wMilliseconds != 0 ) )
{
// YYYY-MM-DDThh:mm:ss.ssssZ
pszBuf[19] = '.';
pszBuf[20] = static_cast<char>(pstTime->wMilliseconds / 100 + '0');
pszBuf[21] = static_cast<char>(((pstTime->wMilliseconds / 10) % 10) + '0');
pszBuf[22] = static_cast<char>((pstTime->wMilliseconds % 10) + '0');
// pad the last digit of millisecond with 0
pszBuf[23] = '0';
pszBuf[24] = 'Z';
pszBuf[25] = 0;
}
else
{
// YYYY-MM-DDThh:mm:ssZ
pszBuf[19] = 'Z';
pszBuf[20] = 0;
}
}
else
{
// YYYY-MM-DD
pszBuf[10] = 0;
}
}
else
{
// YYYY-MM
pszBuf[7] = 0;
}
}
else
{
// YYYY
pszBuf[4] = 0;
}
}
return hr;
}
static HRESULT _FileTimeToiso8601(
const FILETIME *pftTime,
BOOL fGeneratePartial,
__out_ecount(cch) char *pszBuf,
ULONG cch,
BOOL fUseShortTimeFormat
)
{
HRESULT hr = S_OK;
SYSTEMTIME stTime = {0};
if (NULL == pftTime)
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
if (FileTimeToSystemTime( pftTime, &stTime))
{
hr = _SysTimeToiso8601( &stTime, fGeneratePartial, pszBuf, cch, fUseShortTimeFormat );
}
else
{
hr = E_FAIL;
}
}
return hr;
}
HRESULT FILETIMEToISO8601W(
__in const FILETIME* pft,
__in BOOL fGeneratePartial,
__out_ecount(cchISO8601) PWSTR pszISO8601,
__in size_t cchISO8601,
__in BOOL fUseShortTimeFormat
)
{
HRESULT hr = S_OK;
CHAR aszISO8601[ISO8601_MAX_USED_CCH];
hr = _FileTimeToiso8601( pft, fGeneratePartial, aszISO8601, ARRAYSIZE(aszISO8601), fUseShortTimeFormat );
if (SUCCEEDED(hr))
{
int cchSHAnsiToUnicode;
hr = SizeTToInt(cchISO8601, &cchSHAnsiToUnicode);
if (SUCCEEDED(hr))
{
hr = ( ISO8601_MAX_USED_CCH >= ::MultiByteToWideChar(
CP_ACP,
0,
aszISO8601,
-1,
pszISO8601, cchSHAnsiToUnicode) ) ? S_OK : E_UNEXPECTED;
_ASSERTE(SUCCEEDED(hr));
}
}
return hr;
}
HRESULT ISO8601ToFILETIMEW(
__in PCWSTR pszISO8601,
__out FILETIME* pft,
__out Iso8601ParsingStage *pips)
{
HRESULT hr = S_OK;
CHAR aszISO8601[ISO8601_MAX_CCH];
if( !::WideCharToMultiByte(
CP_ACP,
0,
pszISO8601,
-1,
aszISO8601,
ISO8601_MAX_CCH,
NULL,
NULL ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
if (SUCCEEDED(hr))
{
hr = _iso8601ToFileTime(aszISO8601, pft, pips);
}
return hr;
}
HRESULT SYSTEMTIMEToISO8601ExW(
__in const SYSTEMTIME* pst,
__in BOOL fGeneratePartial,
__out_ecount(cchISO8601) PWSTR pszISO8601,
__in size_t cchISO8601)
{
HRESULT hr = S_OK;
SYSTEMTIME st = *pst;
CHAR aszISO8601[ISO8601_MAX_USED_CCH];
hr = _SysTimeToiso8601(&st, fGeneratePartial, aszISO8601, ARRAYSIZE(aszISO8601));
if (SUCCEEDED(hr))
{
int cchSHAnsiToUnicode;
hr = SizeTToInt(cchISO8601, &cchSHAnsiToUnicode);
if (SUCCEEDED(hr))
{
hr = ( ISO8601_MAX_USED_CCH >= ::MultiByteToWideChar(
CP_ACP,
0,
aszISO8601,
-1,
pszISO8601,
cchSHAnsiToUnicode ) ) ? S_OK : E_UNEXPECTED;
_ASSERTE(SUCCEEDED(hr));
}
}
return hr;
}
HRESULT SYSTEMTIMEToISO8601W(
__in const SYSTEMTIME* pst,
__out_ecount(cchISO8601) PWSTR pszISO8601,
__in size_t cchISO8601)
{
return SYSTEMTIMEToISO8601ExW(pst, FALSE /*fGeneratePartial*/, pszISO8601, cchISO8601);
}
HRESULT ISO8601ToSYSTEMTIMEExW(
__in PCWSTR pszISO8601,
__out SYSTEMTIME* pst,
__out Iso8601ParsingStage *pips)
{
HRESULT hr = S_OK;
CHAR aszISO8601[ISO8601_MAX_CCH];
if( !::WideCharToMultiByte(
CP_ACP,
0,
pszISO8601,
-1,
aszISO8601,
ISO8601_MAX_CCH,
NULL,
NULL ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
if (SUCCEEDED(hr))
{
hr = _iso8601ToSysTime(aszISO8601, pst, pips);
}
return hr;
}
HRESULT ISO8601ToSYSTEMTIMEW(
__in PCWSTR pszISO8601,
__out SYSTEMTIME* pst)
{
HRESULT hr = S_OK;
Iso8601ParsingStage ips = IPS_INVALID;
hr = ISO8601ToSYSTEMTIMEExW(pszISO8601, pst, &ips);
// Fix up less-than-full-date
if (SUCCEEDED(hr) && ips < IPS_DAY)
{
if (ips < IPS_DAY)
{
pst->wDay = 1;
}
if (ips < IPS_MONTH)
{
pst->wMonth = 1;
}
hr = S_OK;
}
return hr;
}
@@ -0,0 +1,84 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#pragma once
// Functions to convert dates back and forth to the ISO8601 format
// (http://www.iso.org/iso/en/prods-services/popstds/datesandtime.html)
//
// Supported dates are from 1601-01-01 (SYSTEMTIME and FILETIME limitation) to
// 9999-12-31 (ISO8601 limitation)
// Longest form of ISO8601 is 40 chars + 1 for terminating zero
#define ISO8601_MAX_CCH 41
// Iso8601ParsingStage enum
//
// This enumeration is design so values can be compared.
// ISO8601 dates look like this: YYYY-MM-DDThh:mm:ss.sss+/-hh:mm
// So e.g. by parsing the date and then asking if parse stage was
// <IPS_HOUR you can tell if string contained any time at all, or just the date
enum Iso8601ParsingStage
{
IPS_INVALID = -1,
IPS_YEAR = 0,
IPS_MONTH,
IPS_DAY,
IPS_HOUR,
IPS_MINUTE,
IPS_SECOND,
IPS_MILLISECOND,
IPS_TZHOUR,
IPS_TZMINUTE,
IPS_TZUTC,
};
HRESULT
FILETIMEToISO8601W(
__in const FILETIME* pft,
__in BOOL fGeneratePartial,
__out_ecount(cchISO8601) PWSTR pszISO8601,
__in size_t cchISO8601,
__in BOOL fUseShortTimeFormat = FALSE
);
HRESULT
ISO8601ToFILETIMEW(
__in PCWSTR pszISO8601,
__out FILETIME* pft,
__out Iso8601ParsingStage* pips
);
HRESULT
SYSTEMTIMEToISO8601ExW(
__in const SYSTEMTIME* pst,
__in BOOL fGeneratePartial,
__out_ecount(cchISO8601) PWSTR pszISO8601,
__in size_t cchISO8601
);
HRESULT
SYSTEMTIMEToISO8601W(
__in const SYSTEMTIME* pst,
__out_ecount(cchISO8601) PWSTR pszISO8601,
__in size_t cchISO8601
);
HRESULT
ISO8601ToSYSTEMTIMEExW(
__in PCWSTR pszISO8601,
__out SYSTEMTIME* pst,
__out Iso8601ParsingStage* pips
);
HRESULT ISO8601ToSYSTEMTIMEW(
__in PCWSTR pszISO8601,
__out SYSTEMTIME* pst
);
@@ -0,0 +1,73 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#include "pch.h"
#include "Configuration.h"
#include "Utils.h"
using namespace Concurrency;
using namespace Platform;
using namespace std;
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
bool Configuration::s_enableDebugOutputEvents = false;
XboxNetworkMeshDiagnosticsTraceLevel Configuration::s_xboxNetworkMeshDiagnosticsTraceLevel = XboxNetworkMeshDiagnosticsTraceLevel::Off;
Concurrency::critical_section Configuration::s_writeLock;
bool Configuration::EnableDebugOutputEvents::get()
{
return s_enableDebugOutputEvents;
}
void Configuration::EnableDebugOutputEvents::set(bool value)
{
critical_section::scoped_lock lock(s_writeLock);
s_enableDebugOutputEvents = value;
}
XboxNetworkMeshDiagnosticsTraceLevel Configuration::DiagnosticsTraceLevel::get()
{
return Configuration::s_xboxNetworkMeshDiagnosticsTraceLevel;
}
void Configuration::DiagnosticsTraceLevel::set(XboxNetworkMeshDiagnosticsTraceLevel value)
{
critical_section::scoped_lock lock(s_writeLock);
THROW_INVALIDARGUMENT_IF(
value < XboxNetworkMeshDiagnosticsTraceLevel::Off ||
value > XboxNetworkMeshDiagnosticsTraceLevel::Verbose
);
Configuration::s_xboxNetworkMeshDiagnosticsTraceLevel = value;
}
void Configuration::RaiseDebugOutput(
__in Platform::String^ debugOutputString
)
{
if( Configuration::s_enableDebugOutputEvents &&
!debugOutputString->IsEmpty() )
{
DebugOutput(nullptr, debugOutputString);
}
}
bool Configuration::IsAtDiagnosticsTraceLevel(XboxNetworkMeshDiagnosticsTraceLevel level)
{
return (int)Configuration::s_xboxNetworkMeshDiagnosticsTraceLevel >= (int)level;
}
}}}}
@@ -0,0 +1,60 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#pragma once
#include "macros.h"
#include "XboxNetworkMeshDiagnosticsTraceLevel.h"
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
public ref class Configuration sealed
{
public:
/// <summary>
/// Registers for all DebugOutput notifications
/// </summary>
static event Windows::Foundation::EventHandler<Platform::String^>^ DebugOutput;
/// <summary>
/// Indicates if events should be generated for DebugOutput
/// </summary>
static property bool EnableDebugOutputEvents
{
bool get();
void set(bool value);
}
/// <summary>
/// Indicates the level of debug messages sent to the debugger's output window.
/// This property can be used to override the XboxLiveContextSettings::DiagnosticsTraceLevel
/// for all XboxLiveContexts. In addition, the setting is the only one that enables
/// tracing for internally thrown exceptions.
/// </summary>
static property XboxNetworkMeshDiagnosticsTraceLevel DiagnosticsTraceLevel
{
XboxNetworkMeshDiagnosticsTraceLevel get();
void set(XboxNetworkMeshDiagnosticsTraceLevel value);
}
internal:
static void RaiseDebugOutput( __in Platform::String^ debugOutputString );
static bool IsAtDiagnosticsTraceLevel( XboxNetworkMeshDiagnosticsTraceLevel level );
private:
static Concurrency::critical_section s_writeLock;
static bool s_enableDebugOutputEvents;
static Microsoft::Xbox::Samples::NetworkMesh::XboxNetworkMeshDiagnosticsTraceLevel s_xboxNetworkMeshDiagnosticsTraceLevel;
};
}}}}
@@ -0,0 +1,100 @@
//// 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 "MeshThread.h"
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
MeshThread::MeshThread(
UINT sendPeriodInMilliseconds,
uint32 threadAffinityMask,
int priorityClass ) :
m_sendEveryPeriodInMilliseconds(sendPeriodInMilliseconds),
m_threadAffinityMask(threadAffinityMask),
m_priorityClass(priorityClass),
m_terminateThreadEvent(nullptr),
m_threadHandle(nullptr),
m_wakeupEventHandle(nullptr)
{
m_wakeupEventHandle = CreateEvent( NULL, false, false, NULL );
InitializeCriticalSection(&m_threadManagementLock);
m_terminateThreadEvent = CreateEvent( NULL, false, false, NULL );
if ( !m_terminateThreadEvent )
{
throw E_UNEXPECTED;
}
m_threadHandle = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)MeshThread::StaticThreadProc, (LPVOID)this, CREATE_SUSPENDED, nullptr);
SetThreadPriority(m_threadHandle, m_priorityClass );
#ifdef _TITLE
SetThreadAffinityMask(m_threadHandle, m_threadAffinityMask);
#endif
ResumeThread(m_threadHandle);
}
MeshThread::~MeshThread()
{
Shutdown();
DeleteCriticalSection(&m_threadManagementLock);
}
UINT MeshThread::GetSendPeriod()
{
return m_sendEveryPeriodInMilliseconds;
}
void MeshThread::SetSendPeriod( UINT sendPeriodInMilliseconds)
{
m_sendEveryPeriodInMilliseconds = sendPeriodInMilliseconds;
m_clock.SetInterval(m_sendEveryPeriodInMilliseconds);
}
void MeshThread::Shutdown()
{
EnterCriticalSection(&m_threadManagementLock);
if( m_threadHandle != nullptr )
{
SetEvent(m_terminateThreadEvent);
WaitForSingleObject(m_threadHandle, INFINITE);
CloseHandle(m_threadHandle);
m_threadHandle = nullptr;
}
LeaveCriticalSection(&m_threadManagementLock);
}
DWORD WINAPI MeshThread::StaticThreadProc( MeshThread^ networkSendThread )
{
return networkSendThread->ThreadProc();
}
DWORD WINAPI MeshThread::ThreadProc()
{
m_clock.Initialize( m_sendEveryPeriodInMilliseconds );
static const UINT c_uOneSecondInMS = 1000;
LARGE_INTEGER m_timerFrequency;
QueryPerformanceFrequency(&m_timerFrequency);
while( m_clock.WaitForEventsOrHeartbeat( m_terminateThreadEvent, m_wakeupEventHandle ) != WAIT_OBJECT_0 )
{
auto args = ref new ProcessThreadsEventArgs();
OnDoWork(this, args);
}
return 0;
}
void MeshThread::WakeupThread()
{
SetEvent( m_wakeupEventHandle );
}
}}}}
@@ -0,0 +1,54 @@
//// 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
#pragma once
#include "Clock.h"
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
public ref class ProcessThreadsEventArgs sealed
{
public:
internal:
ProcessThreadsEventArgs(){}
};
public ref class MeshThread sealed
{
public:
event Windows::Foundation::EventHandler<ProcessThreadsEventArgs^>^ OnDoWork;
MeshThread( UINT sendPeriodInMilliseconds, uint32 threadAffinityMask, int priorityClass );
UINT GetSendPeriod( );
void SetSendPeriod( UINT sendPeriodInMilliseconds);
void WakeupThread();
virtual ~MeshThread();
internal:
void Shutdown();
static DWORD WINAPI StaticThreadProc(MeshThread^ networkSendThread);
DWORD ThreadProc();
private:
uint32 m_threadAffinityMask;
CRITICAL_SECTION m_threadManagementLock;
HANDLE m_terminateThreadEvent;
HANDLE m_threadHandle;
int m_priorityClass;
UINT m_sendEveryPeriodInMilliseconds;
HANDLE m_wakeupEventHandle;
Clock m_clock;
};
}}}}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,290 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#pragma once
namespace Microsoft { namespace Xbox { namespace Samples { namespace NetworkMesh {
enum WebErrorStatus
{
WebErrorStatus_Unknown = 0,
WebErrorStatus_CertificateCommonNameIsIncorrect = 1,
WebErrorStatus_CertificateExpired = 2,
WebErrorStatus_CertificateContainsErrors = 3,
WebErrorStatus_CertificateRevoked = 4,
WebErrorStatus_CertificateIsInvalid = 5,
WebErrorStatus_ServerUnreachable = 6,
WebErrorStatus_Timeout = 7,
WebErrorStatus_ErrorHttpInvalidServerResponse = 8,
WebErrorStatus_ConnectionAborted = 9,
WebErrorStatus_ConnectionReset = 10,
WebErrorStatus_Disconnected = 11,
WebErrorStatus_HttpToHttpsOnRedirection = 12,
WebErrorStatus_HttpsToHttpOnRedirection = 13,
WebErrorStatus_CannotConnect = 14,
WebErrorStatus_HostNameNotResolved = 15,
WebErrorStatus_OperationCanceled = 16,
WebErrorStatus_RedirectFailed = 17,
WebErrorStatus_UnexpectedStatusCode = 18,
WebErrorStatus_UnexpectedRedirection = 19,
WebErrorStatus_UnexpectedClientError = 20,
WebErrorStatus_UnexpectedServerError = 21,
WebErrorStatus_MultipleChoices = 300,
WebErrorStatus_MovedPermanently = 301,
WebErrorStatus_Found = 302,
WebErrorStatus_SeeOther = 303,
WebErrorStatus_NotModified = 304,
WebErrorStatus_UseProxy = 305,
WebErrorStatus_TemporaryRedirect = 307,
WebErrorStatus_BadRequest = 400,
WebErrorStatus_Unauthorized = 401,
WebErrorStatus_PaymentRequired = 402,
WebErrorStatus_Forbidden = 403,
WebErrorStatus_NotFound = 404,
WebErrorStatus_MethodNotAllowed = 405,
WebErrorStatus_NotAcceptable = 406,
WebErrorStatus_ProxyAuthenticationRequired = 407,
WebErrorStatus_RequestTimeout = 408,
WebErrorStatus_Conflict = 409,
WebErrorStatus_Gone = 410,
WebErrorStatus_LengthRequired = 411,
WebErrorStatus_PreconditionFailed = 412,
WebErrorStatus_RequestEntityTooLarge = 413,
WebErrorStatus_RequestUriTooLong = 414,
WebErrorStatus_UnsupportedMediaType = 415,
WebErrorStatus_RequestedRangeNotSatisfiable = 416,
WebErrorStatus_ExpectationFailed = 417,
WebErrorStatus_InternalServerError = 500,
WebErrorStatus_NotImplemented = 501,
WebErrorStatus_BadGateway = 502,
WebErrorStatus_ServiceUnavailable = 503,
WebErrorStatus_GatewayTimeout = 504,
WebErrorStatus_HttpVersionNotSupported = 505
};
private class Utils
{
public:
static inline bool IsNullOrEmptyString(__in_opt LPCWSTR pcwsz)
{
return (pcwsz == NULL) || (pcwsz[0] == L'\0');
}
// Searches for a pattern in the source string and replace all occurrences of it with another string.
// Pattern must be non-empty
// Replacement can be empty, in which case all occurrences of Pattern are deleted
static std::wstring&
Replace(
__inout std::wstring& strSource,
__in PCWSTR pwszPattern,
__in_opt PCWSTR pwszReplacement,
__out_opt size_t* pnOccurrencesReplaced = nullptr
);
// Searches for a pattern in the source string and replace all occurrences of it with another string.
// Pattern must be non-empty
// Replacement can be empty, in which case all occurrences of Pattern are deleted
// If Replacement is not empty, it will be Uri encoded
static std::wstring&
UriReplace(
__inout std::wstring& strSource,
__in PCWSTR pwszPattern,
__in_opt PCWSTR pwszReplacement,
__out_opt size_t* pnOccurrencesReplaced = nullptr
);
static std::wstring
UriEncode(
__in std::wstring wstrUriToEncode
);
static std::vector<std::wstring>
StringSplit(
__in const std::wstring& string,
__in WCHAR seperator
);
static Microsoft::WRL::ComPtr<IStream>
StringToStream(
__in Platform::String^ source,
__out uint32* streamSize
);
static Microsoft::WRL::ComPtr<IStream>
ArrayToStream(
__in Platform::Array<byte>^ buffer,
__out uint32* streamSize
);
static Platform::Array<byte>^
BufferToArray(
__in Windows::Storage::Streams::IBuffer^ buffer
);
static Platform::Array<byte>^
StringToArray(
__in Platform::String^ source
);
static Platform::String^
StreamToString(
__in ISequentialStream* source
);
// Converts HTTP status Code (e.g., 200, 401, 304) to HRESULT
static HRESULT
ConvertHttpStatusCodeToHR(
__in uint32 HttpStatusCode
);
static Windows::Foundation::PropertyType
ConvertStringToPropertyType(
__in Platform::String^ typeName
);
static HRESULT
ConvertExceptionToHRESULT();
static Platform::String^
DateTimeToString(
__in Windows::Foundation::DateTime dateTime
);
static Platform::String^
RemoveBracesFromGuidString(
__in Platform::String^ guidString
);
static Microsoft::WRL::ComPtr<IStream>
BufferToStream(
__in Windows::Storage::Streams::IBuffer^ source,
__out uint32* streamSize
);
static Windows::Foundation::TimeSpan
ConvertSecondsToTimeSpan(
__in uint32 seconds
);
static Windows::Foundation::TimeSpan
ConvertMillisecondsToTimeSpan(
__in uint64 milliseconds
);
static uint32
ConvertTimeSpanToSeconds(
__in Windows::Foundation::TimeSpan timespan
);
static int64
ConvertTimeSpanToMilliseconds(
__in Windows::Foundation::TimeSpan timespan
);
static uint32
ConvertTimeSpanToDays(
__in Windows::Foundation::TimeSpan timespan
);
static Windows::Foundation::TimeSpan GetCurrentTime();
static Windows::Data::Json::IJsonValue^
GetNullJsonValue();
static void
BufferCopy(
__in Windows::Storage::Streams::IBuffer^ source,
__in Windows::Storage::Streams::IBuffer^ destination,
__in bool append = false
);
static Platform::String^
ConvertHResultToString( HRESULT hr );
static Platform::String^
GetLocale();
static void
SetLocaleMock(Platform::String^ locale);
static Platform::String^
ConvertHResultToErrorName( HRESULT hr );
static std::wstring
UriEncodeSubPathComponents(
__in std::wstring wstrSubPath
);
static void
LogExceptionDebugInfo(
__in HRESULT hr,
__in_opt PCWSTR pwszFunction,
__in_opt PCWSTR pwszFile,
__in uint32 uLine
);
private:
static Platform::String^ s_locale;
};
template<typename T>
static std::basic_string<T>&
ReplaceSubstring(
__inout std::basic_string<T>& strSource,
__in_ecount_z(nPatternLength+1) const T* pszPattern,
__in const size_t nPatternLength,
__in_z const T* pszReplacement,
__out_opt size_t* pnOccurrencesReplaced
)
{
THROW_INVALIDARGUMENT_IF_NULL( pszPattern );
THROW_INVALIDARGUMENT_IF_NULL( pszReplacement );
if ( pnOccurrencesReplaced != nullptr )
{
*pnOccurrencesReplaced = 0;
}
size_t nReplaced = 0;
if ( nPatternLength > 0 )
{
// Search the string backward for the given pattern first
size_t nPos = strSource.rfind( pszPattern );
while ( nPos != std::basic_string<T>::npos )
{
strSource.replace( nPos, nPatternLength, pszReplacement );
++nReplaced;
// Find the given pattern first
if ( nPos == 0 )
{
// There is nothing left to look at, break
break;
}
// Find the next match starting from the last replaced position
nPos = strSource.rfind( pszPattern, nPos - 1 );
}
}
if ( pnOccurrencesReplaced != nullptr )
{
*pnOccurrencesReplaced = nReplaced;
}
return strSource;
}
}}}}
@@ -0,0 +1,48 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#pragma once
namespace Microsoft {
namespace Xbox {
namespace Samples {
namespace NetworkMesh {
/// <summary>
/// Specifies what messages to output for the Xbox Services classes
/// </summary>
PUBLIC_ONLY_IN_DEVKIT enum class XboxNetworkMeshDiagnosticsTraceLevel
{
/// <summary>
/// Output no tracing and debugging messages.
/// </summary>
Off = 0,
/// <summary>
/// Output error-handling messages.
/// </summary>
Error,
/// <summary>
/// Output warnings and error-handling messages.
/// </summary>
Warning,
/// <summary>
/// Output informational messages, warnings, and error-handling messages.
/// </summary>
Info,
/// <summary>
/// Output all debugging and tracing messages.
/// </summary>
Verbose
};
}}}}
@@ -0,0 +1,13 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#pragma once
#define XBOX_SERVICES_API_VERSION_STRING L"xdk,6.2.9586.0,custom"
@@ -0,0 +1,740 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
// Functions to convert dates back and forth to the ISO8601 format
// http://www.iso.org/iso/en/prods-services/popstds/datesandtime.html
#include "pch.h"
#include "ISO8601.h"
#define ISO8601_MAX_USED_CCH 26 // Max amount of characters when generating ISO 8601 strings: YYYY-MM-DDThh:mm:ss.ssssZ + terminating zero
// This table defines different "types" of characters for use as the columns
// of the state table:
// 0 - invalid character
// 1 - number
// 2 - '-'
// 3 - date-time separator ('T', 't' and ' ')
// 4 - ':'
// 5 - UTC zone ('Z' and 'z')
// 6 - '+'
// 7 - second-fraction separator ('.' and ',')
static const unsigned char iso8601chartable[256] =
{
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 (00 - 0f)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 (10 - 1f)
3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 2, 7, 0, // 2 (20 - 2f)
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 0, 0, 0, 0, 0, // 3 (30 - 3f)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 (40 - 4f)
0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, // 5 (50 - 5f)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 (60 - 6f)
0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, // 7 (70 - 7f)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 (80 - 8f)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 (90 - 9f)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a (a0 - af)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // b (b0 - bf)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // c (c0 - cf)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // d (d0 - df)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // e (e0 - ef)
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // f (f0 - ff)
};
// Parsing state table is made up of WORD values with this meaning:
//
// | action | | next state |
// |_|_|_|_|_|_|_|_| |_|_|_|_|_|_|_|_|
// HIBYTE LOBYTE
#define STENTRY WORD
#define S(a,b) MAKEWORD(b,a)
#define ACTION(a) HIBYTE(a)
#define STATE(a) LOBYTE(a)
// Actions
#define _OK_ 0x00 // valid character
#define _NXT 0x01 // end of a segment, move to next
#define _TZM 0x02 // starting '-' time zone offset
#define _TZP 0x03 // starting '+' time zone offset
#define _MSC 0x04 // millisecond digit
#define _TZU 0x05 // time zone UTC
#define _ERR 0x80 // error, invalid character
#define ___ERROR____ MAKEWORD(0x00, _ERR)
// State table
#define STATE_TABLE_DIM 8
static const STENTRY iso8601StateTable[][STATE_TABLE_DIM] =
{
// unknown number '-' 'T' ':' 'Z' '+' '.'
___ERROR____, S(_OK_,0x01), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x00 year
___ERROR____, S(_OK_,0x02), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x01
___ERROR____, S(_OK_,0x03), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x02
___ERROR____, S(_NXT,0x04), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x03
___ERROR____, S(_OK_,0x06), S(_OK_,0x05), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x04 month
___ERROR____, S(_OK_,0x06), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x05
___ERROR____, S(_NXT,0x07), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x06
___ERROR____, S(_OK_,0x09), S(_OK_,0x08), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x07 day
___ERROR____, S(_OK_,0x09), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x08
___ERROR____, S(_NXT,0x0a), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x09
___ERROR____, S(_OK_,0x0c), ___ERROR____, S(_OK_,0x0b), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0a hour
___ERROR____, S(_OK_,0x0c), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0b
___ERROR____, S(_NXT,0x0d), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0c
___ERROR____, S(_OK_,0x0f), S(_TZM,0x15), ___ERROR____, S(_OK_,0x0e), S(_TZU,0x1b), S(_TZP,0x15), ___ERROR____, //0x0d min
___ERROR____, S(_OK_,0x0f), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0e
___ERROR____, S(_NXT,0x10), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x0f
___ERROR____, S(_OK_,0x12), S(_TZM,0x15), ___ERROR____, S(_OK_,0x11), S(_TZU,0x1b), S(_TZP,0x15), ___ERROR____, //0x10 sec
___ERROR____, S(_OK_,0x12), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x11
___ERROR____, S(_NXT,0x13), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x12
___ERROR____, ___ERROR____, S(_TZM,0x15), ___ERROR____, ___ERROR____, S(_TZU,0x1b), S(_TZP,0x15), S(_OK_,0x14), //0x13 '.' or 'Z' or '+/-'
___ERROR____, S(_MSC,0x14), S(_TZM,0x15), ___ERROR____, ___ERROR____, S(_TZU,0x1b), S(_TZP,0x15), ___ERROR____, //0x14 fragment of a second
___ERROR____, S(_OK_,0x16), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x15 TZ offset - hour
___ERROR____, S(_NXT,0x17), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x16
___ERROR____, S(_OK_,0x19), ___ERROR____, ___ERROR____, S(_OK_,0x18), ___ERROR____, ___ERROR____, ___ERROR____, //0x17 TZ offset - minute
___ERROR____, S(_OK_,0x19), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x18
___ERROR____, S(_NXT,0x1a), ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x19
___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x1a offset done - parsing done
___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, ___ERROR____, //0x1b UTC zone done - parsing done
};
#define IsLeapYear(YEARS) ( \
(((YEARS) % 400 == 0) || \
((YEARS) % 100 != 0) && ((YEARS) % 4 == 0)) ? \
TRUE \
: \
FALSE \
)
static HRESULT _GetNumDaysForYearMonth(
WORD wYear,
WORD wMonth,
__out WORD *pwDays)
{
HRESULT hr = S_OK;
if (NULL == pwDays ||
wMonth < 1 || wMonth > 12 ||
wYear < 1601 || wYear > 9999)
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
static const WORD s_wNumDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
*pwDays = s_wNumDays[wMonth - 1];
// Check for leap year
if ((wMonth == 2) && IsLeapYear(wYear))
{
(*pwDays)++;
}
}
return hr;
}
#define ONE_SECOND 10000000ui64 // number of 100-nanosecond intervals in a second
#define ONE_MINUTE (ONE_SECOND * 60) // number of 100-nanosecond intervals in a minute
#define ONE_HOUR (ONE_MINUTE * 60) // number of 100-nanosecond intervals in an hour
typedef union tagTU
{
FILETIME ft;
ULARGE_INTEGER ui;
} TU;
static HRESULT _AddTZOffset(
Iso8601ParsingStage ips,
WORD wValue,
int iTZDirection,
__inout SYSTEMTIME *pSysTime)
{
HRESULT hr = S_OK;
_ASSERTE((ips == IPS_TZHOUR && wValue < 24) || (ips == IPS_TZMINUTE && wValue < 60));
_ASSERTE(iTZDirection == 1 || iTZDirection == -1);
// Convert SYSTEMTIME to FILETIME to perform date-time arithmetic
TU ftTime = {0};
if (SystemTimeToFileTime(pSysTime, &ftTime.ft))
{
ULARGE_INTEGER ulOffset;
ulOffset.QuadPart = (ips == IPS_TZHOUR) ? ONE_HOUR * wValue : ONE_MINUTE * wValue;
if (iTZDirection > 0)
{
ftTime.ui.QuadPart += ulOffset.QuadPart;
}
else
{
ftTime.ui.QuadPart -= ulOffset.QuadPart;
}
if (!FileTimeToSystemTime(&ftTime.ft, pSysTime))
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
}
else
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
return hr;
}
static HRESULT _CheckValueAndAddToSysTime(
Iso8601ParsingStage ips,
WORD wValue,
WORD wMSDigits,
int iTZDirection,
__inout SYSTEMTIME *pSysTime)
{
HRESULT hr = S_OK;
WORD wHiLimit = 0;
WORD wLoLimit = 0;
WORD *pDateWord = NULL;
_ASSERTE((iTZDirection == 0 && ips != IPS_TZHOUR && ips != IPS_TZMINUTE) ||
(iTZDirection != 0 && (ips == IPS_TZHOUR || ips == IPS_TZMINUTE)));
_ASSERTE((wMSDigits == 0 && ips != IPS_MILLISECOND) ||
(wMSDigits > 0 && wMSDigits < 4 && ips == IPS_MILLISECOND));
switch (ips)
{
case IPS_YEAR:
wLoLimit = 1601;
wHiLimit = 9999;
pDateWord = (WORD *) &(pSysTime->wYear);
break;
case IPS_MONTH:
wLoLimit = 1;
wHiLimit = 12;
pDateWord = (WORD *) &(pSysTime->wMonth);
break;
case IPS_DAY:
wLoLimit = 1;
hr = _GetNumDaysForYearMonth(pSysTime->wYear, pSysTime->wMonth, &wHiLimit);
_ASSERTE(SUCCEEDED(hr)); // internal call, should never fail
pDateWord = (WORD *) &(pSysTime->wDay);
break;
case IPS_HOUR:
wLoLimit = 0;
wHiLimit = 23;
pDateWord = (WORD *) &(pSysTime->wHour);
break;
case IPS_MINUTE:
wLoLimit = 0;
wHiLimit = 59;
pDateWord = (WORD *) &(pSysTime->wMinute);
break;
case IPS_SECOND:
wLoLimit = 0;
wHiLimit = 59;
pDateWord = (WORD *) &(pSysTime->wSecond);
break;
case IPS_MILLISECOND:
wLoLimit = 0;
wHiLimit = 999;
while (wMSDigits++ < 3)
{
wValue *= 10;
}
pDateWord = (WORD *) &(pSysTime->wMilliseconds);
break;
case IPS_TZHOUR:
wLoLimit = 0;
wHiLimit = 23; // valid offsets appear to be -12 to +14, but we'll allow any valid hour value
pDateWord = (WORD *) &(pSysTime->wHour);
break;
case IPS_TZMINUTE:
wLoLimit = 0;
wHiLimit = 59;
pDateWord = (WORD *) &(pSysTime->wMinute);
break;
default:
hr = E_UNEXPECTED;
break;
}
if (SUCCEEDED(hr))
{
_ASSERTE(NULL != pDateWord);
if (wValue < wLoLimit || wValue > wHiLimit)
{
hr = E_ABORT;
}
else
{
_ASSERTE(*pDateWord == 0 || ips >= IPS_MILLISECOND);
if (ips != IPS_TZHOUR && ips != IPS_TZMINUTE)
{
*pDateWord = wValue;
}
else
{
_ASSERTE(iTZDirection == 1 || iTZDirection == -1);
// handle offset rollover - can affect entire date
hr = _AddTZOffset(ips, wValue, iTZDirection, pSysTime);
}
}
}
return hr;
}
static HRESULT _iso8601ToSysTime(
const char *pszisoDate,
__out SYSTEMTIME *pSysTime,
__out Iso8601ParsingStage *pips)
{
HRESULT hr = S_OK;
if (NULL == pszisoDate ||
NULL == pSysTime ||
NULL == pips ||
*pszisoDate == '\0')
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
ZeroMemory(pSysTime, sizeof(SYSTEMTIME));
*pips = IPS_INVALID;
BYTE action = 0;
BYTE state = 0;
WORD DateWord = 0;
Iso8601ParsingStage ips = IPS_YEAR;
int iTZDirection = 0;
WORD wMSDigits = 0;
// Main state machine loop
while(*pszisoDate && SUCCEEDED(hr))
{
unsigned char code = iso8601chartable[*pszisoDate];
// Prevent overflows - sanity check
if ((code >= STATE_TABLE_DIM) ||
(state >= ARRAYSIZE(iso8601StateTable)))
{
hr = E_UNEXPECTED;
break;
}
STENTRY stValue = iso8601StateTable[state][code];
state = STATE(stValue);
action = ACTION(stValue);
switch(action)
{
case _OK_: // input OK, valid character
case _NXT: // finish piece and advance to next stage, valid character
{
if (code == 1)
{
DateWord = (DateWord * 10) + (*pszisoDate - '0');
}
if (action == _NXT)
{
hr = _CheckValueAndAddToSysTime(ips, DateWord, 0 /*wMSDigits*/, iTZDirection, pSysTime);
DateWord = 0;
if (SUCCEEDED(hr))
{
*pips = ips;
ips = Iso8601ParsingStage(int(ips) + 1);
}
}
}
break;
case _TZM: // start '-' timezone offset
iTZDirection = 1;
DateWord = 0;
*pips = ips;
ips = IPS_TZHOUR;
break;
case _TZP: // start '+' timezone offset
iTZDirection = -1;
DateWord = 0;
*pips = ips;
ips = IPS_TZHOUR;
break;
case _MSC: // process millisecond digit
_ASSERTE(code == 1 && ips == IPS_MILLISECOND);
wMSDigits++;
if (wMSDigits < 4)
{
DateWord = (DateWord * 10) + (*pszisoDate - '0');
hr = _CheckValueAndAddToSysTime(ips, DateWord, wMSDigits, iTZDirection, pSysTime);
if (SUCCEEDED(hr))
{
*pips = IPS_MILLISECOND;
}
}
break;
case _TZU: // 'Z' for UTC zone
*pips = IPS_TZUTC;
ips = IPS_TZUTC;
break;
case _ERR: // error, invalid character
_ASSERTE(state == 0x00);
hr = E_ABORT;
break;
default:
hr = E_UNEXPECTED;
break;
}
pszisoDate++;
}
if (SUCCEEDED(hr) && *pszisoDate == '\0')
{
if (action != _NXT && action != _MSC && action != _TZU)
{
hr = E_ABORT;
}
}
if (hr != S_OK && hr != E_ABORT)
{
*pips = IPS_INVALID;
}
}
_ASSERTE(hr != E_UNEXPECTED);
return hr;
}
static HRESULT _iso8601ToFileTime(
const char *pszisoDate,
__out FILETIME *pftTime,
__out Iso8601ParsingStage *pips)
{
HRESULT hr = S_OK;
SYSTEMTIME stTime = {0};
hr = _iso8601ToSysTime(pszisoDate, &stTime, pips);
if (SUCCEEDED(hr))
{
if (!SystemTimeToFileTime(&stTime, pftTime))
{
hr = E_FAIL;
}
}
return hr;
}
static HRESULT _SysTimeToiso8601(
__in SYSTEMTIME *pstTime,
__in BOOL fGeneratePartial,
__out_ecount(cch) char *pszBuf,
__in ULONG cch,
__in BOOL fUseShortTimeFormat = FALSE
)
{
HRESULT hr = S_OK;
if (NULL == pstTime ||
NULL == pszBuf ||
cch < ISO8601_MAX_USED_CCH)
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
if (pstTime->wYear < 1601 || // FILETIME cannot handle less
pstTime->wYear > 9999 || // ISO8601 has four digits for a year
pstTime->wMonth > 12 ||
pstTime->wDay > 31 ||
pstTime->wHour > 24 ||
pstTime->wMinute > 59 ||
pstTime->wSecond > 59 ||
pstTime->wMilliseconds > 999)
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
WORD wDays = 0;
hr = _GetNumDaysForYearMonth(pstTime->wYear, pstTime->wMonth, &wDays);
if (SUCCEEDED(hr) && pstTime->wDay > wDays)
{
hr = E_INVALIDARG;
}
}
}
if (SUCCEEDED(hr))
{
pszBuf[0] = static_cast<char>((pstTime->wYear / 1000) + '0');
pszBuf[1] = static_cast<char>(((pstTime->wYear / 100) % 10) + '0');
pszBuf[2] = static_cast<char>(((pstTime->wYear / 10) % 10) + '0');
pszBuf[3] = static_cast<char>(((pstTime->wYear) % 10) + '0');
if (pstTime->wMonth > 0 ||
!fGeneratePartial)
{
pszBuf[4] = '-';
pszBuf[5] = static_cast<char>((pstTime->wMonth / 10) + '0');
pszBuf[6] = static_cast<char>((pstTime->wMonth % 10) + '0');
if (pstTime->wDay > 0 ||
!fGeneratePartial)
{
pszBuf[7] = '-';
pszBuf[8] = static_cast<char>((pstTime->wDay / 10) + '0');
pszBuf[9] = static_cast<char>((pstTime->wDay % 10) + '0');
if (pstTime->wHour != 0 ||
pstTime->wMinute != 0 ||
pstTime->wSecond != 0 ||
pstTime->wMilliseconds != 0 ||
!fGeneratePartial)
{
pszBuf[10] = 'T';
pszBuf[11] = static_cast<char>(pstTime->wHour / 10 + '0');
pszBuf[12] = static_cast<char>((pstTime->wHour % 10) + '0');
pszBuf[13] = ':';
pszBuf[14] = static_cast<char>(pstTime->wMinute / 10 + '0');
pszBuf[15] = static_cast<char>((pstTime->wMinute % 10) + '0');
pszBuf[16] = ':';
pszBuf[17] = static_cast<char>(pstTime->wSecond / 10 + '0');
pszBuf[18] = static_cast<char>((pstTime->wSecond % 10) + '0');
if ( !fUseShortTimeFormat && ( pstTime->wMilliseconds != 0 ) )
{
// YYYY-MM-DDThh:mm:ss.ssssZ
pszBuf[19] = '.';
pszBuf[20] = static_cast<char>(pstTime->wMilliseconds / 100 + '0');
pszBuf[21] = static_cast<char>(((pstTime->wMilliseconds / 10) % 10) + '0');
pszBuf[22] = static_cast<char>((pstTime->wMilliseconds % 10) + '0');
// pad the last digit of millisecond with 0
pszBuf[23] = '0';
pszBuf[24] = 'Z';
pszBuf[25] = 0;
}
else
{
// YYYY-MM-DDThh:mm:ssZ
pszBuf[19] = 'Z';
pszBuf[20] = 0;
}
}
else
{
// YYYY-MM-DD
pszBuf[10] = 0;
}
}
else
{
// YYYY-MM
pszBuf[7] = 0;
}
}
else
{
// YYYY
pszBuf[4] = 0;
}
}
return hr;
}
static HRESULT _FileTimeToiso8601(
const FILETIME *pftTime,
BOOL fGeneratePartial,
__out_ecount(cch) char *pszBuf,
ULONG cch,
BOOL fUseShortTimeFormat
)
{
HRESULT hr = S_OK;
SYSTEMTIME stTime = {0};
if (NULL == pftTime)
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
if (FileTimeToSystemTime( pftTime, &stTime))
{
hr = _SysTimeToiso8601( &stTime, fGeneratePartial, pszBuf, cch, fUseShortTimeFormat );
}
else
{
hr = E_FAIL;
}
}
return hr;
}
HRESULT FILETIMEToISO8601W(
__in const FILETIME* pft,
__in BOOL fGeneratePartial,
__out_ecount(cchISO8601) PWSTR pszISO8601,
__in size_t cchISO8601,
__in BOOL fUseShortTimeFormat
)
{
HRESULT hr = S_OK;
CHAR aszISO8601[ISO8601_MAX_USED_CCH];
hr = _FileTimeToiso8601( pft, fGeneratePartial, aszISO8601, ARRAYSIZE(aszISO8601), fUseShortTimeFormat );
if (SUCCEEDED(hr))
{
int cchSHAnsiToUnicode;
hr = SizeTToInt(cchISO8601, &cchSHAnsiToUnicode);
if (SUCCEEDED(hr))
{
hr = ( ISO8601_MAX_USED_CCH >= ::MultiByteToWideChar(
CP_ACP,
0,
aszISO8601,
-1,
pszISO8601, cchSHAnsiToUnicode) ) ? S_OK : E_UNEXPECTED;
_ASSERTE(SUCCEEDED(hr));
}
}
return hr;
}
HRESULT ISO8601ToFILETIMEW(
__in PCWSTR pszISO8601,
__out FILETIME* pft,
__out Iso8601ParsingStage *pips)
{
HRESULT hr = S_OK;
CHAR aszISO8601[ISO8601_MAX_CCH];
if( !::WideCharToMultiByte(
CP_ACP,
0,
pszISO8601,
-1,
aszISO8601,
ISO8601_MAX_CCH,
NULL,
NULL ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
if (SUCCEEDED(hr))
{
hr = _iso8601ToFileTime(aszISO8601, pft, pips);
}
return hr;
}
HRESULT SYSTEMTIMEToISO8601ExW(
__in const SYSTEMTIME* pst,
__in BOOL fGeneratePartial,
__out_ecount(cchISO8601) PWSTR pszISO8601,
__in size_t cchISO8601)
{
HRESULT hr = S_OK;
SYSTEMTIME st = *pst;
CHAR aszISO8601[ISO8601_MAX_USED_CCH];
hr = _SysTimeToiso8601(&st, fGeneratePartial, aszISO8601, ARRAYSIZE(aszISO8601));
if (SUCCEEDED(hr))
{
int cchSHAnsiToUnicode;
hr = SizeTToInt(cchISO8601, &cchSHAnsiToUnicode);
if (SUCCEEDED(hr))
{
hr = ( ISO8601_MAX_USED_CCH >= ::MultiByteToWideChar(
CP_ACP,
0,
aszISO8601,
-1,
pszISO8601,
cchSHAnsiToUnicode ) ) ? S_OK : E_UNEXPECTED;
_ASSERTE(SUCCEEDED(hr));
}
}
return hr;
}
HRESULT SYSTEMTIMEToISO8601W(
__in const SYSTEMTIME* pst,
__out_ecount(cchISO8601) PWSTR pszISO8601,
__in size_t cchISO8601)
{
return SYSTEMTIMEToISO8601ExW(pst, FALSE /*fGeneratePartial*/, pszISO8601, cchISO8601);
}
HRESULT ISO8601ToSYSTEMTIMEExW(
__in PCWSTR pszISO8601,
__out SYSTEMTIME* pst,
__out Iso8601ParsingStage *pips)
{
HRESULT hr = S_OK;
CHAR aszISO8601[ISO8601_MAX_CCH];
if( !::WideCharToMultiByte(
CP_ACP,
0,
pszISO8601,
-1,
aszISO8601,
ISO8601_MAX_CCH,
NULL,
NULL ) )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
}
if (SUCCEEDED(hr))
{
hr = _iso8601ToSysTime(aszISO8601, pst, pips);
}
return hr;
}
HRESULT ISO8601ToSYSTEMTIMEW(
__in PCWSTR pszISO8601,
__out SYSTEMTIME* pst)
{
HRESULT hr = S_OK;
Iso8601ParsingStage ips = IPS_INVALID;
hr = ISO8601ToSYSTEMTIMEExW(pszISO8601, pst, &ips);
// Fix up less-than-full-date
if (SUCCEEDED(hr) && ips < IPS_DAY)
{
if (ips < IPS_DAY)
{
pst->wDay = 1;
}
if (ips < IPS_MONTH)
{
pst->wMonth = 1;
}
hr = S_OK;
}
return hr;
}
@@ -0,0 +1,84 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#pragma once
// Functions to convert dates back and forth to the ISO8601 format
// (http://www.iso.org/iso/en/prods-services/popstds/datesandtime.html)
//
// Supported dates are from 1601-01-01 (SYSTEMTIME and FILETIME limitation) to
// 9999-12-31 (ISO8601 limitation)
// Longest form of ISO8601 is 40 chars + 1 for terminating zero
#define ISO8601_MAX_CCH 41
// Iso8601ParsingStage enum
//
// This enumeration is design so values can be compared.
// ISO8601 dates look like this: YYYY-MM-DDThh:mm:ss.sss+/-hh:mm
// So e.g. by parsing the date and then asking if parse stage was
// <IPS_HOUR you can tell if string contained any time at all, or just the date
enum Iso8601ParsingStage
{
IPS_INVALID = -1,
IPS_YEAR = 0,
IPS_MONTH,
IPS_DAY,
IPS_HOUR,
IPS_MINUTE,
IPS_SECOND,
IPS_MILLISECOND,
IPS_TZHOUR,
IPS_TZMINUTE,
IPS_TZUTC,
};
HRESULT
FILETIMEToISO8601W(
__in const FILETIME* pft,
__in BOOL fGeneratePartial,
__out_ecount(cchISO8601) PWSTR pszISO8601,
__in size_t cchISO8601,
__in BOOL fUseShortTimeFormat = FALSE
);
HRESULT
ISO8601ToFILETIMEW(
__in PCWSTR pszISO8601,
__out FILETIME* pft,
__out Iso8601ParsingStage* pips
);
HRESULT
SYSTEMTIMEToISO8601ExW(
__in const SYSTEMTIME* pst,
__in BOOL fGeneratePartial,
__out_ecount(cchISO8601) PWSTR pszISO8601,
__in size_t cchISO8601
);
HRESULT
SYSTEMTIMEToISO8601W(
__in const SYSTEMTIME* pst,
__out_ecount(cchISO8601) PWSTR pszISO8601,
__in size_t cchISO8601
);
HRESULT
ISO8601ToSYSTEMTIMEExW(
__in PCWSTR pszISO8601,
__out SYSTEMTIME* pst,
__out Iso8601ParsingStage* pips
);
HRESULT ISO8601ToSYSTEMTIMEW(
__in PCWSTR pszISO8601,
__out SYSTEMTIME* pst
);
@@ -0,0 +1,71 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#pragma once
// forward declarations
namespace Microsoft { namespace Xbox { namespace Services { class Utils; } } }
// preprocessor workaround
// to work with L##__FUNCTION__ and L##__FILE__ statements.
#define TEXTW(quote) _TEXTW(quote)
#define _TEXTW(quote) L##quote
#define LOG_INFO_MSG(msg) { OutputDebugString( msg ); }
#define LOG_ERROR_MSG(msg) if( m_xboxLiveContextSettings->IsAtDiagnosticsTraceLevel(XboxNetworkMeshDiagnosticsTraceLevel::Error) ) { OutputDebugString( msg ); }
#define LOG_EXCEPTION(hr) { Microsoft::Xbox::Samples::NetworkMesh::Utils::LogExceptionDebugInfo(hr, TEXTW(__FUNCTION__), TEXTW(__FILE__), __LINE__ ); }
#define THROW_INVALIDARGUMENT_IF(x) if ( x ) { LOG_EXCEPTION(E_INVALIDARG); throw ref new Platform::InvalidArgumentException(); }
#define THROW_INVALIDARGUMENT_IF_WITH_LOG(x,msg) if ( x ) { LOG_ERROR_MSG( msg ); LOG_EXCEPTION(E_INVALIDARG); throw ref new Platform::InvalidArgumentException(); }
#define THROW_INVALIDARGUMENT_IF_NULL(x) if ( ( x ) == nullptr ) { LOG_EXCEPTION(E_INVALIDARG); throw ref new Platform::InvalidArgumentException(); }
#define THROW_INVALIDARGUMENT_IF_NULL_WITH_LOG(x,msg) if ( ( x ) == nullptr ) { LOG_ERROR_MSG( msg ); LOG_EXCEPTION(E_INVALIDARG); throw ref new Platform::InvalidArgumentException(); }
#define THROW_E_POINTER_IF_NULL(x) if ( ( x ) == nullptr ) { LOG_EXCEPTION(E_POINTER); throw ref new Platform::COMException(E_POINTER); }
#define THROW_E_POINTER_IF_NULL_WITH_LOG(x,msg) if ( ( x ) == nullptr ) { LOG_ERROR_MSG( msg ); LOG_EXCEPTION(E_POINTER); throw ref new Platform::COMException(E_POINTER); }
#define THROW_INVALIDARGUMENT_IF_STRING_EMPTY(x) { auto y = x; if ( y->IsEmpty() ) { LOG_EXCEPTION(E_INVALIDARG); throw ref new Platform::InvalidArgumentException(); } }
#define THROW_INVALIDARGUMENT_IF_STRING_EMPTY_WITH_LOG(x,msg) { auto y = x; if ( y->IsEmpty() ) { LOG_ERROR_MSG( msg ); LOG_EXCEPTION(E_INVALIDARG); throw ref new Platform::InvalidArgumentException(); } }
#define THROW_IF_HR_FAILED(hr) { HRESULT hr2 = hr; if ( FAILED( hr2 ) ) { LOG_EXCEPTION(hr2); throw ref new Platform::COMException(hr2); } }
#define THROW_IF_HR_FAILED_WITH_LOG(hr,msg) { HRESULT hr2 = hr; if ( FAILED( hr2 ) ) { LOG_ERROR_MSG( msg ); LOG_EXCEPTION(hr2); throw ref new Platform::COMException(hr2); } }
#define THROW_HR(hr) { LOG_EXCEPTION(hr); throw ref new Platform::COMException(hr); }
#define THROW_HR_WITH_LOG(hr,msg) { LOG_ERROR_MSG( msg ); LOG_EXCEPTION(hr); throw ref new Platform::COMException(hr); }
#define THROW_HR_IF(x,hr) if ( x ) { LOG_EXCEPTION(hr); throw ref new Platform::COMException(hr); }
#define THROW_HR_IF_WITH_LOG(x,hr,msg) if ( x ) { LOG_ERROR_MSG( msg ); LOG_EXCEPTION(hr); throw ref new Platform::COMException(hr); }
#define THROW_WIN32_IF(x,e) if ( x ) { HRESULT hr = __HRESULT_FROM_WIN32(e); LOG_EXCEPTION(hr); throw ref new Platform::COMException(hr); }
#define THROW_WIN32_IF_WITH_LOG(x,e,msg) if ( x ) { HRESULT hr = __HRESULT_FROM_WIN32(e); LOG_ERROR_MSG( msg ); LOG_EXCEPTION(hr); throw ref new Platform::COMException(hr); }
#define E_INVALIDARG_IF(x) if ( x ) { return E_INVALIDARG; }
#define E_POINTER_IF_NULL(x) if ( ( x ) == nullptr ) { return E_POINTER; }
#define E_POINTER_OR_INVALIDARG_IF_STRING_EMPTY(x) { auto y = x; E_POINTER_IF_NULL( y ); E_INVALIDARG_IF( wcslen( y ) == 0 ); }
#define CHECKHR_EXIT(hrResult) { hr = hrResult; if ( FAILED( hr ) ) { goto exit; } }
#define TV_API (WINAPI_FAMILY == WINAPI_FAMILY_TV_APP | WINAPI_FAMILY == WINAPI_FAMILY_TV_TITLE)
#define NAMESPACE_MICROSOFT_XBOX_SAMPLES_NETWORKMESH_BEGIN namespace Microsoft { namespace Xbox { namespace Samples { namespace NetworkMesh {
#define NAMESPACE_MICROSOFT_XBOX_SAMPLES_NETWORKMESH_END }}}}
#define USING_NAMESPACE_MICROSOFT_XBOX_SAMPLES_NETWORKMESH using namespace Microsoft::Xbox::Samples::NetworkMesh;
#ifdef PRERELEASE
#define PUBLIC_ONLY_IN_PRERELEASE public
#define PUBLIC_ONLY_IN_PRERELEASE_INTERNAL_OTHERWISE public
#else
#define PUBLIC_ONLY_IN_PRERELEASE private
#define PUBLIC_ONLY_IN_PRERELEASE_INTERNAL_OTHERWISE internal
#endif
#ifdef INTERNAL_BUILD
#define PUBLIC_ONLY_IN_DEVKIT private
#else
#define PUBLIC_ONLY_IN_DEVKIT public
#endif
inline int64 SECONDS_TO_100NS(int32 x)
{
return ((int64)x) * 10000000;
}
@@ -0,0 +1,10 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#include "pch.h"
+40
View File
@@ -0,0 +1,40 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#pragma once
#include <wrl/implements.h>
#include <wrl/client.h>
#include <Windows.h>
#include <mstcpip.h>
#include <winsock2.h>
#include <ws2ipdef.h>
#include <ws2tcpip.h>
#include <tchar.h>
#include <collection.h>
#include <ppltasks.h>
#include <queue>
#include <xdk.h>
#include <wrl.h>
#include <d3d11_x.h>
#include <DirectXMath.h>
#include <concrt.h>
#include <stdlib.h>
#include <Robuffer.h>
#include "Windows.Xbox.Chat.h"
#ifdef _TITLE
#include <pix.h>
#endif
#include "Common/macros.h"
@@ -0,0 +1,3 @@
This drop is from the August 2013 [9586.0.130810-2000] XDK
Please note below any changes you make to this directory to minimize merge issues with updated source drops from newer XDKs