mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-06 05:37:48 +00:00
GEEKING
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
}}}}
|
||||
+46
@@ -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>
|
||||
+111
@@ -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"
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user