first commit
This commit is contained in:
805
Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp
Normal file
805
Minecraft.Client/Durango/Network/ChatIntegrationLayer.cpp
Normal file
@@ -0,0 +1,805 @@
|
||||
//// 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 "stdafx.h"
|
||||
#include "ChatIntegrationLayer.h"
|
||||
#include "DQRNetworkManager.h"
|
||||
#include <robuffer.h>
|
||||
|
||||
using namespace Windows::Foundation;
|
||||
using namespace Windows::Xbox::System;
|
||||
|
||||
// To integrate the Chat DLL in your game, you can use this ChatIntegrationLayer class with modifications,
|
||||
// or create your own design your own class using the code in this file a guide.
|
||||
std::shared_ptr<ChatIntegrationLayer> GetChatIntegrationLayer()
|
||||
{
|
||||
static std::shared_ptr<ChatIntegrationLayer> chatIntegrationLayerInstance;
|
||||
if (chatIntegrationLayerInstance == nullptr)
|
||||
{
|
||||
chatIntegrationLayerInstance.reset( new ChatIntegrationLayer() );
|
||||
}
|
||||
|
||||
return chatIntegrationLayerInstance;
|
||||
}
|
||||
|
||||
ChatIntegrationLayer::ChatIntegrationLayer()
|
||||
{
|
||||
ZeroMemory( m_chatVoicePacketsStatistic, sizeof(m_chatVoicePacketsStatistic) );
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::InitializeChatManager(
|
||||
__in bool combineCaptureBuffersIntoSinglePacket,
|
||||
__in bool useKinectAsCaptureSource,
|
||||
__in bool applySoundEffectsToCapturedAudio,
|
||||
__in bool applySoundEffectsToChatRenderedAudio,
|
||||
DQRNetworkManager *pDQRNet
|
||||
)
|
||||
{
|
||||
m_pDQRNet = pDQRNet;
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_chatPacketStatsLock);
|
||||
ZeroMemory( m_chatVoicePacketsStatistic, sizeof(m_chatVoicePacketsStatistic) );
|
||||
}
|
||||
|
||||
m_chatManager = ref new Microsoft::Xbox::GameChat::ChatManager();
|
||||
m_chatManager->ChatSettings->DiagnosticsTraceLevel = Microsoft::Xbox::GameChat::GameChatDiagnosticsTraceLevel::Verbose;
|
||||
|
||||
// Optionally, change the default settings below as desired by commenting out and editing any of the following lines
|
||||
// Otherwise these defaults are used.
|
||||
//
|
||||
// m_chatManager = ref new Microsoft::Xbox::GameChat::ChatManager( ChatSessionPeriod::ChatPeriodOf40Milliseconds );
|
||||
// m_chatManager->ChatSettings->AudioThreadPeriodInMilliseconds = 40;
|
||||
// m_chatManager->ChatSettings->AudioThreadAffinityMask = XAUDIO2_DEFAULT_PROCESSOR; // <- this means is core 5, same as the default XAudio2 core
|
||||
// m_chatManager->ChatSettings->AudioThreadPriority = THREAD_PRIORITY_TIME_CRITICAL;
|
||||
// m_chatManager->ChatSettings->AudioEncodingQuality = Windows::Xbox::Chat::EncodingQuality::Normal;
|
||||
// m_chatManager->ChatSettings->DiagnosticsTraceLevel = Microsoft::Xbox::GameChat::GameChatDiagnosticsTraceLevel::Verbose;
|
||||
m_chatManager->ChatSettings->CombineCaptureBuffersIntoSinglePacket = combineCaptureBuffersIntoSinglePacket; // if unset, it defaults to TRUE
|
||||
m_chatManager->ChatSettings->UseKinectAsCaptureSource = useKinectAsCaptureSource; // if unset, it defaults to FALSE
|
||||
m_chatManager->ChatSettings->PreEncodeCallbackEnabled = applySoundEffectsToCapturedAudio; // if unset, it defaults to FALSE
|
||||
m_chatManager->ChatSettings->PostDecodeCallbackEnabled = applySoundEffectsToChatRenderedAudio; // if unset, it defaults to FALSE
|
||||
|
||||
InitializeCriticalSection(&m_csAddedUsers);
|
||||
|
||||
std::weak_ptr<ChatIntegrationLayer> weakPtrToThis = shared_from_this();
|
||||
|
||||
#ifdef PROFILE
|
||||
m_chatManager->ChatSettings->PerformanceCountersEnabled = true;
|
||||
#endif
|
||||
|
||||
// Upon enter constrained mode, mute everyone.
|
||||
// Upon leaving constrained mode, unmute everyone who was previously muted.
|
||||
m_tokenResourceAvailabilityChanged = Windows::ApplicationModel::Core::CoreApplication::ResourceAvailabilityChanged +=
|
||||
ref new EventHandler< Platform::Object^ >( [weakPtrToThis] (Platform::Object^, Platform::Object^ )
|
||||
{
|
||||
// Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released.
|
||||
// Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer
|
||||
std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
if (Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Constrained)
|
||||
{
|
||||
if( sharedPtrToThis->m_chatManager != nullptr )
|
||||
{
|
||||
sharedPtrToThis->m_chatManager->MuteAllUsersFromAllChannels();
|
||||
}
|
||||
}
|
||||
else if(Windows::ApplicationModel::Core::CoreApplication::ResourceAvailability == Windows::ApplicationModel::Core::ResourceAvailability::Full)
|
||||
{
|
||||
if( sharedPtrToThis->m_chatManager != nullptr )
|
||||
{
|
||||
sharedPtrToThis->m_chatManager->UnmuteAllUsersFromAllChannels();
|
||||
|
||||
// The title should remember who was muted so when the Resume even occurs
|
||||
// to avoid unmuting users who has been previously muted. Simply re-mute them here
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
m_tokenOnDebugMessage = m_chatManager->OnDebugMessage +=
|
||||
ref new Windows::Foundation::EventHandler<Microsoft::Xbox::GameChat::DebugMessageEventArgs^>(
|
||||
[weakPtrToThis] ( Platform::Object^, Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args )
|
||||
{
|
||||
// Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released.
|
||||
// Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer
|
||||
std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
sharedPtrToThis->OnDebugMessageReceived(args);
|
||||
}
|
||||
});
|
||||
|
||||
m_tokenOnOutgoingChatPacketReady = m_chatManager->OnOutgoingChatPacketReady +=
|
||||
ref new Windows::Foundation::EventHandler<Microsoft::Xbox::GameChat::ChatPacketEventArgs^>(
|
||||
[weakPtrToThis] ( Platform::Object^, Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args )
|
||||
{
|
||||
// Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released.
|
||||
// Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer
|
||||
std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
sharedPtrToThis->OnOutgoingChatPacketReady(args);
|
||||
}
|
||||
});
|
||||
|
||||
m_tokenOnCompareUniqueConsoleIdentifiers = m_chatManager->OnCompareUniqueConsoleIdentifiers +=
|
||||
ref new Microsoft::Xbox::GameChat::CompareUniqueConsoleIdentifiersHandler(
|
||||
[weakPtrToThis] ( Platform::Object^ obj1, Platform::Object^ obj2 )
|
||||
{
|
||||
// Using a std::weak_ptr instead of 'this' to avoid dangling pointer if caller class is released.
|
||||
// Simply unregistering the callback in the destructor isn't enough to prevent a dangling pointer
|
||||
std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
return sharedPtrToThis->CompareUniqueConsoleIdentifiers(obj1, obj2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
m_tokenUserAudioDeviceAdded = WXS::User::AudioDeviceAdded +=
|
||||
ref new Windows::Foundation::EventHandler<WXS::AudioDeviceAddedEventArgs^>(
|
||||
[weakPtrToThis] ( Platform::Object^, WXS::AudioDeviceAddedEventArgs^ value )
|
||||
{
|
||||
std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
sharedPtrToThis->EvaluateDevicesForUser(value->User);
|
||||
}
|
||||
});
|
||||
|
||||
m_tokenUserAudioDeviceRemoved = WXS::User::AudioDeviceRemoved +=
|
||||
ref new Windows::Foundation::EventHandler<WXS::AudioDeviceRemovedEventArgs^>(
|
||||
[weakPtrToThis] ( Platform::Object^, WXS::AudioDeviceRemovedEventArgs^ value )
|
||||
{
|
||||
std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
sharedPtrToThis->EvaluateDevicesForUser(value->User);
|
||||
}
|
||||
});
|
||||
|
||||
m_tokenUserAudioDeviceChanged = WXS::User::AudioDeviceChanged +=
|
||||
ref new Windows::Foundation::EventHandler<WXS::AudioDeviceChangedEventArgs^>(
|
||||
[weakPtrToThis] ( Platform::Object^, WXS::AudioDeviceChangedEventArgs^ value )
|
||||
{
|
||||
std::shared_ptr<ChatIntegrationLayer> sharedPtrToThis(weakPtrToThis.lock());
|
||||
if( sharedPtrToThis != nullptr )
|
||||
{
|
||||
sharedPtrToThis->EvaluateDevicesForUser(value->User);
|
||||
}
|
||||
});
|
||||
|
||||
//m_tokenSuspending = Windows::ApplicationModel::Core::CoreApplication::Suspending +=
|
||||
// ref new EventHandler< Windows::ApplicationModel::SuspendingEventArgs^ >( [weakPtrToThis] (Platform::Object^, Windows::ApplicationModel::SuspendingEventArgs^ args)
|
||||
//{
|
||||
// // Upon Suspending, nothing needs to be done
|
||||
//});
|
||||
|
||||
//m_tokenResuming = Windows::ApplicationModel::Core::CoreApplication::Resuming +=
|
||||
// ref new EventHandler< Platform::Object^ >( [weakPtrToThis] (Platform::Object^, Windows::ApplicationModel::SuspendingEventArgs^ args)
|
||||
//{
|
||||
// // Upon Resuming, re-initialize the network, and reinitialize the chat session
|
||||
//});
|
||||
}
|
||||
|
||||
|
||||
void ChatIntegrationLayer::Shutdown()
|
||||
{
|
||||
if( m_chatManager != nullptr )
|
||||
{
|
||||
m_chatManager->OnDebugMessage -= m_tokenOnDebugMessage;
|
||||
m_chatManager->OnOutgoingChatPacketReady -= m_tokenOnOutgoingChatPacketReady;
|
||||
m_chatManager->OnCompareUniqueConsoleIdentifiers -= m_tokenOnCompareUniqueConsoleIdentifiers;
|
||||
Windows::ApplicationModel::Core::CoreApplication::ResourceAvailabilityChanged -= m_tokenResourceAvailabilityChanged;
|
||||
if( m_chatManager->ChatSettings->PreEncodeCallbackEnabled )
|
||||
{
|
||||
m_chatManager->OnPreEncodeAudioBuffer -= m_tokenOnPreEncodeAudioBuffer;
|
||||
}
|
||||
if( m_chatManager->ChatSettings->PostDecodeCallbackEnabled )
|
||||
{
|
||||
m_chatManager->OnPostDecodeAudioBuffer -= m_tokenOnPostDecodeAudioBuffer;
|
||||
}
|
||||
WXS::User::AudioDeviceAdded -= m_tokenUserAudioDeviceAdded;
|
||||
WXS::User::AudioDeviceRemoved -= m_tokenUserAudioDeviceRemoved;
|
||||
WXS::User::AudioDeviceChanged -= m_tokenUserAudioDeviceChanged;
|
||||
|
||||
DeleteCriticalSection(&m_csAddedUsers);
|
||||
|
||||
m_chatManager = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::OnDebugMessageReceived(
|
||||
__in Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args
|
||||
)
|
||||
{
|
||||
// To integrate the Chat DLL in your game,
|
||||
// change this to false and remove the LogComment calls,
|
||||
// or integrate with your game's own UI/debug message logging system
|
||||
bool outputToUI = false;
|
||||
|
||||
if( outputToUI )
|
||||
{
|
||||
if (args->ErrorCode == S_OK )
|
||||
{
|
||||
m_pDQRNet->LogComment(L"GameChat: " + args->Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pDQRNet->LogCommentWithError(L"GameChat: " + args->Message, args->ErrorCode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The string appear in the Visual Studio Output window
|
||||
#ifndef _CONTENT_PACKAGE
|
||||
OutputDebugString( args->Message->Data() );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::GameUI_RecordPacketStatistic(
|
||||
__in Microsoft::Xbox::GameChat::ChatMessageType messageType,
|
||||
__in ChatPacketType chatPacketType
|
||||
)
|
||||
{
|
||||
uint32 messageTypeInt = static_cast<uint32>(messageType);
|
||||
if( messageType > Microsoft::Xbox::GameChat::ChatMessageType::InvalidMessage )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_chatPacketStatsLock);
|
||||
m_chatVoicePacketsStatistic[static_cast<int>(chatPacketType)][messageTypeInt]++;
|
||||
}
|
||||
}
|
||||
|
||||
int ChatIntegrationLayer::GameUI_GetPacketStatistic(
|
||||
__in Microsoft::Xbox::GameChat::ChatMessageType messageType,
|
||||
__in ChatPacketType chatPacketType
|
||||
)
|
||||
{
|
||||
uint32 messageTypeInt = static_cast<uint32>(messageType);
|
||||
if( messageType > Microsoft::Xbox::GameChat::ChatMessageType::InvalidMessage )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
{
|
||||
Concurrency::critical_section::scoped_lock lock(m_chatPacketStatsLock);
|
||||
return m_chatVoicePacketsStatistic[static_cast<int>(chatPacketType)][messageTypeInt];
|
||||
}
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::OnOutgoingChatPacketReady(
|
||||
__in Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args
|
||||
)
|
||||
{
|
||||
byte *bytes;
|
||||
int byteCount;
|
||||
|
||||
GetBufferBytes(args->PacketBuffer, &bytes);
|
||||
byteCount = args->PacketBuffer->Length;
|
||||
unsigned int address = 0;
|
||||
if( !args->SendPacketToAllConnectedConsoles )
|
||||
{
|
||||
address = safe_cast<unsigned int>(args->UniqueTargetConsoleIdentifier);
|
||||
}
|
||||
m_pDQRNet->SendBytesChat(address, bytes, byteCount, args->SendReliable, args->SendInOrder, args->SendPacketToAllConnectedConsoles);
|
||||
|
||||
GameUI_RecordPacketStatistic( args->ChatMessageType, ChatPacketType::OutgoingPacket );
|
||||
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::OnIncomingChatMessage(
|
||||
unsigned int sessionAddress,
|
||||
Platform::Array<byte>^ message
|
||||
)
|
||||
{
|
||||
// To integrate the Chat DLL in your game, change the following code to use your game's network layer.
|
||||
// Ignore the OnChatMessageReceived event as that is specific to this sample's simple network layer.
|
||||
// Instead your title should upon receiving a packet, extract the chat message from it, and then call m_chatManager->ProcessIncomingChatMessage as shown below
|
||||
// You will need to isolate chat messages to be unique from the rest of you game's other message types.
|
||||
|
||||
// uniqueRemoteConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types.
|
||||
// What exactly you use doesn't matter, but optimally it would be something that uniquely identifies a console on in the session.
|
||||
// A Windows::Xbox::Networking::SecureDeviceAssociation^ is perfect to use if you have access to it.
|
||||
|
||||
// This is how you would convert from byte array to a IBuffer^
|
||||
//
|
||||
// Windows::Storage::Streams::IBuffer^ destBuffer = ref new Windows::Storage::Streams::Buffer( sourceByteBufferSize );
|
||||
// byte* destBufferBytes = nullptr;
|
||||
// GetBufferBytes( destBuffer, &destBufferBytes );
|
||||
// errno_t err = memcpy_s( destBufferBytes, destBuffer->Capacity, sourceByteBuffer, sourceByteBufferSize );
|
||||
// THROW_HR_IF(err != 0, E_FAIL);
|
||||
// destBuffer->Length = sourceByteBufferSize;
|
||||
|
||||
// This is how you would convert from an int to a Platform::Object^
|
||||
// Platform::Object obj = IntToPlatformObject(5);
|
||||
|
||||
Windows::Storage::Streams::IBuffer^ chatMessage = ArrayToBuffer(message);
|
||||
Platform::Object^ uniqueRemoteConsoleIdentifier = (Platform::Object^)sessionAddress;
|
||||
|
||||
if( m_chatManager != nullptr )
|
||||
{
|
||||
Microsoft::Xbox::GameChat::ChatMessageType chatMessageType = m_chatManager->ProcessIncomingChatMessage(chatMessage, uniqueRemoteConsoleIdentifier);
|
||||
|
||||
GameUI_RecordPacketStatistic( chatMessageType, ChatPacketType::IncomingPacket );
|
||||
}
|
||||
}
|
||||
|
||||
// Only add people who intend to play.
|
||||
void ChatIntegrationLayer::AddAllLocallySignedInUsersToChatClient(
|
||||
__in uint8 channelIndex,
|
||||
__in Windows::Foundation::Collections::IVectorView<Windows::Xbox::System::User^>^ locallySignedInUsers
|
||||
)
|
||||
{
|
||||
// To integrate the Chat DLL in your game,
|
||||
// add all locally signed in users to the chat client
|
||||
for each( Windows::Xbox::System::User^ user in locallySignedInUsers )
|
||||
{
|
||||
if( user != nullptr )
|
||||
{
|
||||
// LogComment(L"Adding Local User to Chat Client");
|
||||
AddLocalUserToChatChannel( channelIndex, user );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChatIntegrationLayer::AddedUser::AddedUser(Windows::Xbox::System::IUser^ user, bool canCaptureAudio)
|
||||
{
|
||||
m_user = user;
|
||||
m_canCaptureAudio = canCaptureAudio;
|
||||
}
|
||||
|
||||
|
||||
void ChatIntegrationLayer::AddLocalUser( __in Windows::Xbox::System::IUser^ user )
|
||||
{
|
||||
// Check we haven't added already
|
||||
for( int i = 0; i < m_addedUsers.size(); i++ )
|
||||
{
|
||||
if( m_addedUsers[i]->m_user->XboxUserId == user->XboxUserId )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool kinectAvailable = false;
|
||||
Windows::Kinect::KinectSensor^ sensor = Windows::Kinect::KinectSensor::GetDefault();
|
||||
if( sensor )
|
||||
{
|
||||
sensor->Open();
|
||||
if( sensor->IsAvailable )
|
||||
{
|
||||
kinectAvailable = true;
|
||||
m_pDQRNet->LogComment(L"Evaluated that kinect is available\n");
|
||||
}
|
||||
sensor->Close();
|
||||
}
|
||||
|
||||
EnterCriticalSection(&m_csAddedUsers);
|
||||
// First establish whether we have an appropriate audio device at this time
|
||||
bool canCaptureAudio = false;
|
||||
for each( WXS::IAudioDeviceInfo^ audioDevice in user->AudioDevices )
|
||||
{
|
||||
m_pDQRNet->LogComment(L"Evaluating device " + audioDevice->DeviceCategory.ToString() + L" " +
|
||||
audioDevice->DeviceType.ToString() + L" " +
|
||||
audioDevice->Id + L" " +
|
||||
audioDevice->Sharing.ToString() + L" " +
|
||||
audioDevice->IsMicrophoneMuted.ToString() + L"\n");
|
||||
|
||||
// Consider shared devices only if kinect is actually available - every machine seems to claim a shared device whether kinect is attached or not
|
||||
if( ( audioDevice->DeviceType == WXS::AudioDeviceType::Capture ) && ( kinectAvailable || ( audioDevice->Sharing != WXS::AudioDeviceSharing::Shared) ) )
|
||||
{
|
||||
canCaptureAudio = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we can capture audio initially, then register with the chat session. Otherwise we'll reevaluate this situation when audio devices change
|
||||
if( canCaptureAudio )
|
||||
{
|
||||
AddLocalUserToChatChannel( 0 , user );
|
||||
}
|
||||
|
||||
// Add to vector of users that we are tracking in the chat system
|
||||
m_addedUsers.push_back(new AddedUser(user, canCaptureAudio));
|
||||
LeaveCriticalSection(&m_csAddedUsers);
|
||||
}
|
||||
|
||||
// Remove from our list of tracked users, if the user is already there
|
||||
void ChatIntegrationLayer::RemoveLocalUser( __in Windows::Xbox::System::IUser^ user )
|
||||
{
|
||||
EnterCriticalSection(&m_csAddedUsers);
|
||||
for( auto it = m_addedUsers.begin(); it != m_addedUsers.end(); it++ )
|
||||
{
|
||||
if( (*it)->m_user->XboxUserId == user->XboxUserId )
|
||||
{
|
||||
delete (*it);
|
||||
m_addedUsers.erase(it);
|
||||
LeaveCriticalSection(&m_csAddedUsers);
|
||||
return;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&m_csAddedUsers);
|
||||
}
|
||||
|
||||
// This is called when the audio devices for a user change in any way, and establishes whether we can now capture audio. Any change in this status from before will cause the user to be added/removed from the chat session.
|
||||
void ChatIntegrationLayer::EvaluateDevicesForUser(__in Windows::Xbox::System::IUser^ user )
|
||||
{
|
||||
bool kinectAvailable = false;
|
||||
Windows::Kinect::KinectSensor^ sensor = Windows::Kinect::KinectSensor::GetDefault();
|
||||
if( sensor )
|
||||
{
|
||||
sensor->Open();
|
||||
if( sensor->IsAvailable )
|
||||
{
|
||||
kinectAvailable = true;
|
||||
m_pDQRNet->LogComment(L"Evaluated that kinect is available\n");
|
||||
}
|
||||
sensor->Close();
|
||||
}
|
||||
|
||||
EnterCriticalSection(&m_csAddedUsers);
|
||||
for( int i = 0; i < m_addedUsers.size(); i++ )
|
||||
{
|
||||
AddedUser *addedUser = m_addedUsers[i];
|
||||
if( addedUser->m_user->XboxUserId == user->XboxUserId )
|
||||
{
|
||||
bool canCaptureAudio = false;
|
||||
for each( WXS::IAudioDeviceInfo^ audioDevice in addedUser->m_user->AudioDevices )
|
||||
{
|
||||
// Consider shared devices only if kinect is actually available - every machine seems to claim a shared device whether kinect is attached or not
|
||||
if( ( audioDevice->DeviceType == WXS::AudioDeviceType::Capture ) && ( kinectAvailable || ( audioDevice->Sharing != WXS::AudioDeviceSharing::Shared) ) )
|
||||
{
|
||||
canCaptureAudio = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( canCaptureAudio != addedUser->m_canCaptureAudio )
|
||||
{
|
||||
if( canCaptureAudio )
|
||||
{
|
||||
AddLocalUserToChatChannel(0, addedUser->m_user );
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveUserFromChatChannel(0, addedUser->m_user );
|
||||
}
|
||||
addedUser->m_canCaptureAudio = canCaptureAudio;
|
||||
LeaveCriticalSection(&m_csAddedUsers);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&m_csAddedUsers);
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::AddLocalUserToChatChannel(
|
||||
__in uint8 channelIndex,
|
||||
__in Windows::Xbox::System::IUser^ user
|
||||
)
|
||||
{
|
||||
// Adds a local user to a specific channel.
|
||||
|
||||
// This is helper function waits for the task to cm_chatManageromplete so shouldn't be called from the UI thread
|
||||
// Remove the .wait() and return the result of concurrency::create_task() if you want to call it from the UI thread
|
||||
// and chain PPL tasks together
|
||||
|
||||
m_pDQRNet->LogComment( L">>>>>>>>>>>>> AddLocalUserToChatChannel" );
|
||||
if( m_chatManager != nullptr )
|
||||
{
|
||||
auto asyncOp = m_chatManager->AddLocalUserToChatChannelAsync( channelIndex, user );
|
||||
concurrency::create_task( asyncOp )
|
||||
.then( [this] ( concurrency::task<void> t )
|
||||
{
|
||||
// Error handling
|
||||
try
|
||||
{
|
||||
t.get();
|
||||
}
|
||||
catch ( Platform::Exception^ ex )
|
||||
{
|
||||
m_pDQRNet->LogCommentWithError( L"AddLocalUserToChatChannelAsync failed", ex->HResult );
|
||||
}
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::RemoveRemoteConsole(
|
||||
unsigned int address
|
||||
)
|
||||
{
|
||||
// uniqueConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types.
|
||||
// What exactly you use doesn't matter, but optimally it would be something that uniquely identifies a console on in the session.
|
||||
// A Windows::Xbox::Networking::SecureDeviceAssociation^ is perfect to use if you have access to it.
|
||||
|
||||
// This is how you would convert from an int to a Platform::Object^
|
||||
// Platform::Object obj = IntToPlatformObject(5);
|
||||
|
||||
// This is helper function waits for the task to complete so shouldn't be called from the UI thread
|
||||
// Remove the .wait() and return the result of concurrency::create_task() if you want to call it from the UI thread
|
||||
// and chain PPL tasks together
|
||||
|
||||
Platform::Object^ uniqueRemoteConsoleIdentifier = (Platform::Object^)address;
|
||||
|
||||
if( m_chatManager != nullptr )
|
||||
{
|
||||
auto asyncOp = m_chatManager->RemoveRemoteConsoleAsync( uniqueRemoteConsoleIdentifier );
|
||||
concurrency::create_task( asyncOp ).then( [this] ( concurrency::task<void> t )
|
||||
{
|
||||
// Error handling
|
||||
try
|
||||
{
|
||||
t.get();
|
||||
}
|
||||
catch ( Platform::Exception^ ex )
|
||||
{
|
||||
m_pDQRNet->LogCommentWithError( L"RemoveRemoteConsoleAsync failed", ex->HResult );
|
||||
}
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::RemoveUserFromChatChannel(
|
||||
__in uint8 channelIndex,
|
||||
__in Windows::Xbox::System::IUser^ user
|
||||
)
|
||||
{
|
||||
if( m_chatManager != nullptr )
|
||||
{
|
||||
// This is helper function waits for the task to complete so shouldn't be called from the UI thread
|
||||
// Remove the .wait() and return the result of concurrency::create_task() if you want to call it from the UI thread
|
||||
// and chain PPL tasks together
|
||||
|
||||
auto asyncOp = m_chatManager->RemoveLocalUserFromChatChannelAsync( channelIndex, user );
|
||||
concurrency::create_task( asyncOp ).then( [this] ( concurrency::task<void> t )
|
||||
{
|
||||
// Error handling
|
||||
try
|
||||
{
|
||||
t.get();
|
||||
}
|
||||
catch ( Platform::Exception^ ex )
|
||||
{
|
||||
m_pDQRNet->LogCommentWithError( L"RemoveLocalUserFromChatChannelAsync failed", ex->HResult );
|
||||
}
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::OnNewSessionAddressAdded(
|
||||
__in unsigned int address
|
||||
)
|
||||
{
|
||||
m_pDQRNet->LogCommentFormat( L">>>>>>>>>>>>> OnNewSessionAddressAdded (%d)",address );
|
||||
Platform::Object^ uniqueConsoleIdentifier = (Platform::Object^)address;
|
||||
/// Call this when a new console connects.
|
||||
/// This adds this console to the chat layer
|
||||
if( m_chatManager != nullptr )
|
||||
{
|
||||
m_chatManager->HandleNewRemoteConsole(uniqueConsoleIdentifier );
|
||||
}
|
||||
}
|
||||
|
||||
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ ChatIntegrationLayer::GetChatUsers()
|
||||
{
|
||||
if( m_chatManager != nullptr )
|
||||
{
|
||||
return m_chatManager->GetChatUsers();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool ChatIntegrationLayer::HasMicFocus()
|
||||
{
|
||||
if( m_chatManager != nullptr )
|
||||
{
|
||||
return m_chatManager->HasMicFocus;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Platform::Object^ ChatIntegrationLayer::IntToPlatformObject(
|
||||
__in int val
|
||||
)
|
||||
{
|
||||
return (Platform::Object^)val;
|
||||
|
||||
// You can also do the same using a PropertyValue.
|
||||
//return Windows::Foundation::PropertyValue::CreateInt32(val);
|
||||
}
|
||||
|
||||
int ChatIntegrationLayer::PlatformObjectToInt(
|
||||
__in Platform::Object^ uniqueRemoteConsoleIdentifier
|
||||
)
|
||||
{
|
||||
return safe_cast<int>( uniqueRemoteConsoleIdentifier );
|
||||
|
||||
// You can also do the same using a PropertyValue.
|
||||
//return safe_cast<Windows::Foundation::IPropertyValue^>(uniqueRemoteConsoleIdentifier)->GetInt32();
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::HandleChatChannelChanged(
|
||||
__in uint8 oldChatChannelIndex,
|
||||
__in uint8 newChatChannelIndex,
|
||||
__in Microsoft::Xbox::GameChat::ChatUser^ chatUser
|
||||
)
|
||||
{
|
||||
// We remember if the local user was currently muted from all channels. And when we switch channels,
|
||||
// we ensure that the state persists. For remote users, title should implement this themselves
|
||||
// based on title game design if they want to persist the muting state.
|
||||
|
||||
bool wasUserMuted = false;
|
||||
IUser^ userBeingRemoved = nullptr;
|
||||
|
||||
if (chatUser != nullptr && chatUser->IsLocal)
|
||||
{
|
||||
wasUserMuted = chatUser->IsMuted;
|
||||
userBeingRemoved = chatUser->User;
|
||||
if (userBeingRemoved != nullptr)
|
||||
{
|
||||
RemoveUserFromChatChannel(oldChatChannelIndex, userBeingRemoved);
|
||||
AddLocalUserToChatChannel(newChatChannelIndex, userBeingRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
// If the local user was muted earlier, get the latest chat users and mute him again on the newly added channel.
|
||||
if (wasUserMuted && userBeingRemoved != nullptr)
|
||||
{
|
||||
auto chatUsers = GetChatUsers();
|
||||
if (chatUsers != nullptr )
|
||||
{
|
||||
for (UINT chatUserIndex = 0; chatUserIndex < chatUsers->Size; chatUserIndex++)
|
||||
{
|
||||
Microsoft::Xbox::GameChat::ChatUser^ chatUser = chatUsers->GetAt(chatUserIndex);
|
||||
if( chatUser != nullptr && (chatUser->XboxUserId == userBeingRemoved->XboxUserId) )
|
||||
{
|
||||
m_chatManager->MuteUserFromAllChannels(chatUser);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::ChangeChatUserMuteState(
|
||||
__in Microsoft::Xbox::GameChat::ChatUser^ chatUser
|
||||
)
|
||||
{
|
||||
/// Helper function to swap the mute state of a specific chat user
|
||||
if( m_chatManager != nullptr && chatUser != nullptr)
|
||||
{
|
||||
if (chatUser->IsMuted)
|
||||
{
|
||||
m_chatManager->UnmuteUserFromAllChannels(chatUser);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_chatManager->MuteUserFromAllChannels(chatUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Microsoft::Xbox::GameChat::ChatUser^ ChatIntegrationLayer::GetChatUserByXboxUserId(
|
||||
__in Platform::String^ xboxUserId
|
||||
)
|
||||
{
|
||||
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ chatUsers = GetChatUsers();
|
||||
for each (Microsoft::Xbox::GameChat::ChatUser^ chatUser in chatUsers)
|
||||
{
|
||||
if (chatUser != nullptr && ( chatUser->XboxUserId == xboxUserId ) )
|
||||
{
|
||||
return chatUser;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool ChatIntegrationLayer::CompareUniqueConsoleIdentifiers(
|
||||
__in Platform::Object^ uniqueRemoteConsoleIdentifier1,
|
||||
__in Platform::Object^ uniqueRemoteConsoleIdentifier2
|
||||
)
|
||||
{
|
||||
if (uniqueRemoteConsoleIdentifier1 == nullptr || uniqueRemoteConsoleIdentifier2 == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// uniqueRemoteConsoleIdentifier is a Platform::Object^ and can be cast or unboxed to most types.
|
||||
// We're using XRNS addresses, which are unsigned ints
|
||||
unsigned int address1 = safe_cast<unsigned int>(uniqueRemoteConsoleIdentifier1);
|
||||
unsigned int address2 = safe_cast<unsigned int>(uniqueRemoteConsoleIdentifier2);
|
||||
return address1 == address2;
|
||||
}
|
||||
|
||||
Microsoft::Xbox::GameChat::ChatPerformanceCounters^ ChatIntegrationLayer::GetChatPerformanceCounters()
|
||||
{
|
||||
if( m_chatManager != nullptr &&
|
||||
m_chatManager->ChatSettings != nullptr &&
|
||||
m_chatManager->ChatSettings->PerformanceCountersEnabled )
|
||||
{
|
||||
return m_chatManager->ChatPerformanceCounters;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::OnControllerPairingChanged( Windows::Xbox::Input::ControllerPairingChangedEventArgs^ args )
|
||||
{
|
||||
#if 0
|
||||
auto controller = args->Controller;
|
||||
if ( controller )
|
||||
{
|
||||
if ( controller->Type == L"Windows.Xbox.Input.Gamepad" )
|
||||
{
|
||||
// Either add the user or sign one in
|
||||
User^ user = args->User;
|
||||
if ( user != nullptr )
|
||||
{
|
||||
g_sampleInstance->GetLoggingUI()->LogCommentToUI("OnControllerPairingChanged: " + user->DisplayInfo->Gamertag);
|
||||
AddLocalUserToChatChannel(
|
||||
g_sampleInstance->GetUISelectionChatChannelIndex(),
|
||||
user
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ChatIntegrationLayer::ToggleRenderTargetVolume()
|
||||
{
|
||||
// Simple toggle logic to just show usage of LocalRenderTargetVolume property
|
||||
static bool makeRenderTargetVolumeQuiet = false;
|
||||
makeRenderTargetVolumeQuiet = !makeRenderTargetVolumeQuiet;
|
||||
|
||||
auto chatUsers = GetChatUsers();
|
||||
if (chatUsers != nullptr )
|
||||
{
|
||||
for (UINT chatUserIndex = 0; chatUserIndex < chatUsers->Size; chatUserIndex++)
|
||||
{
|
||||
Microsoft::Xbox::GameChat::ChatUser^ chatUser = chatUsers->GetAt(chatUserIndex);
|
||||
if( chatUser != nullptr && chatUser->IsLocal )
|
||||
{
|
||||
chatUser->LocalRenderTargetVolume = ( makeRenderTargetVolumeQuiet ) ? 0.1f : 1.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ChatIntegrationLayer::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);
|
||||
}
|
||||
|
||||
Windows::Storage::Streams::IBuffer^ ChatIntegrationLayer::ArrayToBuffer( __in Platform::Array<byte>^ array )
|
||||
{
|
||||
Windows::Storage::Streams::DataWriter^ writer = ref new Windows::Storage::Streams::DataWriter();
|
||||
writer->WriteBytes(array);
|
||||
return writer->DetachBuffer();
|
||||
}
|
||||
244
Minecraft.Client/Durango/Network/ChatIntegrationLayer.h
Normal file
244
Minecraft.Client/Durango/Network/ChatIntegrationLayer.h
Normal file
@@ -0,0 +1,244 @@
|
||||
//// 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 "UserController.h"
|
||||
|
||||
class DQRNetworkManager;
|
||||
|
||||
enum ChatPacketType
|
||||
{
|
||||
IncomingPacket = 0,
|
||||
OutgoingPacket = 1
|
||||
};
|
||||
|
||||
class ChatIntegrationLayer
|
||||
: public std::enable_shared_from_this<ChatIntegrationLayer> // shared_from_this is needed to use a weak ref to 'this' when handling delegate callbacks
|
||||
{
|
||||
public:
|
||||
ChatIntegrationLayer();
|
||||
|
||||
DQRNetworkManager *m_pDQRNet;
|
||||
/// <summary>
|
||||
/// Initializes the chat manager
|
||||
/// </summary>
|
||||
void InitializeChatManager(
|
||||
__in bool combineCaptureBuffersIntoSinglePacketEnabled,
|
||||
__in bool useKinectAsCaptureSource,
|
||||
__in bool applySoundEffectToCapture,
|
||||
__in bool applySoundEffectToRender,
|
||||
DQRNetworkManager *pDQRNet
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down the chat manager
|
||||
/// </summary>
|
||||
void Shutdown();
|
||||
|
||||
class AddedUser
|
||||
{
|
||||
public:
|
||||
AddedUser(Windows::Xbox::System::IUser^ user, bool canCaptureAudio);
|
||||
Windows::Xbox::System::IUser^ m_user;
|
||||
bool m_canCaptureAudio;
|
||||
};
|
||||
|
||||
void AddLocalUser( __in Windows::Xbox::System::IUser^ user );
|
||||
void RemoveLocalUser( __in Windows::Xbox::System::IUser^ user );
|
||||
void EvaluateDevicesForUser(__in Windows::Xbox::System::IUser^ user );
|
||||
|
||||
vector<AddedUser *> m_addedUsers;
|
||||
CRITICAL_SECTION m_csAddedUsers;
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Adds a local user to a specific channel
|
||||
/// This is helper function waits for the task to complete so shouldn't be called from the UI thread
|
||||
/// </summary>
|
||||
/// <param name="channelIndex">The channel to add the user to</param>
|
||||
/// <param name="user">The local user to add</param>
|
||||
void AddLocalUserToChatChannel(
|
||||
__in uint8 channelIndex,
|
||||
__in Windows::Xbox::System::IUser^ user
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a local user from a specific channel
|
||||
/// This is helper function waits for the task to complete so shouldn't be called from the UI thread
|
||||
/// </summary>
|
||||
/// <param name="channelIndex">The channel to remove the user from</param>
|
||||
/// <param name="user">The local user to remove</param>
|
||||
void RemoveUserFromChatChannel(
|
||||
__in uint8 channelIndex,
|
||||
__in Windows::Xbox::System::IUser^ user
|
||||
);
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Removes a remote console from chat
|
||||
/// This is helper function waits for the task to complete so shouldn't be called from the UI thread
|
||||
/// </summary>
|
||||
/// <param name="uniqueRemoteConsoleIdentifier">A unique ID for the remote console</param>
|
||||
void RemoveRemoteConsole(
|
||||
unsigned int address
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Handles incoming chat messages from the game's network layer
|
||||
/// </summary>
|
||||
/// <param name="chatPacket">A buffer containing the chat message</param>
|
||||
/// <param name="uniqueRemoteConsoleIdentifier">A unique ID for the remote console</param>
|
||||
void OnIncomingChatMessage(
|
||||
unsigned int sessionAddress,
|
||||
Platform::Array<byte>^ message
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of chat users in the chat session
|
||||
/// </summary>
|
||||
Windows::Foundation::Collections::IVectorView<Microsoft::Xbox::GameChat::ChatUser^>^ GetChatUsers();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the game has mic focus. Otherwise another app has mic focus
|
||||
/// </summary>
|
||||
bool HasMicFocus();
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to swap the mute state of a specific chat user
|
||||
/// </summary>
|
||||
void ChangeChatUserMuteState(
|
||||
__in Microsoft::Xbox::GameChat::ChatUser^ chatUser
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to change the channel of a specific chat user
|
||||
/// </summary>
|
||||
void HandleChatChannelChanged(
|
||||
__in uint8 oldChatChannelIndex,
|
||||
__in uint8 newChatChannelIndex,
|
||||
__in Microsoft::Xbox::GameChat::ChatUser^ chatUser
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Call this when a new console connects.
|
||||
/// This adds this console to the chat layer
|
||||
/// </summary>
|
||||
void OnNewSessionAddressAdded(
|
||||
__in unsigned int address
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a list of locally signed in users that have intent to play to the chat session on a specific channel index.
|
||||
/// Avoid adding any user who is signed in that doesn't have intent to play otherwise users who are biometrically
|
||||
/// signed in automatically will be added to the chat session
|
||||
/// </summary>
|
||||
/// <param name="channelIndex">The channel to add the users to</param>
|
||||
/// <param name="locallySignedInUsers">A list of locally signed in users that have intent to play</param>
|
||||
void AddAllLocallySignedInUsersToChatClient(
|
||||
__in uint8 channelIndex,
|
||||
__in Windows::Foundation::Collections::IVectorView<Windows::Xbox::System::User^>^ locallySignedInUsers
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Handles when a debug message is received. Send this to the UI and OutputDebugString. Games should integrate with their existing log system.
|
||||
/// </summary>
|
||||
/// <param name="args">Contains the debug message to log</param>
|
||||
void OnDebugMessageReceived(
|
||||
__in Microsoft::Xbox::GameChat::DebugMessageEventArgs^ args
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Send the chat packet to all connected consoles
|
||||
///
|
||||
/// To integrate the Chat DLL in your game, change the following code to use your game's network layer.
|
||||
/// You will need to isolate chat messages to be unique from the rest of you game's other message types.
|
||||
/// When args->SendPacketToAllConnectedConsoles is true, your game should send the chat message to each connected console using the game's network layer.
|
||||
/// It should send the chat message with Reliable UDP if args->SendReliable is true.
|
||||
/// It should send the chat message in order (if that feature is available) if args->SendInOrder is true
|
||||
/// </summary>
|
||||
/// <param name="args">Describes the packet to send</param>
|
||||
void OnOutgoingChatPacketReady(
|
||||
__in Microsoft::Xbox::GameChat::ChatPacketEventArgs^ args
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Example of how to cast an int to a Platform::Object^
|
||||
/// </summary>
|
||||
Platform::Object^ IntToPlatformObject(
|
||||
__in int val
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Example of how to cast an Platform::Object^ to an int
|
||||
/// </summary>
|
||||
int PlatformObjectToInt(
|
||||
__in Platform::Object^ obj
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to get specific ChatUser by xboxUserId
|
||||
/// </summary>
|
||||
Microsoft::Xbox::GameChat::ChatUser^ GetChatUserByXboxUserId(
|
||||
__in Platform::String^ xboxUserId
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to get specific ChatUser by xboxUserId
|
||||
/// </summary>
|
||||
bool ChatIntegrationLayer::CompareUniqueConsoleIdentifiers(
|
||||
__in Platform::Object^ uniqueRemoteConsoleIdentifier1,
|
||||
__in Platform::Object^ uniqueRemoteConsoleIdentifier2
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Helper function to return the ChatPerformanceCounters^ from the ChatManager so perf numbers can be shown on the UI
|
||||
/// These numbers will only be valid if m_chatManager->ChatSettings->PerformanceCountersEnabled is set to true.
|
||||
/// </summary>
|
||||
Microsoft::Xbox::GameChat::ChatPerformanceCounters^ GetChatPerformanceCounters();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a count of the number of chat packets of a specific type that have been either sent or received.
|
||||
/// It is useful to monitor this number in the UI / logs to debug network issues.
|
||||
/// </summary>
|
||||
int GameUI_GetPacketStatistic(
|
||||
__in Microsoft::Xbox::GameChat::ChatMessageType messageType,
|
||||
__in ChatPacketType chatPacketType
|
||||
);
|
||||
|
||||
void OnControllerPairingChanged(
|
||||
__in Windows::Xbox::Input::ControllerPairingChangedEventArgs^ args
|
||||
);
|
||||
|
||||
void ToggleRenderTargetVolume();
|
||||
|
||||
private:
|
||||
void GetBufferBytes( __in Windows::Storage::Streams::IBuffer^ buffer, __out byte** ppOut );
|
||||
Windows::Storage::Streams::IBuffer^ ArrayToBuffer( __in Platform::Array<byte>^ array );
|
||||
|
||||
Concurrency::critical_section m_lock;
|
||||
Microsoft::Xbox::GameChat::ChatManager^ m_chatManager;
|
||||
Windows::Foundation::EventRegistrationToken m_tokenOnDebugMessage;
|
||||
Windows::Foundation::EventRegistrationToken m_tokenOnOutgoingChatPacketReady;
|
||||
Windows::Foundation::EventRegistrationToken m_tokenOnCompareUniqueConsoleIdentifiers;
|
||||
Windows::Foundation::EventRegistrationToken m_tokenResourceAvailabilityChanged;
|
||||
Windows::Foundation::EventRegistrationToken m_tokenOnPreEncodeAudioBuffer;
|
||||
Windows::Foundation::EventRegistrationToken m_tokenOnPostDecodeAudioBuffer;
|
||||
Windows::Foundation::EventRegistrationToken m_tokenUserAudioDeviceAdded;
|
||||
Windows::Foundation::EventRegistrationToken m_tokenUserAudioDeviceRemoved;
|
||||
Windows::Foundation::EventRegistrationToken m_tokenUserAudioDeviceChanged;
|
||||
|
||||
// Debug stats for chat packets. Use Debug_GetPacketStatistic() to get the values.
|
||||
/// It is useful to monitor this number in the UI / logs to debug network issues.
|
||||
void GameUI_RecordPacketStatistic(
|
||||
__in Microsoft::Xbox::GameChat::ChatMessageType messageType,
|
||||
__in ChatPacketType chatPacketType
|
||||
);
|
||||
Concurrency::critical_section m_chatPacketStatsLock;
|
||||
int m_chatVoicePacketsStatistic[2][(int)Microsoft::Xbox::GameChat::ChatMessageType::InvalidMessage+1];
|
||||
};
|
||||
|
||||
std::shared_ptr<ChatIntegrationLayer> GetChatIntegrationLayer();
|
||||
3093
Minecraft.Client/Durango/Network/DQRNetworkManager.cpp
Normal file
3093
Minecraft.Client/Durango/Network/DQRNetworkManager.cpp
Normal file
File diff suppressed because it is too large
Load Diff
582
Minecraft.Client/Durango/Network/DQRNetworkManager.h
Normal file
582
Minecraft.Client/Durango/Network/DQRNetworkManager.h
Normal file
@@ -0,0 +1,582 @@
|
||||
#pragma once
|
||||
|
||||
#include "DQRNetworkPlayer.h"
|
||||
#include "..\Minecraft.World\C4JThread.h"
|
||||
#include <collection.h>
|
||||
|
||||
class IDQRNetworkManagerListener;
|
||||
class PartyController;
|
||||
|
||||
class DQRNetworkManager;
|
||||
class ChatIntegrationLayer;
|
||||
|
||||
namespace MXS = Microsoft::Xbox::Services;
|
||||
namespace MXSM = Microsoft::Xbox::Services::Multiplayer;
|
||||
namespace MXSS = Microsoft::Xbox::Services::Social;
|
||||
namespace WXM = Windows::Xbox::Multiplayer;
|
||||
namespace WXN = Windows::Xbox::Networking;
|
||||
namespace WXNRs = Windows::Xbox::Networking::RealtimeSession;
|
||||
namespace WFC = Windows::Foundation::Collections;
|
||||
|
||||
#define MATCH_SESSION_TEMPLATE_NAME L"PeerToHostTemplate"
|
||||
#define MAX_PLAYERS_IN_TEMPLATE 8
|
||||
|
||||
using namespace std;
|
||||
|
||||
ref class DQRNetworkManagerEventHandlers sealed
|
||||
{
|
||||
internal:
|
||||
DQRNetworkManagerEventHandlers(DQRNetworkManager *pDQRNet);
|
||||
public:
|
||||
void Setup(WXNRs::Session^ session);
|
||||
void Pulldown(WXNRs::Session^ session);
|
||||
|
||||
void DataReceivedHandler(Platform::Object^ session, WXNRs::DataReceivedEventArgs^ args);
|
||||
void SessionAddressDataChangedHandler(Platform::Object^ session, WXNRs::SessionAddressDataChangedEventArgs^ args);
|
||||
void SessionStatusUpdateHandler(Platform::Object^ session, WXNRs::SessionStatusUpdateEventArgs^ args);
|
||||
void AddedSessionAddressHandler(Platform::Object^ session, WXNRs::AddedSessionAddressEventArgs^ args);
|
||||
void RemovedSessionAddressHandler(Platform::Object^ session, WXNRs::RemovedSessionAddressEventArgs^ args);
|
||||
void GlobalSessionDataChangedHandler(Platform::Object^ session, WXNRs::GlobalSessionDataChangedEventArgs^ args);
|
||||
|
||||
private:
|
||||
Windows::Foundation::EventRegistrationToken m_dataReceivedToken;
|
||||
Windows::Foundation::EventRegistrationToken m_sessionStatusToken;
|
||||
Windows::Foundation::EventRegistrationToken m_sessionAddressToken;
|
||||
Windows::Foundation::EventRegistrationToken m_addedSessionToken;
|
||||
Windows::Foundation::EventRegistrationToken m_removedSessionToken;
|
||||
Windows::Foundation::EventRegistrationToken m_globalDataToken;
|
||||
|
||||
DQRNetworkManager *m_pDQRNet;
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DQR_INTERNAL_ASSIGN_SMALL_IDS,
|
||||
DQR_INTERNAL_UNASSIGN_SMALL_ID,
|
||||
DQR_INTERNAL_PLAYER_TABLE,
|
||||
DQR_INTERNAL_ADD_PLAYER_FAILED,
|
||||
};
|
||||
|
||||
class DQRConnectionInfo
|
||||
{
|
||||
public:
|
||||
typedef enum
|
||||
{
|
||||
ConnectionState_HeaderByte0,
|
||||
ConnectionState_HeaderByte1,
|
||||
ConnectionState_ReadBytes
|
||||
} eDQRConnectionState;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ConnectionState_InternalHeaderByte,
|
||||
ConnectionState_InternalAssignSmallIdMask,
|
||||
ConnectionState_InternalAssignSmallId0,
|
||||
ConnectionState_InternalAssignSmallId1,
|
||||
ConnectionState_InternalAssignSmallId2,
|
||||
ConnectionState_InternalAssignSmallId3,
|
||||
ConnectionState_InternalRoomSyncData,
|
||||
ConnectionState_InternalAddPlayerFailedData,
|
||||
} eDQRInternalDataState;
|
||||
|
||||
DQRConnectionInfo();
|
||||
void Reset();
|
||||
|
||||
eDQRConnectionState m_state;
|
||||
eDQRInternalDataState m_internalDataState;
|
||||
|
||||
int m_currentChannel;
|
||||
bool m_internalFlag;
|
||||
int m_bytesRemaining;
|
||||
int m_roomSyncDataBytesRead;
|
||||
int m_roomSyncDataBytesToRead;
|
||||
unsigned char * m_pucRoomSyncData;
|
||||
int m_addFailedPlayerDataBytesRead;
|
||||
int m_addFailedPlayerDataBytesToRead;
|
||||
unsigned char * m_pucAddFailedPlayerData;
|
||||
unsigned char m_smallId[4];
|
||||
bool m_channelActive[4];
|
||||
unsigned char m_smallIdReadMask;
|
||||
unsigned char *m_pucsmallIdReadMaskResolved;
|
||||
bool m_initialPacketReceived;
|
||||
};
|
||||
|
||||
|
||||
class DQRNetworkManager
|
||||
{
|
||||
friend class PartyController;
|
||||
friend ref class DQRNetworkManagerEventHandlers;
|
||||
friend class DQRNetworkPlayer;
|
||||
friend class ChatIntegrationLayer;
|
||||
public:
|
||||
static const int JOIN_PACKET_RESEND_DELAY_MS = 200;
|
||||
static const int JOIN_PACKET_RESEND_TIMEOUT_MS = 20000;
|
||||
static const int JOIN_RESERVATION_WAIT_TIME = 30000;
|
||||
static const int JOIN_CREATE_SESSION_MAX_ATTEMPTS = 5;
|
||||
|
||||
static const int PRIMARY_PLAYER_LEAVING_PARTY_WAIT_MS = 20000; // Time between primary player leaving and when we should respond to it, to allow time for us to receive a new party for them to be going to if they are just changing party rather than leaving altogether
|
||||
|
||||
class SessionInfo
|
||||
{
|
||||
public:
|
||||
SessionInfo(wstring& sessionName, wstring& serviceConfig, wstring& sessionTemplate);
|
||||
SessionInfo();
|
||||
bool m_detailsValid;
|
||||
wstring m_sessionName;
|
||||
wstring m_serviceConfig;
|
||||
wstring m_sessionTemplate;
|
||||
};
|
||||
|
||||
static const int MAX_LOCAL_PLAYER_COUNT = 4;
|
||||
static const int MAX_ONLINE_PLAYER_COUNT = 8;
|
||||
static const int MAX_ONLINE_PLAYER_NAME_LENGTH = 21;
|
||||
|
||||
// This class stores everything about a player that must be synchronised between machines.
|
||||
class PlayerSyncData
|
||||
{
|
||||
public:
|
||||
wchar_t *m_XUID; // XUID / XboxUserIds are passed round as decimal strings on Xbox 1
|
||||
uint32_t m_sessionAddress; // XRNS session address for this player, ie can identify which machine this player is on
|
||||
uint8_t m_smallId; // Assigned by DQRNetworkManager, to attach a permanent id to this player (until we have to wrap round), to match a similar concept in qnet
|
||||
uint8_t m_channel; // Local index / communication channel within session address, of player on this machine
|
||||
wchar_t m_name[MAX_ONLINE_PLAYER_NAME_LENGTH];
|
||||
};
|
||||
|
||||
class RoomSyncData
|
||||
{
|
||||
public:
|
||||
int playerCount;
|
||||
PlayerSyncData players[MAX_ONLINE_PLAYER_COUNT];
|
||||
};
|
||||
|
||||
class HostGamertagResolveDetails
|
||||
{
|
||||
public:;
|
||||
DQRNetworkPlayer* m_pPlayer;
|
||||
wstring m_name;
|
||||
unsigned int m_sessionAddress;
|
||||
int m_channel;
|
||||
bool m_sync;
|
||||
};
|
||||
|
||||
DQRNetworkManager(IDQRNetworkManagerListener *listener);
|
||||
void Initialise();
|
||||
void EnableDebugXBLContext(MXS::XboxLiveContext^ XBLContext);
|
||||
|
||||
void CreateAndJoinSession(int userMask, unsigned char *customSessionData, unsigned int customSessionDataSize, bool offline);
|
||||
void JoinSession(int playerMask);
|
||||
void JoinSessionFromInviteInfo(int playerMask);
|
||||
bool AddUsersToSession(int playerMask, MXSM::MultiplayerSessionReference^ sessionRef );
|
||||
void UpdateCustomSessionData();
|
||||
|
||||
bool AddLocalPlayerByUserIndex(int userIndex);
|
||||
bool RemoveLocalPlayerByUserIndex(int userIndex);
|
||||
|
||||
bool IsHost();
|
||||
bool IsInSession();
|
||||
|
||||
// Player retrieval
|
||||
int GetPlayerCount();
|
||||
int GetOnlinePlayerCount();
|
||||
DQRNetworkPlayer *GetPlayerByIndex(int idx);
|
||||
DQRNetworkPlayer *GetPlayerBySmallId(int idx);
|
||||
DQRNetworkPlayer *GetPlayerByXuid(PlayerUID xuid);
|
||||
wstring GetDisplayNameByGamertag(wstring gamertag);
|
||||
DQRNetworkPlayer *GetLocalPlayerByUserIndex(int idx);
|
||||
DQRNetworkPlayer *GetHostPlayer();
|
||||
int GetSessionIndex(DQRNetworkPlayer *player);
|
||||
void Tick();
|
||||
void Tick_XRNS();
|
||||
void Tick_VoiceChat();
|
||||
void Tick_Party();
|
||||
void Tick_CustomSessionData();
|
||||
void Tick_AddAndRemoveLocalPlayers();
|
||||
void Tick_ResolveGamertags();
|
||||
void Tick_PartyProcess();
|
||||
void Tick_StateMachine();
|
||||
void Tick_CheckInviteParty();
|
||||
|
||||
bool ShouldMessageForFullSession();
|
||||
void FlagInvitedToFullSession();
|
||||
void UnflagInvitedToFullSession();
|
||||
// Externally exposed state. All internal states are mapped to one of these broader states.
|
||||
typedef enum
|
||||
{
|
||||
DNM_STATE_INITIALISING,
|
||||
DNM_STATE_INITIALISE_FAILED,
|
||||
DNM_STATE_IDLE,
|
||||
|
||||
DNM_STATE_HOSTING,
|
||||
DNM_STATE_JOINING,
|
||||
|
||||
DNM_STATE_STARTING,
|
||||
DNM_STATE_PLAYING,
|
||||
|
||||
DNM_STATE_LEAVING,
|
||||
DNM_STATE_ENDING,
|
||||
} eDQRNetworkManagerState;
|
||||
|
||||
eDQRNetworkManagerState GetState();
|
||||
|
||||
class SessionSearchResult
|
||||
{
|
||||
public:
|
||||
SessionSearchResult() { m_extData = NULL; }
|
||||
~SessionSearchResult() { free(m_extData); }
|
||||
wstring m_partyId;
|
||||
wstring m_sessionName;
|
||||
|
||||
// These names/xuids reflect the server controlled list of who is actually in the game
|
||||
wstring m_playerNames[MAX_ONLINE_PLAYER_COUNT];
|
||||
PlayerUID m_playerXuids[MAX_ONLINE_PLAYER_COUNT];
|
||||
int m_playerCount;
|
||||
|
||||
// This count & set of xuids reflects the session document list of who is in the game
|
||||
wstring m_sessionXuids[MAX_ONLINE_PLAYER_COUNT];
|
||||
int m_usedSlotCount;
|
||||
|
||||
void *m_extData;
|
||||
};
|
||||
|
||||
protected:
|
||||
// NOTE: If anything changes in here, then the mapping from internal -> external state needs to be updated (m_INTtoEXTStateMappings, defined in the cpp file)
|
||||
typedef enum
|
||||
{
|
||||
DNM_INT_STATE_INITIALISING,
|
||||
DNM_INT_STATE_INITIALISE_FAILED,
|
||||
DNM_INT_STATE_IDLE,
|
||||
DNM_INT_STATE_HOSTING,
|
||||
DNM_INT_STATE_HOSTING_WAITING_TO_PLAY,
|
||||
DNM_INT_STATE_HOSTING_FAILED,
|
||||
DNM_INT_STATE_JOINING,
|
||||
DNM_INT_STATE_JOINING_WAITING_FOR_RESERVATIONS,
|
||||
DNM_INT_STATE_JOINING_GET_SDA,
|
||||
DNM_INT_STATE_JOINING_WAITING_FOR_SDA,
|
||||
DNM_INT_STATE_JOINING_CREATE_SESSION,
|
||||
DNM_INT_STATE_JOINING_WAITING_FOR_ACTIVE_SESSION,
|
||||
DNM_INT_STATE_JOINING_SENDING_UNRELIABLE,
|
||||
DNM_INT_STATE_JOINING_FAILED_TIDY_UP,
|
||||
DNM_INT_STATE_JOINING_FAILED,
|
||||
DNM_INT_STATE_STARTING,
|
||||
DNM_INT_STATE_PLAYING,
|
||||
DNM_INT_STATE_LEAVING,
|
||||
DNM_INT_STATE_LEAVING_FAILED,
|
||||
DNM_INT_STATE_ENDING,
|
||||
DNM_INT_STATE_COUNT
|
||||
} eDQRNetworkManagerInternalState;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DNM_ADD_PLAYER_STATE_IDLE,
|
||||
DNM_ADD_PLAYER_STATE_PROCESSING,
|
||||
DNM_ADD_PLAYER_STATE_COMPLETE_SUCCESS,
|
||||
DNM_ADD_PLAYER_STATE_COMPLETE_FAIL,
|
||||
DNM_ADD_PLAYER_STATE_COMPLETE_FAIL_FULL,
|
||||
} eDQRAddLocalPlayerState;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DNM_REMOVE_PLAYER_STATE_IDLE,
|
||||
DNM_REMOVE_PLAYER_STATE_PROCESSING,
|
||||
DNM_REMOVE_PLAYER_STATE_COMPLETE_SUCCESS,
|
||||
DNM_REMOVE_PLAYER_STATE_COMPLETE_FAIL,
|
||||
} eDQRRemoveLocalPlayerState;
|
||||
|
||||
class StateChangeInfo
|
||||
{
|
||||
public:
|
||||
eDQRNetworkManagerState m_oldState;
|
||||
eDQRNetworkManagerState m_newState;
|
||||
StateChangeInfo(eDQRNetworkManagerState oldState, eDQRNetworkManagerState newState) : m_oldState(oldState), m_newState(newState) {}
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
// Incoming messages
|
||||
RTS_MESSAGE_DATA_RECEIVED,
|
||||
RTS_MESSAGE_DATA_RECEIVED_CHAT,
|
||||
RTS_MESSAGE_ADDED_SESSION_ADDRESS,
|
||||
RTS_MESSAGE_REMOVED_SESSION_ADDRESS,
|
||||
RTS_MESSAGE_STATUS_ACTIVE,
|
||||
RTS_MESSAGE_STATUS_TERMINATED,
|
||||
|
||||
// Outgoing messages
|
||||
RTS_MESSAGE_START_CLIENT,
|
||||
RTS_MESSAGE_START_HOST,
|
||||
RTS_MESSAGE_TERMINATE,
|
||||
RTS_MESSAGE_SEND_DATA,
|
||||
} eRTSMessageType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
RTS_MESSAGE_FLAG_BROADCAST_MODE = 1,
|
||||
RTS_MESSAGE_FLAG_RELIABLE = 2,
|
||||
RTS_MESSAGE_FLAG_SEQUENTIAL = 4,
|
||||
RTS_MESSAGE_FLAG_COALESCE = 8,
|
||||
RTS_MESSAGE_FLAG_GAME_CHANNEL = 16,
|
||||
} eRTSFlags;
|
||||
|
||||
class RTS_Message
|
||||
{
|
||||
public:
|
||||
eRTSMessageType m_eType;
|
||||
unsigned int m_sessionAddress;
|
||||
unsigned char *m_pucData;
|
||||
unsigned int m_dataSize;
|
||||
unsigned int m_flags;
|
||||
};
|
||||
|
||||
std::queue<StateChangeInfo> m_stateChangeQueue;
|
||||
CRITICAL_SECTION m_csStateChangeQueue;
|
||||
CRITICAL_SECTION m_csSendBytes;
|
||||
CRITICAL_SECTION m_csPartyViewVector;
|
||||
std::queue<RTS_Message> m_RTSMessageQueueIncoming;
|
||||
CRITICAL_SECTION m_csRTSMessageQueueIncoming;
|
||||
std::queue<RTS_Message> m_RTSMessageQueueOutgoing;
|
||||
CRITICAL_SECTION m_csRTSMessageQueueOutgoing;
|
||||
private:
|
||||
void SetState(DQRNetworkManager::eDQRNetworkManagerInternalState state);
|
||||
static const eDQRNetworkManagerState m_INTtoEXTStateMappings[DNM_INT_STATE_COUNT];
|
||||
eDQRNetworkManagerInternalState m_state;
|
||||
eDQRNetworkManagerState m_stateExternal;
|
||||
__int64 m_lastUnreliableSendTime;
|
||||
__int64 m_firstUnreliableSendTime;
|
||||
__int64 m_startedWaitingForReservationsTime;
|
||||
unsigned char *m_customSessionData;
|
||||
unsigned int m_customSessionDataSize;
|
||||
int m_customDataDirtyUpdateTicks;
|
||||
std::shared_ptr<ChatIntegrationLayer> m_chat;
|
||||
|
||||
eDQRAddLocalPlayerState m_addLocalPlayerState;
|
||||
DQRNetworkPlayer *m_addLocalPlayerSuccessPlayer;
|
||||
int m_addLocalPlayerSuccessIndex;
|
||||
int m_addLocalPlayerFailedIndex;
|
||||
|
||||
eDQRRemoveLocalPlayerState m_removeLocalPlayerState;
|
||||
int m_removeLocalPlayerIndex;
|
||||
|
||||
Windows::Xbox::System::User^ m_primaryUser;
|
||||
MXS::XboxLiveContext^ m_primaryUserXboxLiveContext;
|
||||
WXN::SecureDeviceAssociationTemplate^ m_associationTemplate;
|
||||
|
||||
CRITICAL_SECTION m_csRoomSyncData;
|
||||
RoomSyncData m_roomSyncData;
|
||||
DQRNetworkPlayer *m_players[MAX_ONLINE_PLAYER_COUNT];
|
||||
|
||||
IDQRNetworkManagerListener *m_listener;
|
||||
PartyController *m_partyController;
|
||||
DQRNetworkManagerEventHandlers^ m_eventHandlers;
|
||||
WXNRs::Session^ m_XRNS_Session;
|
||||
unsigned int m_XRNS_LocalAddress;
|
||||
unsigned int m_XRNS_OldestAddress;
|
||||
MXSM::MultiplayerSession^ m_multiplayerSession;
|
||||
WXN::SecureDeviceAssociation^ m_sda;
|
||||
Platform::String^ m_secureDeviceAddressBase64;
|
||||
unsigned char m_currentSmallId;
|
||||
unsigned char m_hostSmallId;
|
||||
bool m_isHosting;
|
||||
bool m_isInSession;
|
||||
bool m_isOfflineGame;
|
||||
public:
|
||||
int m_currentUserMask;
|
||||
private:
|
||||
int m_joinSessionUserMask;
|
||||
Platform::Array<Platform::String^>^ m_joinSessionXUIDs;
|
||||
int m_joinSmallIdMask;
|
||||
|
||||
Platform::Array<BYTE>^ m_remoteSocketAddress;
|
||||
Platform::Array<BYTE>^ m_localSocketAddress;
|
||||
int m_joinCreateSessionAttempts;
|
||||
|
||||
C4JThread *m_CreateSessionThread;
|
||||
C4JThread *m_LeaveRoomThread;
|
||||
C4JThread *m_TidyUpJoinThread;
|
||||
C4JThread *m_UpdateCustomSessionDataThread;
|
||||
C4JThread *m_RTS_DoWorkThread;
|
||||
C4JThread *m_CheckPartyInviteThread;
|
||||
|
||||
bool m_notifyForFullParty;
|
||||
|
||||
unordered_map<unsigned int,DQRConnectionInfo *> m_sessionAddressToConnectionInfoMapHost; // For host - there may be more than one remote session attached to this listening session
|
||||
unsigned int m_sessionAddressFromSmallId[256]; // For host - for mapping back from small Id, to session address
|
||||
unsigned char m_channelFromSmallId[256]; // For host and client, for mapping back from small Id, to channel
|
||||
DQRConnectionInfo m_connectionInfoClient; // For client
|
||||
unsigned int m_hostSessionAddress; // For client
|
||||
|
||||
CRITICAL_SECTION m_csHostGamertagResolveResults;
|
||||
queue<HostGamertagResolveDetails *> m_hostGamertagResolveResults;
|
||||
|
||||
void AddPlayerFailed(Platform::String ^xuid);
|
||||
Platform::String^ RemoveBracesFromGuidString(__in Platform::String^ guid );
|
||||
void HandleSessionChange( MXSM::MultiplayerSession^ session );
|
||||
MXSM::MultiplayerSession^ WriteSessionHelper( MXS::XboxLiveContext^ xboxLiveContext,
|
||||
MXSM::MultiplayerSession^ multiplayerSession,
|
||||
MXSM::MultiplayerSessionWriteMode writeMode,
|
||||
HRESULT& hr);
|
||||
MXSM::MultiplayerSessionMember^ GetUserMemberInSession( Windows::Xbox::System::User^ user, MXSM::MultiplayerSession^ session);
|
||||
bool IsPlayerInSession( Platform::String^ xboxUserId, MXSM::MultiplayerSession^ session, int *smallId );
|
||||
|
||||
WXM::MultiplayerSessionReference^ ConvertToWindowsXboxMultiplayerSessionReference( MXSM::MultiplayerSessionReference^ sessionRef);
|
||||
MXSM::MultiplayerSessionReference^ ConvertToMicrosoftXboxServicesMultiplayerSessionReference( WXM::MultiplayerSessionReference^ sessionRef );
|
||||
|
||||
void BytesReceived(int smallId, BYTE *bytes, int byteCount);
|
||||
void BytesReceivedInternal(DQRConnectionInfo *connectionInfo, unsigned int sessionAddress, BYTE *bytes, int byteCount);
|
||||
void SendBytes(int smallId, BYTE *bytes, int byteCount);
|
||||
int GetQueueSizeBytes();
|
||||
int GetQueueSizeMessages();
|
||||
void SendBytesRaw(int smallId, BYTE *bytes, int byteCount, bool reliableAndSequential);
|
||||
void SendBytesChat(unsigned int address, BYTE *bytes, int byteCount, bool reliable, bool sequential, bool broadcast);
|
||||
|
||||
bool AddRoomSyncPlayer(DQRNetworkPlayer *pPlayer, unsigned int sessionAddress, int channel);
|
||||
void RemoveRoomSyncPlayersWithSessionAddress(unsigned int sessionAddress);
|
||||
void RemoveRoomSyncPlayer(DQRNetworkPlayer *pPlayer);
|
||||
void UpdateRoomSyncPlayers(RoomSyncData *pNewSyncData);
|
||||
void SendRoomSyncInfo();
|
||||
void SendAddPlayerFailed(Platform::String^ xuid);
|
||||
void SendSmallId(bool reliableAndSequential, int playerMask);
|
||||
void SendUnassignSmallId(int playerIndex);
|
||||
int GetSessionIndexForSmallId(unsigned char smallId);
|
||||
int GetSessionIndexAndSmallIdForHost(unsigned char *smallId);
|
||||
|
||||
static void LogComment( Platform::String^ strText );
|
||||
static void LogCommentFormat( LPCWSTR strMsg, ... );
|
||||
static void LogCommentWithError( Platform::String^ strTest, HRESULT hr );
|
||||
|
||||
static Platform::String^ GetErrorString( HRESULT hr );
|
||||
static Platform::String^ FormatString( LPCWSTR strMsg, ... );
|
||||
static Platform::String^ ConvertHResultToErrorName( HRESULT hr );
|
||||
|
||||
Platform::String^ GetNextSmallIdAsJsonString();
|
||||
static int _HostGameThreadProc( void* lpParameter );
|
||||
int HostGameThreadProc();
|
||||
static int _LeaveRoomThreadProc( void* lpParameter );
|
||||
int LeaveRoomThreadProc();
|
||||
static int _TidyUpJoinThreadProc( void* lpParameter );
|
||||
int TidyUpJoinThreadProc();
|
||||
static int _UpdateCustomSessionDataThreadProc( void* lpParameter );
|
||||
int UpdateCustomSessionDataThreadProc();
|
||||
static int _CheckInviteThreadProc(void *lpParameter);
|
||||
int CheckInviteThreadProc();
|
||||
static int _RTSDoWorkThread(void* lpParameter);
|
||||
int RTSDoWorkThread();
|
||||
|
||||
CRITICAL_SECTION m_csVecChatPlayers;
|
||||
vector<int> m_vecChatPlayersJoined;
|
||||
public:
|
||||
void HandleNewPartyFoundForPlayer();
|
||||
void HandlePlayerRemovedFromParty(int playerMask);
|
||||
|
||||
void ChatPlayerJoined(int idx);
|
||||
bool IsReadyToPlayOrIdle();
|
||||
void StartGame();
|
||||
void LeaveRoom();
|
||||
void TidyUpFailedJoin();
|
||||
|
||||
static void SetInviteReceivedFlag();
|
||||
static void SetPartyProcessJoinParty();
|
||||
static void SetPartyProcessJoinSession(int bootUserIndex, Platform::String^ bootSessionName, Platform::String^ bootServiceConfig, Platform::String^ bootSessionTemplate);
|
||||
|
||||
void SendInviteGUI(int quadrant);
|
||||
bool IsAddingPlayer();
|
||||
|
||||
bool FriendPartyManagerIsBusy();
|
||||
int FriendPartyManagerGetCount();
|
||||
bool FriendPartyManagerSearch();
|
||||
void FriendPartyManagerGetSessionInfo(int idx, SessionSearchResult *searchResult);
|
||||
WFC::IVectorView<WXM::UserPartyAssociation^>^ FilterPartiesByPermission(MXS::XboxLiveContext ^context, WFC::IVectorView<WXM::UserPartyAssociation^>^ partyResults);
|
||||
|
||||
bool JoinPartyFromSearchResult(SessionSearchResult *searchResult, int playerMask);
|
||||
void CancelJoinPartyFromSearchResult();
|
||||
void RequestDisplayName(DQRNetworkPlayer *player);
|
||||
void SetDisplayName(PlayerUID xuid, wstring displayName);
|
||||
|
||||
private:
|
||||
__int64 m_playersLeftPartyTime;
|
||||
int m_playersLeftParty;
|
||||
|
||||
bool GetBestPartyUserIndex();
|
||||
C4JThread *m_GetFriendPartyThread;
|
||||
static int _GetFriendsThreadProc( void* lpParameter );
|
||||
int GetFriendsThreadProc();
|
||||
bool IsSessionFriendsOfFriends(MXSM::MultiplayerSession^ session);
|
||||
bool GetGameSessionData(MXSM::MultiplayerSession^ session, void *gameSessionData);
|
||||
|
||||
public:
|
||||
static Platform::Collections::Vector<Platform::String^>^ GetFriends();
|
||||
|
||||
private:
|
||||
SessionSearchResult *m_sessionSearchResults;
|
||||
int m_sessionResultCount;
|
||||
bool m_cancelJoinFromSearchResult;
|
||||
|
||||
map<wstring, wstring> m_displayNames; // Player display names by gamertag
|
||||
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DNM_PARTY_PROCESS_NONE,
|
||||
DNM_PARTY_PROCESS_JOIN_PARTY,
|
||||
DNM_PARTY_PROCESS_JOIN_SPECIFIED
|
||||
} ePartyProcessType;
|
||||
static int m_bootUserIndex;
|
||||
static ePartyProcessType m_partyProcess;
|
||||
static wstring m_bootSessionName;
|
||||
static wstring m_bootServiceConfig;
|
||||
static wstring m_bootSessionTemplate;
|
||||
static bool m_inviteReceived;
|
||||
|
||||
static DQRNetworkManager *s_pDQRManager;
|
||||
|
||||
static void GetProfileCallback(LPVOID pParam, Microsoft::Xbox::Services::Social::XboxUserProfile^ profile);
|
||||
|
||||
// Forced signout
|
||||
bool m_handleForcedSignOut;
|
||||
|
||||
void CheckForcedSignOut();
|
||||
void HandleForcedSignOut();
|
||||
|
||||
unsigned int m_RTS_Stat_totalBytes;
|
||||
unsigned int m_RTS_Stat_totalSends;
|
||||
|
||||
void UpdateRTSStats();
|
||||
|
||||
// Incoming messages - to be called from the main thread, to get incoming messages from the RTS work thread
|
||||
void ProcessRTSMessagesIncoming();
|
||||
void Process_RTS_MESSAGE_DATA_RECEIVED(RTS_Message &message);
|
||||
void Process_RTS_MESSAGE_DATA_RECEIVED_CHAT(RTS_Message &message);
|
||||
void Process_RTS_MESSAGE_ADDED_SESSION_ADDRESS(RTS_Message &message);
|
||||
void Process_RTS_MESSAGE_REMOVED_SESSION_ADDRESS(RTS_Message &message);
|
||||
void Process_RTS_MESSAGE_STATUS_ACTIVE(RTS_Message &message);
|
||||
void Process_RTS_MESSAGE_STATUS_TERMINATED(RTS_Message &message);
|
||||
|
||||
// Outgoing messages - to be called from the RTS work thread, to process requests from the main thread
|
||||
|
||||
void ProcessRTSMessagesOutgoing();
|
||||
void Process_RTS_MESSAGE_START_CLIENT(RTS_Message &message);
|
||||
void Process_RTS_MESSAGE_START_HOST(RTS_Message &message);
|
||||
void Process_RTS_MESSAGE_TERMINATE(RTS_Message &message);
|
||||
void Process_RTS_MESSAGE_SEND_DATA(RTS_Message &message);
|
||||
|
||||
// These methods are called from the main thread, to put an outgoing message onto the queue to be processed by these previous methods
|
||||
void RTS_StartCient();
|
||||
void RTS_StartHost();
|
||||
void RTS_Terminate();
|
||||
void RTS_SendData(unsigned char *pucData, unsigned int dataSize, unsigned int sessionAddress, bool reliable, bool sequential, bool coalesce, bool includeMode, bool gameChannel );
|
||||
|
||||
};
|
||||
|
||||
// Class defining interface to be implemented for class that handles callbacks
|
||||
class IDQRNetworkManagerListener
|
||||
{
|
||||
public:
|
||||
virtual void HandleDataReceived(DQRNetworkPlayer *playerFrom, DQRNetworkPlayer *playerTo, unsigned char *data, unsigned int dataSize) = 0;
|
||||
virtual void HandlePlayerJoined(DQRNetworkPlayer *player) = 0;
|
||||
virtual void HandlePlayerLeaving(DQRNetworkPlayer *player) = 0;
|
||||
virtual void HandleStateChange(DQRNetworkManager::eDQRNetworkManagerState oldState, DQRNetworkManager::eDQRNetworkManagerState newState) = 0;
|
||||
// virtual void HandleResyncPlayerRequest(DQRNetworkPlayer **aPlayers) = 0;
|
||||
virtual void HandleAddLocalPlayerFailed(int idx, bool serverFull) = 0;
|
||||
virtual void HandleDisconnect(bool bLostRoomOnly) = 0;
|
||||
virtual void HandleInviteReceived(int playerIndex, DQRNetworkManager::SessionInfo *pInviteInfo) = 0;
|
||||
virtual bool IsSessionJoinable() = 0;
|
||||
};
|
||||
@@ -0,0 +1,591 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "DQRNetworkManager.h"
|
||||
#include "PartyController.h"
|
||||
#include <collection.h>
|
||||
#include <ppltasks.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include "..\Minecraft.World\StringHelpers.h"
|
||||
#include "base64.h"
|
||||
|
||||
#ifdef _DURANGO
|
||||
#include "..\Minecraft.World\DurangoStats.h"
|
||||
#endif
|
||||
|
||||
#include "ChatIntegrationLayer.h"
|
||||
|
||||
using namespace Concurrency;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
// Returns true if we are already processing a request to find game parties of friends
|
||||
bool DQRNetworkManager::FriendPartyManagerIsBusy()
|
||||
{
|
||||
if( m_GetFriendPartyThread )
|
||||
{
|
||||
if( m_GetFriendPartyThread->isRunning() )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns the total count of game parties that we found for our friends
|
||||
int DQRNetworkManager::FriendPartyManagerGetCount()
|
||||
{
|
||||
return m_sessionResultCount;
|
||||
}
|
||||
|
||||
// Initiate the (asynchronous) search for game parties of our friends
|
||||
bool DQRNetworkManager::FriendPartyManagerSearch()
|
||||
{
|
||||
if( m_GetFriendPartyThread )
|
||||
{
|
||||
if( m_GetFriendPartyThread->isRunning() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_sessionResultCount = 0;
|
||||
delete [] m_sessionSearchResults;
|
||||
m_sessionSearchResults = NULL;
|
||||
|
||||
m_GetFriendPartyThread = new C4JThread(&_GetFriendsThreadProc,this,"GetFriendsThreadProc");
|
||||
m_GetFriendPartyThread->Run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get a particular search result for a game party that we have discovered. Index should be from 0 to the value returned by FriendPartyManagerGetCount.
|
||||
void DQRNetworkManager::FriendPartyManagerGetSessionInfo(int idx, SessionSearchResult *searchResult)
|
||||
{
|
||||
assert( idx < m_sessionResultCount );
|
||||
assert( ( m_GetFriendPartyThread == NULL ) || ( !m_GetFriendPartyThread->isRunning()) );
|
||||
|
||||
// Need to make sure that copied data has independently allocated m_extData, so both copies can be freed
|
||||
*searchResult = m_sessionSearchResults[idx];
|
||||
searchResult->m_extData = malloc(sizeof(GameSessionData));
|
||||
memcpy(searchResult->m_extData, m_sessionSearchResults[idx].m_extData, sizeof(GameSessionData));
|
||||
}
|
||||
|
||||
int DQRNetworkManager::_GetFriendsThreadProc(void* lpParameter)
|
||||
{
|
||||
DQRNetworkManager *pDQR = (DQRNetworkManager *)lpParameter;
|
||||
return pDQR->GetFriendsThreadProc();
|
||||
}
|
||||
|
||||
// This is the main thread that is kicked off to find game sessions associated with our friends. We have to do this
|
||||
// by finding parties associated with our friends, and from the parties get the assocated game session.
|
||||
int DQRNetworkManager::GetFriendsThreadProc()
|
||||
{
|
||||
LogComment(L"Starting GetFriendsThreadProc");
|
||||
WXS::User^ primaryUser = ProfileManager.GetUser(0);
|
||||
|
||||
if( primaryUser == nullptr )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
MXS::XboxLiveContext^ primaryUserXboxLiveContext = ref new MXS::XboxLiveContext(primaryUser);
|
||||
if( primaryUserXboxLiveContext == nullptr )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
MXSS::XboxSocialRelationshipResult^ socialRelationshipResult = nullptr;
|
||||
|
||||
// First get our friends list (people we follow who may or may not follow us back), note we're requesting all friends
|
||||
auto getSocialRelationshipsAsync = primaryUserXboxLiveContext->SocialService->GetSocialRelationshipsAsync(MXSS::SocialRelationship::All, 0, 1100);
|
||||
create_task(getSocialRelationshipsAsync).then([this,&socialRelationshipResult](task<MXSS::XboxSocialRelationshipResult^> t)
|
||||
{
|
||||
try
|
||||
{
|
||||
socialRelationshipResult = t.get();
|
||||
}
|
||||
catch (Platform::COMException^ ex)
|
||||
{
|
||||
LogCommentWithError( L"GetSocialRelationshipsAsync failed", ex->HResult );
|
||||
}
|
||||
})
|
||||
.wait();
|
||||
if( socialRelationshipResult == nullptr )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
IVector<Platform::String^>^ friendXUIDs = ref new Platform::Collections::Vector<Platform::String^>;
|
||||
|
||||
// Now construct a vector of these users, that follow us back - these are our "friends"
|
||||
for( int i = 0; i < socialRelationshipResult->TotalCount; i++ )
|
||||
{
|
||||
MXSS::XboxSocialRelationship^ relationship = socialRelationshipResult->Items->GetAt(i);
|
||||
if(relationship->IsFollowingCaller)
|
||||
{
|
||||
friendXUIDs->Append(relationship->XboxUserId);
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have any such friends, we're done
|
||||
if( friendXUIDs->Size == 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Now get party associations for these friends
|
||||
auto getPartyAssociationsAsync = WXM::Party::GetUserPartyAssociationsAsync(primaryUser, friendXUIDs->GetView() );
|
||||
|
||||
IVectorView<WXM::UserPartyAssociation^>^ partyResults = nullptr;
|
||||
|
||||
create_task(getPartyAssociationsAsync).then([this,&partyResults](task<IVectorView<WXM::UserPartyAssociation^>^> t)
|
||||
{
|
||||
try
|
||||
{
|
||||
partyResults = t.get();
|
||||
}
|
||||
catch (Platform::COMException^ ex)
|
||||
{
|
||||
LogCommentWithError( L"getPartyAssociationsAsync failed", ex->HResult );
|
||||
}
|
||||
})
|
||||
.wait();
|
||||
|
||||
if( partyResults == nullptr )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( partyResults->Size == 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Filter these parties by whether we have permission to see them online
|
||||
partyResults = FilterPartiesByPermission(primaryUserXboxLiveContext, partyResults);
|
||||
|
||||
|
||||
// At this point, we have Party Ids for our friends. Now we need to get Party Views for each of these Ids.
|
||||
|
||||
LogComment("Parties found");
|
||||
|
||||
// Get party views for each of the user party associations that we have. These seem to be able to (individually) raise errors, so
|
||||
// accumulate results into 2 matched vectors declared below so that we can ignore any broken UserPartyAssociations from now
|
||||
vector<WXM::PartyView^> partyViewVector;
|
||||
vector<WXM::UserPartyAssociation^> partyResultsVector;
|
||||
|
||||
vector<task<void>> taskVector;
|
||||
for each(WXM::UserPartyAssociation^ remoteParty in partyResults)
|
||||
{
|
||||
auto asyncOp = WXM::Party::GetPartyViewByPartyIdAsync( primaryUser, remoteParty->PartyId );
|
||||
task<WXM::PartyView^> asyncTask = create_task(asyncOp);
|
||||
|
||||
taskVector.push_back(asyncTask.then([this, &partyViewVector, &partyResultsVector, remoteParty] (task<WXM::PartyView^> t)
|
||||
{
|
||||
try
|
||||
{
|
||||
WXM::PartyView^ partyView = t.get();
|
||||
|
||||
if( partyView != nullptr )
|
||||
{
|
||||
app.DebugPrintf("Got party view\n");
|
||||
EnterCriticalSection(&m_csPartyViewVector);
|
||||
partyViewVector.push_back(partyView);
|
||||
partyResultsVector.push_back(remoteParty);
|
||||
LeaveCriticalSection(&m_csPartyViewVector);
|
||||
}
|
||||
}
|
||||
catch ( Platform::COMException^ ex )
|
||||
{
|
||||
app.DebugPrintf("Getting party view error 0x%x\n",ex->HResult);
|
||||
}
|
||||
}));
|
||||
}
|
||||
for( auto it = taskVector.begin(); it != taskVector.end(); it++ )
|
||||
{
|
||||
it->wait();
|
||||
}
|
||||
|
||||
if( partyViewVector.size() == 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Filter the party view, and party results vector (partyResultsVector) this is matched to, to remove any that don't have game sessions - or game sessions that aren't this game
|
||||
vector<WXM::PartyView^> partyViewVectorFiltered;
|
||||
vector<WXM::UserPartyAssociation^> partyResultsFiltered;
|
||||
|
||||
for( int i = 0; i < partyViewVector.size(); i++ )
|
||||
{
|
||||
WXM::PartyView^ partyView = partyViewVector[i];
|
||||
|
||||
if( partyView->Joinability == WXM::SessionJoinability::JoinableByFriends )
|
||||
{
|
||||
if( partyView->GameSession )
|
||||
{
|
||||
if( partyView->GameSession->ServiceConfigurationId == SERVICE_CONFIG_ID )
|
||||
{
|
||||
partyViewVectorFiltered.push_back( partyView );
|
||||
partyResultsFiltered.push_back( partyResultsVector[i] );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We now have matched vectors:
|
||||
//
|
||||
// partyResultsFiltered
|
||||
// partyViewVectorFiltered
|
||||
//
|
||||
// and, from the party views, we can now attempt to get game sessions
|
||||
|
||||
vector<MXSM::MultiplayerSession^> sessionVector;
|
||||
vector<WXM::PartyView^> partyViewVectorValid;
|
||||
vector<WXM::UserPartyAssociation^> partyResultsValid;
|
||||
|
||||
for( int i = 0; i < partyViewVectorFiltered.size(); i++ )
|
||||
{
|
||||
WXM::PartyView^ partyView = partyViewVectorFiltered[i];
|
||||
Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionRef = ConvertToMicrosoftXboxServicesMultiplayerSessionReference(partyView->GameSession);
|
||||
|
||||
LogComment(L"Party view vector " + sessionRef->SessionName + L" " + partyResultsFiltered[i]->QueriedXboxUserIds->GetAt(0));
|
||||
|
||||
MXSM::MultiplayerSession^ session = nullptr;
|
||||
auto asyncOp = primaryUserXboxLiveContext->MultiplayerService->GetCurrentSessionAsync( sessionRef );
|
||||
create_task(asyncOp).then([&session] (task<MXSM::MultiplayerSession^> t)
|
||||
{
|
||||
try
|
||||
{
|
||||
session = t.get();
|
||||
}
|
||||
catch (Platform::COMException^ ex)
|
||||
{
|
||||
}
|
||||
})
|
||||
.wait();
|
||||
if( session )
|
||||
{
|
||||
sessionVector.push_back(session);
|
||||
partyViewVectorValid.push_back(partyView);
|
||||
partyResultsValid.push_back(partyResultsFiltered[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if( sessionVector.size() == 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We now have matched vectors:
|
||||
//
|
||||
// partyResultsValid
|
||||
// partyViewVectorValid
|
||||
// sessionVector
|
||||
|
||||
// The next stage is to resolve the display names for the XUIDs of all the players in each of the sessions. It is possible that
|
||||
// a session won't have any XUIDs to resolve, which would make GetUserProfilesAsync unhappy, so we'll only be creating a task
|
||||
// when there are members. Creating new matching arrays for party results and sessions, to match the results (we don't care about the party view anymore)
|
||||
|
||||
vector<task<IVectorView<MXSS::XboxUserProfile^>^>> nameResolveTaskVector;
|
||||
vector<IVectorView<MXSS::XboxUserProfile^>^> nameResolveVector;
|
||||
vector<MXSM::MultiplayerSession^> newSessionVector;
|
||||
vector<WXM::UserPartyAssociation^> newPartyVector;
|
||||
|
||||
for( int j = 0; j < sessionVector.size(); j++ )
|
||||
{
|
||||
MXSM::MultiplayerSession^ session = sessionVector[j];
|
||||
IVector<Platform::String^>^ memberXUIDs = ref new Platform::Collections::Vector<Platform::String^>;
|
||||
|
||||
Windows::Data::Json::JsonArray^ roomSyncArray = nullptr;
|
||||
try
|
||||
{
|
||||
Windows::Data::Json::JsonObject^ customJson = Windows::Data::Json::JsonObject::Parse(session->SessionProperties->SessionCustomPropertiesJson);
|
||||
Windows::Data::Json::JsonValue^ customValue = customJson->GetNamedValue(L"RoomSyncData");
|
||||
roomSyncArray = customValue->GetArray();
|
||||
LogComment("Attempting to parse RoomSyncData");
|
||||
for( int i = 0; i < roomSyncArray->Size; i++ )
|
||||
{
|
||||
LogComment(roomSyncArray->GetAt(i)->GetString());
|
||||
}
|
||||
}
|
||||
catch (Platform::COMException^ ex)
|
||||
{
|
||||
LogCommentWithError( L"Custom RoomSyncData Parse/GetNamedValue failed", ex->HResult );
|
||||
continue;
|
||||
}
|
||||
|
||||
if( roomSyncArray && ( roomSyncArray->Size > 0 ) )
|
||||
{
|
||||
// For each session, we want to order these XUIDs so the display name of the first one is what we will name the session by. Prioritise doing this by:
|
||||
//
|
||||
// (1) If the host player (indicated by having a small id of 0) is our friend, use that
|
||||
// (2) Otherwise use anyone who is our friend
|
||||
|
||||
// Default to true
|
||||
bool friendsOfFriends = true;
|
||||
|
||||
int hostIndexFound = -1;
|
||||
int friendIndexFound = -1;
|
||||
|
||||
friendsOfFriends = IsSessionFriendsOfFriends(session);
|
||||
|
||||
for( int i = 0; i < roomSyncArray->Size; i++ )
|
||||
{
|
||||
Platform::String^ roomSyncXuid = roomSyncArray->GetAt(i)->GetString();
|
||||
|
||||
// Determine if this player is a friend
|
||||
bool isFriend = false;
|
||||
for each( Platform::String^ friendXUID in friendXUIDs )
|
||||
{
|
||||
if( friendXUID == roomSyncXuid )
|
||||
{
|
||||
isFriend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool isHost = i == 0;
|
||||
|
||||
// Store that what we found at this index if it is a friend, or a friend who is a host
|
||||
if( isFriend && ( friendsOfFriends || isHost ) )
|
||||
{
|
||||
friendIndexFound = i;
|
||||
if( isHost ) // Host is always in slot 0
|
||||
{
|
||||
hostIndexFound = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer to use index of host who is our friend
|
||||
int bestIndex = friendIndexFound;
|
||||
if( hostIndexFound != -1 )
|
||||
{
|
||||
bestIndex = hostIndexFound;
|
||||
}
|
||||
|
||||
// Only consider if we have at least found one friend in the list of players
|
||||
if( bestIndex != -1 )
|
||||
{
|
||||
// Compile list of XUIDs to resolve with our specially chosen player as entry 0, then the rest
|
||||
memberXUIDs->Append(roomSyncArray->GetAt(bestIndex)->GetString());
|
||||
for( int i = 0; i < roomSyncArray->Size; i++ )
|
||||
{
|
||||
if( i != bestIndex )
|
||||
{
|
||||
memberXUIDs->Append(roomSyncArray->GetAt(i)->GetString());
|
||||
}
|
||||
}
|
||||
nameResolveTaskVector.push_back( create_task( primaryUserXboxLiveContext->ProfileService->GetUserProfilesAsync( memberXUIDs->GetView() ) ) );
|
||||
newSessionVector.push_back(session);
|
||||
newPartyVector.push_back(partyResultsValid[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto joinTask = when_all(begin(nameResolveTaskVector), end(nameResolveTaskVector) ).then([this, &nameResolveVector](vector<IVectorView<MXSS::XboxUserProfile^>^> results)
|
||||
{
|
||||
nameResolveVector = results;
|
||||
})
|
||||
.wait();
|
||||
}
|
||||
catch(Platform::COMException^ ex)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// We now have matched vectors:
|
||||
//
|
||||
// newPartyVector - contains the party Ids that we'll need should we wish to join
|
||||
// nameResolveVector - contains vectors views of the names of the members of the session each of these parties is in
|
||||
// newSessionVector - contains the session information itself associated with each of the parties
|
||||
|
||||
// Construct the final result vector
|
||||
m_sessionResultCount = newSessionVector.size();
|
||||
m_sessionSearchResults = new SessionSearchResult[m_sessionResultCount];
|
||||
for( int i = 0; i < m_sessionResultCount; i++ )
|
||||
{
|
||||
m_sessionSearchResults[i].m_partyId = newPartyVector[i]->PartyId->Data();
|
||||
m_sessionSearchResults[i].m_sessionName = newSessionVector[i]->SessionReference->SessionName->Data();
|
||||
for( int j = 0; j < nameResolveVector[i]->Size; j++ )
|
||||
{
|
||||
m_sessionSearchResults[i].m_playerNames[j] = nameResolveVector[i]->GetAt(j)->GameDisplayName->Data();
|
||||
m_sessionSearchResults[i].m_playerXuids[j] = PlayerUID(nameResolveVector[i]->GetAt(j)->XboxUserId->Data());
|
||||
}
|
||||
m_sessionSearchResults[i].m_playerCount = nameResolveVector[i]->Size;
|
||||
m_sessionSearchResults[i].m_usedSlotCount = newSessionVector[i]->Members->Size;
|
||||
if( m_sessionSearchResults[i].m_usedSlotCount > MAX_ONLINE_PLAYER_COUNT )
|
||||
{
|
||||
// Don't think this could ever happen, but no harm in checking
|
||||
m_sessionSearchResults[i].m_usedSlotCount = MAX_ONLINE_PLAYER_COUNT;
|
||||
}
|
||||
for( int j = 0; j < m_sessionSearchResults[i].m_usedSlotCount; j++ )
|
||||
{
|
||||
m_sessionSearchResults[i].m_sessionXuids[j] = wstring( newSessionVector[i]->Members->GetAt(j)->XboxUserId->Data() );
|
||||
}
|
||||
|
||||
m_sessionSearchResults[i].m_extData = malloc( sizeof(GameSessionData) );
|
||||
memset( m_sessionSearchResults[i].m_extData, 0, sizeof(GameSessionData) );
|
||||
|
||||
GetGameSessionData(newSessionVector[i], m_sessionSearchResults[i].m_extData);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Filters list of parties based on online presence permission (whether the friend is set to invisible or not)
|
||||
IVectorView<WXM::UserPartyAssociation^>^ DQRNetworkManager::FilterPartiesByPermission(MXS::XboxLiveContext ^context, IVectorView<WXM::UserPartyAssociation^>^ partyResults)
|
||||
{
|
||||
Platform::Collections::Vector<WXM::UserPartyAssociation^>^ filteredPartyResults = ref new Platform::Collections::Vector<WXM::UserPartyAssociation^>();
|
||||
|
||||
// List of permissions we want
|
||||
auto permissionIds = ref new Platform::Collections::Vector<Platform::String^>(1, ref new Platform::String(L"ViewTargetPresence"));
|
||||
|
||||
// List of target users
|
||||
auto targetXboxUserIds = ref new Platform::Collections::Vector<Platform::String^>();
|
||||
for (int i = 0; i < partyResults->Size; i++)
|
||||
{
|
||||
assert(partyResults->GetAt(i)->QueriedXboxUserIds->Size > 0);
|
||||
targetXboxUserIds->Append( partyResults->GetAt(i)->QueriedXboxUserIds->GetAt(0) );
|
||||
}
|
||||
|
||||
// Check
|
||||
auto checkPermissionsAsync = context->PrivacyService->CheckMultiplePermissionsWithMultipleTargetUsersAsync(permissionIds->GetView(), targetXboxUserIds->GetView());
|
||||
create_task(checkPermissionsAsync).then([&partyResults, &filteredPartyResults](task<IVectorView<MXS::Privacy::MultiplePermissionsCheckResult^>^> t)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto results = t.get();
|
||||
|
||||
// For each party, check to see if we have permission for the user
|
||||
for (int i = 0; i < partyResults->Size; i++)
|
||||
{
|
||||
// For each permissions result
|
||||
for (int j = 0; j < results->Size; j++)
|
||||
{
|
||||
auto result = results->GetAt(j);
|
||||
|
||||
// If allowed to see this user AND it's the same user, add the party to the just
|
||||
if ((result->Items->GetAt(0)->IsAllowed) && (partyResults->GetAt(i)->QueriedXboxUserIds->GetAt(0) == result->XboxUserId))
|
||||
{
|
||||
filteredPartyResults->Append(partyResults->GetAt(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Platform::COMException^ ex)
|
||||
{
|
||||
LogCommentWithError( L"CheckMultiplePermissionsWithMultipleTargetUsersAsync failed", ex->HResult );
|
||||
}
|
||||
})
|
||||
.wait();
|
||||
|
||||
app.DebugPrintf("DQRNetworkManager::FilterPartiesByPermission: Removed %i parties because of online presence permissions\n", partyResults->Size - filteredPartyResults->Size);
|
||||
|
||||
return filteredPartyResults->GetView();
|
||||
}
|
||||
|
||||
// Get all friends (list of XUIDs) syncronously from the service (slow, may take 300ms+), returns empty list if something goes wrong
|
||||
Platform::Collections::Vector<Platform::String^>^ DQRNetworkManager::GetFriends()
|
||||
{
|
||||
auto friends = ref new Platform::Collections::Vector<Platform::String^>;
|
||||
|
||||
auto primaryUser = ProfileManager.GetUser(0);
|
||||
if (primaryUser == nullptr)
|
||||
{
|
||||
// Return empty
|
||||
return friends;
|
||||
}
|
||||
|
||||
auto xboxLiveContext = ref new MXS::XboxLiveContext(primaryUser);
|
||||
|
||||
// Request ALL friends because there's no other way to check friendships without using the REST API
|
||||
auto getSocialRelationshipsAsync = xboxLiveContext->SocialService->GetSocialRelationshipsAsync(MXSS::SocialRelationship::All, 0, 1100);
|
||||
MXSS::XboxSocialRelationshipResult^ socialRelationshipResult = nullptr;
|
||||
|
||||
// First get our friends list (people we follow who may or may not follow us back)
|
||||
Concurrency::create_task(getSocialRelationshipsAsync).then([&socialRelationshipResult](Concurrency::task<MXSS::XboxSocialRelationshipResult^> t)
|
||||
{
|
||||
try
|
||||
{
|
||||
socialRelationshipResult = t.get();
|
||||
}
|
||||
catch (Platform::COMException^ ex)
|
||||
{
|
||||
app.DebugPrintf("DQRNetworkManager::GetFriends: GetSocialRelationshipsAsync failed ()\n", ex->HResult);
|
||||
}
|
||||
})
|
||||
.wait();
|
||||
|
||||
if (socialRelationshipResult == nullptr)
|
||||
{
|
||||
// Return empty
|
||||
return friends;
|
||||
}
|
||||
|
||||
app.DebugPrintf("DQRNetworkManager::GetFriends: Retrieved %i relationships\n", socialRelationshipResult->TotalCount);
|
||||
|
||||
// Now construct a vector of these users, that follow us back - these are our "friends"
|
||||
for( int i = 0; i < socialRelationshipResult->TotalCount; i++ )
|
||||
{
|
||||
MXSS::XboxSocialRelationship^ relationship = socialRelationshipResult->Items->GetAt(i);
|
||||
if(relationship->IsFollowingCaller)
|
||||
{
|
||||
app.DebugPrintf("DQRNetworkManager::GetFriends: Found friend \"%ls\"\n", relationship->XboxUserId->Data());
|
||||
friends->Append(relationship->XboxUserId);
|
||||
}
|
||||
}
|
||||
|
||||
app.DebugPrintf("DQRNetworkManager::GetFriends: Found %i 2-way friendships\n", friends->Size);
|
||||
|
||||
return friends;
|
||||
}
|
||||
|
||||
// If data for game settings exists returns FriendsOfFriends value, otherwise returns true
|
||||
bool DQRNetworkManager::IsSessionFriendsOfFriends(MXSM::MultiplayerSession^ session)
|
||||
{
|
||||
// Default to true, don't want to incorrectly prevent joining
|
||||
bool friendsOfFriends = true;
|
||||
|
||||
// We retrieve the game session data later too, shouldn't really duplicate this
|
||||
void *gameSessionData = malloc( sizeof(GameSessionData));
|
||||
memset(gameSessionData, 0, sizeof(GameSessionData));
|
||||
|
||||
bool result = GetGameSessionData(session, gameSessionData);
|
||||
|
||||
if (result)
|
||||
{
|
||||
friendsOfFriends = app.GetGameHostOption(((GameSessionData *)gameSessionData)->m_uiGameHostSettings, eGameHostOption_FriendsOfFriends);
|
||||
}
|
||||
|
||||
free(gameSessionData);
|
||||
|
||||
return friendsOfFriends;
|
||||
}
|
||||
|
||||
// Parses custom json data from session and populates game session data param, return true if parse succeeded
|
||||
bool DQRNetworkManager::GetGameSessionData(MXSM::MultiplayerSession^ session, void *gameSessionData)
|
||||
{
|
||||
Platform::String ^gameSessionDataJson = session->SessionProperties->SessionCustomPropertiesJson;
|
||||
if( gameSessionDataJson )
|
||||
{
|
||||
try
|
||||
{
|
||||
Windows::Data::Json::JsonObject^ customParam = Windows::Data::Json::JsonObject::Parse(gameSessionDataJson);
|
||||
Windows::Data::Json::JsonValue^ customValue = customParam->GetNamedValue(L"GameSessionData");
|
||||
Platform::String ^customValueString = customValue->GetString();
|
||||
if( customValueString )
|
||||
{
|
||||
base64_decode( customValueString, (unsigned char *)gameSessionData, sizeof(GameSessionData) );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Platform::COMException^ ex)
|
||||
{
|
||||
LogCommentWithError( L"Custom GameSessionData parameter Parse/GetNamedValue failed", ex->HResult );
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
303
Minecraft.Client/Durango/Network/DQRNetworkManager_Log.cpp
Normal file
303
Minecraft.Client/Durango/Network/DQRNetworkManager_Log.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "DQRNetworkManager.h"
|
||||
#include "PartyController.h"
|
||||
#include <collection.h>
|
||||
#include <ppltasks.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include "..\Minecraft.World\StringHelpers.h"
|
||||
#include "base64.h"
|
||||
|
||||
#ifdef _DURANGO
|
||||
#include "..\Minecraft.World\DurangoStats.h"
|
||||
#endif
|
||||
|
||||
#include "ChatIntegrationLayer.h"
|
||||
|
||||
using namespace Concurrency;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
void DQRNetworkManager::LogComment( Platform::String^ strText )
|
||||
{
|
||||
#ifndef _CONTENT_PACKAGE
|
||||
static int64_t firstTime = 0;
|
||||
wchar_t buf[64];
|
||||
|
||||
int64_t currentTime = System::currentTimeMillis();
|
||||
if( firstTime != 0 )
|
||||
{
|
||||
_i64tow_s(currentTime - firstTime, buf, 64, 10);
|
||||
OutputDebugString(buf);
|
||||
OutputDebugString(L" ms: ");
|
||||
}
|
||||
else
|
||||
{
|
||||
firstTime = currentTime;
|
||||
}
|
||||
OutputDebugString(strText->Data());
|
||||
OutputDebugString(L"\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void DQRNetworkManager::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 DQRNetworkManager::LogCommentWithError( Platform::String^ strTest, HRESULT hr )
|
||||
{
|
||||
Platform::String^ final = strTest + GetErrorString(hr);
|
||||
LogComment(final);
|
||||
}
|
||||
|
||||
Platform::String^ DQRNetworkManager::GetErrorString( HRESULT hr )
|
||||
{
|
||||
Platform::String^ str = FormatString(L" %s [0x%0.8x]", ConvertHResultToErrorName(hr)->Data(), hr );
|
||||
return str;
|
||||
}
|
||||
|
||||
Platform::String^ DQRNetworkManager::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^ DQRNetworkManager::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";
|
||||
|
||||
// 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_INVALID_NSAL_DATA";
|
||||
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 0x87DD0011: return L"AM_E_USER_HASH_MISSING";
|
||||
case 0x87DD0012: return L"AM_E_ACTOR_NOT_SPECIFIED";
|
||||
case 0x87DD0013: return L"AM_E_USER_NOT_FOUND";
|
||||
case 0x87DD0014: return L"AM_E_INVALID_SUBTOKEN";
|
||||
case 0x87DD0015: return L"AM_E_INVALID_ENVIRONMENT";
|
||||
case 0x87DD0016: return L"AM_E_XASD_TIMEOUT";
|
||||
case 0x87DD0017: return L"AM_E_XASU_TIMEOUT";
|
||||
case 0x87DD0018: return L"AM_E_XAST_TIMEOUT";
|
||||
case 0x87DD0019: return L"AM_E_XSTS_TIMEOUT";
|
||||
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";
|
||||
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";
|
||||
|
||||
// winsock errors
|
||||
case MAKE_HRESULT(1,7,WSAEWOULDBLOCK) : return L"WSAEWOULDBLOCK";
|
||||
case MAKE_HRESULT(1,7,WSAEINPROGRESS) : return L"WSAEINPROGRESS";
|
||||
case MAKE_HRESULT(1,7,WSAEALREADY) : return L"WSAEALREADY";
|
||||
case MAKE_HRESULT(1,7,WSAENOTSOCK) : return L"WSAENOTSOCK";
|
||||
case MAKE_HRESULT(1,7,WSAEDESTADDRREQ) : return L"WSAEDESTADDRREQ";
|
||||
case MAKE_HRESULT(1,7,WSAEMSGSIZE) : return L"WSAEMSGSIZE";
|
||||
case MAKE_HRESULT(1,7,WSAEPROTOTYPE) : return L"WSAEPROTOTYPE";
|
||||
case MAKE_HRESULT(1,7,WSAENOPROTOOPT) : return L"WSAENOPROTOOPT";
|
||||
case MAKE_HRESULT(1,7,WSAEPROTONOSUPPORT) : return L"WSAEPROTONOSUPPORT";
|
||||
case MAKE_HRESULT(1,7,WSAESOCKTNOSUPPORT) : return L"WSAESOCKTNOSUPPORT";
|
||||
case MAKE_HRESULT(1,7,WSAEOPNOTSUPP) : return L"WSAEOPNOTSUPP";
|
||||
case MAKE_HRESULT(1,7,WSAEPFNOSUPPORT) : return L"WSAEPFNOSUPPORT";
|
||||
case MAKE_HRESULT(1,7,WSAEAFNOSUPPORT) : return L"WSAEAFNOSUPPORT";
|
||||
case MAKE_HRESULT(1,7,WSAEADDRINUSE) : return L"WSAEADDRINUSE";
|
||||
case MAKE_HRESULT(1,7,WSAEADDRNOTAVAIL) : return L"WSAEADDRNOTAVAIL";
|
||||
case MAKE_HRESULT(1,7,WSAENETDOWN) : return L"WSAENETDOWN";
|
||||
case MAKE_HRESULT(1,7,WSAENETUNREACH) : return L"WSAENETUNREACH";
|
||||
case MAKE_HRESULT(1,7,WSAENETRESET) : return L"WSAENETRESET";
|
||||
case MAKE_HRESULT(1,7,WSAECONNABORTED) : return L"WSAECONNABORTED";
|
||||
case MAKE_HRESULT(1,7,WSAECONNRESET) : return L"WSAECONNRESET";
|
||||
case MAKE_HRESULT(1,7,WSAENOBUFS) : return L"WSAENOBUFS";
|
||||
case MAKE_HRESULT(1,7,WSAEISCONN) : return L"WSAEISCONN";
|
||||
case MAKE_HRESULT(1,7,WSAENOTCONN) : return L"WSAENOTCONN";
|
||||
case MAKE_HRESULT(1,7,WSAESHUTDOWN) : return L"WSAESHUTDOWN";
|
||||
case MAKE_HRESULT(1,7,WSAETOOMANYREFS) : return L"WSAETOOMANYREFS";
|
||||
case MAKE_HRESULT(1,7,WSAETIMEDOUT) : return L"WSAETIMEDOUT";
|
||||
case MAKE_HRESULT(1,7,WSAECONNREFUSED) : return L"WSAECONNREFUSED";
|
||||
case MAKE_HRESULT(1,7,WSAELOOP) : return L"WSAELOOP";
|
||||
case MAKE_HRESULT(1,7,WSAENAMETOOLONG) : return L"WSAENAMETOOLONG";
|
||||
case MAKE_HRESULT(1,7,WSAEHOSTDOWN) : return L"WSAEHOSTDOWN";
|
||||
case MAKE_HRESULT(1,7,WSAEHOSTUNREACH) : return L"WSAEHOSTUNREACH";
|
||||
case MAKE_HRESULT(1,7,WSAENOTEMPTY) : return L"WSAENOTEMPTY";
|
||||
case MAKE_HRESULT(1,7,WSAEPROCLIM) : return L"WSAEPROCLIM";
|
||||
case MAKE_HRESULT(1,7,WSAEUSERS) : return L"WSAEUSERS";
|
||||
case MAKE_HRESULT(1,7,WSAEDQUOT) : return L"WSAEDQUOT";
|
||||
case MAKE_HRESULT(1,7,WSAESTALE) : return L"WSAESTALE";
|
||||
case MAKE_HRESULT(1,7,WSAEREMOTE) : return L"WSAEREMOTE";
|
||||
case MAKE_HRESULT(1,7,WSASYSNOTREADY) : return L"WSASYSNOTREADY";
|
||||
case MAKE_HRESULT(1,7,WSAVERNOTSUPPORTED) : return L"WSAVERNOTSUPPORTED";
|
||||
case MAKE_HRESULT(1,7,WSANOTINITIALISED) : return L"WSANOTINITIALISED";
|
||||
case MAKE_HRESULT(1,7,WSAEDISCON) : return L"WSAEDISCON";
|
||||
case MAKE_HRESULT(1,7,WSAENOMORE) : return L"WSAENOMORE";
|
||||
case MAKE_HRESULT(1,7,WSAECANCELLED) : return L"WSAECANCELLED";
|
||||
case MAKE_HRESULT(1,7,WSAEINVALIDPROCTABLE) : return L"WSAEINVALIDPROCTABLE";
|
||||
case MAKE_HRESULT(1,7,WSAEINVALIDPROVIDER) : return L"WSAEINVALIDPROVIDER";
|
||||
case MAKE_HRESULT(1,7,WSAEPROVIDERFAILEDINIT) : return L"WSAEPROVIDERFAILEDINIT";
|
||||
case MAKE_HRESULT(1,7,WSASYSCALLFAILURE) : return L"WSASYSCALLFAILURE";
|
||||
case MAKE_HRESULT(1,7,WSASERVICE_NOT_FOUND) : return L"WSASERVICE_NOT_FOUND";
|
||||
case MAKE_HRESULT(1,7,WSATYPE_NOT_FOUND) : return L"WSATYPE_NOT_FOUND";
|
||||
case MAKE_HRESULT(1,7,WSA_E_NO_MORE) : return L"WSA_E_NO_MORE";
|
||||
case MAKE_HRESULT(1,7,WSA_E_CANCELLED) : return L"WSA_E_CANCELLED";
|
||||
case MAKE_HRESULT(1,7,WSAEREFUSED) : return L"WSAEREFUSED";
|
||||
case MAKE_HRESULT(1,7,WSAHOST_NOT_FOUND) : return L"WSAHOST_NOT_FOUND";
|
||||
case MAKE_HRESULT(1,7,WSATRY_AGAIN) : return L"WSATRY_AGAIN";
|
||||
case MAKE_HRESULT(1,7,WSANO_RECOVERY) : return L"WSANO_RECOVERY";
|
||||
case MAKE_HRESULT(1,7,WSANO_DATA) : return L"WSANO_DATA";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_RECEIVERS) : return L"WSA_QOS_RECEIVERS";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_SENDERS) : return L"WSA_QOS_SENDERS";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_NO_SENDERS) : return L"WSA_QOS_NO_SENDERS";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_NO_RECEIVERS) : return L"WSA_QOS_NO_RECEIVERS";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_REQUEST_CONFIRMED) : return L"WSA_QOS_REQUEST_CONFIRMED";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_ADMISSION_FAILURE) : return L"WSA_QOS_ADMISSION_FAILURE";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_POLICY_FAILURE) : return L"WSA_QOS_POLICY_FAILURE";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_BAD_STYLE) : return L"WSA_QOS_BAD_STYLE";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_BAD_OBJECT) : return L"WSA_QOS_BAD_OBJECT";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_TRAFFIC_CTRL_ERROR) : return L"WSA_QOS_TRAFFIC_CTRL_ERROR";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_GENERIC_ERROR) : return L"WSA_QOS_GENERIC_ERROR";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_ESERVICETYPE) : return L"WSA_QOS_ESERVICETYPE";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_EFLOWSPEC) : return L"WSA_QOS_EFLOWSPEC";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_EPROVSPECBUF) : return L"WSA_QOS_EPROVSPECBUF";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_EFILTERSTYLE) : return L"WSA_QOS_EFILTERSTYLE";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_EFILTERTYPE) : return L"WSA_QOS_EFILTERTYPE";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_EFILTERCOUNT) : return L"WSA_QOS_EFILTERCOUNT";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_EOBJLENGTH) : return L"WSA_QOS_EOBJLENGTH";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_EFLOWCOUNT) : return L"WSA_QOS_EFLOWCOUNT";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_EUNKOWNPSOBJ) : return L"WSA_QOS_EUNKOWNPSOBJ";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_EPOLICYOBJ) : return L"WSA_QOS_EPOLICYOBJ";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_EFLOWDESC) : return L"WSA_QOS_EFLOWDESC";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_EPSFLOWSPEC) : return L"WSA_QOS_EPSFLOWSPEC";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_EPSFILTERSPEC) : return L"WSA_QOS_EPSFILTERSPEC";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_ESDMODEOBJ) : return L"WSA_QOS_ESDMODEOBJ";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_ESHAPERATEOBJ) : return L"WSA_QOS_ESHAPERATEOBJ";
|
||||
case MAKE_HRESULT(1,7,WSA_QOS_RESERVED_PETYPE) : return L"WSA_QOS_RESERVED_PETYPE";
|
||||
|
||||
// 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"";
|
||||
}
|
||||
@@ -0,0 +1,409 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "DQRNetworkManager.h"
|
||||
#include "PartyController.h"
|
||||
#include <collection.h>
|
||||
#include <ppltasks.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include "..\Minecraft.World\StringHelpers.h"
|
||||
#include "base64.h"
|
||||
|
||||
#ifdef _DURANGO
|
||||
#include "..\Minecraft.World\DurangoStats.h"
|
||||
#endif
|
||||
|
||||
#include "ChatIntegrationLayer.h"
|
||||
|
||||
using namespace Concurrency;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
// This method is called when bytes have been received that are to be passed on to the game itself. The data is associated with a small id so we can specify which network player
|
||||
// that it was received for.
|
||||
void DQRNetworkManager::BytesReceived(int smallId, BYTE *bytes, int byteCount)
|
||||
{
|
||||
DQRNetworkPlayer *host = GetPlayerBySmallId(m_hostSmallId);
|
||||
DQRNetworkPlayer *client = GetPlayerBySmallId(smallId);
|
||||
|
||||
if( ( host == NULL ) || ( client == NULL ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if( m_isHosting )
|
||||
{
|
||||
m_listener->HandleDataReceived(client, host, bytes, byteCount );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_listener->HandleDataReceived(host, client, bytes, byteCount );
|
||||
}
|
||||
// app.DebugPrintf("%d bytes received: %s\n", byteCount, bytes);
|
||||
}
|
||||
|
||||
// This method is called when network data is received, that is to be processed by the DQRNetworkManager itself. This is for handling internal
|
||||
// updates such as assigning & unassigning of small Ids, transmission of the table of players currently in the session etc.
|
||||
// Processing of these things is handled as a state machine so that we can receive a message split over more than one call to this method should
|
||||
// the underlying communcation layer split data up somehow.
|
||||
void DQRNetworkManager::BytesReceivedInternal(DQRConnectionInfo *connectionInfo, unsigned int sessionAddress, BYTE *bytes, int byteCount)
|
||||
{
|
||||
BYTE *pNextByte = bytes;
|
||||
BYTE *pEndByte = pNextByte + byteCount;
|
||||
|
||||
do
|
||||
{
|
||||
BYTE byte = *pNextByte;
|
||||
switch( connectionInfo->m_internalDataState )
|
||||
{
|
||||
case DQRConnectionInfo::ConnectionState_InternalHeaderByte:
|
||||
switch( byte )
|
||||
{
|
||||
case DQR_INTERNAL_ASSIGN_SMALL_IDS:
|
||||
connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallIdMask;
|
||||
break;
|
||||
case DQR_INTERNAL_UNASSIGN_SMALL_ID:
|
||||
// Host only
|
||||
if( connectionInfo->m_channelActive[connectionInfo->m_currentChannel] )
|
||||
{
|
||||
int smallId = connectionInfo->m_smallId[connectionInfo->m_currentChannel];
|
||||
connectionInfo->m_channelActive[connectionInfo->m_currentChannel] = false;
|
||||
m_sessionAddressFromSmallId[smallId] = 0;
|
||||
DQRNetworkPlayer *pPlayer = GetPlayerBySmallId(smallId);
|
||||
if( pPlayer )
|
||||
{
|
||||
RemoveRoomSyncPlayer(pPlayer);
|
||||
SendRoomSyncInfo();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DQR_INTERNAL_PLAYER_TABLE:
|
||||
connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalRoomSyncData;
|
||||
connectionInfo->m_pucRoomSyncData = new unsigned char[4];
|
||||
connectionInfo->m_roomSyncDataBytesToRead = 0;
|
||||
connectionInfo->m_roomSyncDataBytesRead = 0;
|
||||
break;
|
||||
case DQR_INTERNAL_ADD_PLAYER_FAILED:
|
||||
connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAddPlayerFailedData;
|
||||
connectionInfo->m_pucAddFailedPlayerData = new unsigned char[4];
|
||||
connectionInfo->m_addFailedPlayerDataBytesToRead = 0;
|
||||
connectionInfo->m_addFailedPlayerDataBytesRead = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pNextByte++;
|
||||
break;
|
||||
case DQRConnectionInfo::ConnectionState_InternalAssignSmallIdMask:
|
||||
// Up to 4 smallIds are assigned at once, with the ones that are being assigned dictated by a mask byte which is passed in first.
|
||||
// The small Ids themselves follow, always 4 bytes, and any that are masked as being assigned are processed, the other bytes ignored.
|
||||
// In order work around a bug with the networking library, this particular packet (being the first this that is sent from a client)
|
||||
// is at first sent unreliably, with retries, until a message is received back to the client, or it times out. We therefore have to be able
|
||||
// to handle (and ignore) this being received more than once
|
||||
DQRNetworkManager::LogCommentFormat(L"Small Ids being received");
|
||||
connectionInfo->m_smallIdReadMask = byte;
|
||||
// Create a uniquely allocated byte to which names have been resolved, as another one of these packets could be received whilst that asyncronous process is going o n
|
||||
connectionInfo->m_pucsmallIdReadMaskResolved = new unsigned char;
|
||||
*connectionInfo->m_pucsmallIdReadMaskResolved = 0;
|
||||
connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallId0;
|
||||
connectionInfo->m_initialPacketReceived = true;
|
||||
|
||||
pNextByte++;
|
||||
break;
|
||||
case DQRConnectionInfo::ConnectionState_InternalAssignSmallId0:
|
||||
case DQRConnectionInfo::ConnectionState_InternalAssignSmallId1:
|
||||
case DQRConnectionInfo::ConnectionState_InternalAssignSmallId2:
|
||||
case DQRConnectionInfo::ConnectionState_InternalAssignSmallId3:
|
||||
{
|
||||
int channel = ((int)connectionInfo->m_internalDataState) - DQRConnectionInfo::ConnectionState_InternalAssignSmallId0;
|
||||
|
||||
if( ( connectionInfo->m_smallIdReadMask & ( 1 << channel ) ) && ( !connectionInfo->m_channelActive[channel] ) )
|
||||
{
|
||||
// HOST ONLY
|
||||
// Store the small Id that is associated with this send channel. In order work around a bug with the networking library, this particular packet
|
||||
// (being the first this that is sent from a client) is sent unreliably, with retries, until a message is received back to the client, or it times out.
|
||||
// We therefore have to be able to handle (and ignore) this being received more than once - hence the check of the bool above.
|
||||
// At this point, the connection is considered properly active from the point of view of the host.
|
||||
|
||||
int sessionIndex = GetSessionIndexForSmallId(byte);
|
||||
if( sessionIndex != -1 )
|
||||
{
|
||||
connectionInfo->m_channelActive[channel] = true;
|
||||
connectionInfo->m_smallId[channel] = byte;
|
||||
|
||||
m_sessionAddressFromSmallId[byte] = sessionAddress;
|
||||
m_channelFromSmallId[byte] = channel;
|
||||
|
||||
auto pAsyncOp = m_primaryUserXboxLiveContext->ProfileService->GetUserProfileAsync(m_multiplayerSession->Members->GetAt(sessionIndex)->XboxUserId);
|
||||
DQRNetworkManager::LogCommentFormat(L"Session index of %d found for player with small id %d - attempting to resolve display name\n",sessionIndex,byte);
|
||||
|
||||
DQRNetworkPlayer *pPlayer = new DQRNetworkPlayer(this, DQRNetworkPlayer::DNP_TYPE_REMOTE, true, 0, sessionAddress);
|
||||
pPlayer->SetSmallId(byte);
|
||||
pPlayer->SetUID(PlayerUID(m_multiplayerSession->Members->GetAt(sessionIndex)->XboxUserId->Data()));
|
||||
|
||||
HostGamertagResolveDetails *resolveDetails = new HostGamertagResolveDetails();
|
||||
resolveDetails->m_pPlayer = pPlayer;
|
||||
resolveDetails->m_sessionAddress = sessionAddress;
|
||||
resolveDetails->m_channel = channel;
|
||||
resolveDetails->m_sync = false;
|
||||
|
||||
int mask = 1 << channel;
|
||||
unsigned char *pucsmallIdReadMaskResolved = connectionInfo->m_pucsmallIdReadMaskResolved;
|
||||
unsigned char ucsmallIdReadMask = connectionInfo->m_smallIdReadMask;
|
||||
create_task( pAsyncOp ).then( [this,resolveDetails,mask,pucsmallIdReadMaskResolved,ucsmallIdReadMask] (task<Microsoft::Xbox::Services::Social::XboxUserProfile^> resultTask)
|
||||
{
|
||||
try
|
||||
{
|
||||
Microsoft::Xbox::Services::Social::XboxUserProfile^ result = resultTask.get();
|
||||
|
||||
resolveDetails->m_name.assign(result->Gamertag->Data()); // Use the gamertag for this data, as it is synchronised round all the machines and so we can't use a display name that could be a real name
|
||||
|
||||
EnterCriticalSection(&m_csHostGamertagResolveResults);
|
||||
// Update flags for which names have been resolved, and if this completes this set, then set the flag to say that we should synchronise these out to the clients
|
||||
*pucsmallIdReadMaskResolved |= mask;
|
||||
LogCommentFormat(L"<<>> Compare %d to %d",*pucsmallIdReadMaskResolved,ucsmallIdReadMask);
|
||||
if(ucsmallIdReadMask == *pucsmallIdReadMaskResolved)
|
||||
{
|
||||
resolveDetails->m_sync = true;
|
||||
delete pucsmallIdReadMaskResolved;
|
||||
}
|
||||
m_hostGamertagResolveResults.push(resolveDetails);
|
||||
LeaveCriticalSection(&m_csHostGamertagResolveResults);
|
||||
}
|
||||
|
||||
catch (Platform::Exception^ ex)
|
||||
{
|
||||
LogComment("Name resolve exception raised");
|
||||
// TODO - handle errors more usefully than just not setting the name...
|
||||
EnterCriticalSection(&m_csHostGamertagResolveResults);
|
||||
// Update flags for which names have been resolved, and if this completes this set, then set the flag to say that we should synchronise these out to the clients
|
||||
*pucsmallIdReadMaskResolved |= mask;
|
||||
if(ucsmallIdReadMask == *pucsmallIdReadMaskResolved)
|
||||
{
|
||||
resolveDetails->m_sync = true;
|
||||
delete pucsmallIdReadMaskResolved;
|
||||
}
|
||||
m_hostGamertagResolveResults.push(resolveDetails);
|
||||
LeaveCriticalSection(&m_csHostGamertagResolveResults);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch(connectionInfo->m_internalDataState)
|
||||
{
|
||||
case DQRConnectionInfo::ConnectionState_InternalAssignSmallId0:
|
||||
connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallId1;
|
||||
break;
|
||||
case DQRConnectionInfo::ConnectionState_InternalAssignSmallId1:
|
||||
connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallId2;
|
||||
break;
|
||||
case DQRConnectionInfo::ConnectionState_InternalAssignSmallId2:
|
||||
connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallId3;
|
||||
break;
|
||||
case DQRConnectionInfo::ConnectionState_InternalAssignSmallId3:
|
||||
connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalHeaderByte;
|
||||
break;
|
||||
}
|
||||
|
||||
pNextByte++;
|
||||
break;
|
||||
case DQRConnectionInfo::ConnectionState_InternalRoomSyncData:
|
||||
connectionInfo->m_pucRoomSyncData[connectionInfo->m_roomSyncDataBytesRead++] = byte;
|
||||
// The room sync info is sent as a 4 byte count of the length of XUID strings, then the RoomSyncData, then the XUID strings
|
||||
if( connectionInfo->m_roomSyncDataBytesToRead == 0 )
|
||||
{
|
||||
// At first stage of reading the 4 byte count
|
||||
if( connectionInfo->m_roomSyncDataBytesRead == 4 )
|
||||
{
|
||||
memcpy( &connectionInfo->m_roomSyncDataBytesToRead, connectionInfo->m_pucRoomSyncData, 4);
|
||||
delete [] connectionInfo->m_pucRoomSyncData;
|
||||
connectionInfo->m_roomSyncDataBytesToRead += sizeof(RoomSyncData);
|
||||
connectionInfo->m_pucRoomSyncData = new unsigned char[ connectionInfo->m_roomSyncDataBytesToRead ];
|
||||
connectionInfo->m_roomSyncDataBytesRead = 0;
|
||||
}
|
||||
}
|
||||
else if( connectionInfo->m_roomSyncDataBytesRead == connectionInfo->m_roomSyncDataBytesToRead )
|
||||
{
|
||||
// Second stage of reading the variable length data - when we've read this all, we can created storage for the XUID strings and copy them all in
|
||||
RoomSyncData *roomSyncData = (RoomSyncData *)connectionInfo->m_pucRoomSyncData;
|
||||
wchar_t *pwcsData = (wchar_t *)((unsigned char *)connectionInfo->m_pucRoomSyncData + sizeof(RoomSyncData));
|
||||
for( int i = 0; i < roomSyncData->playerCount; i++ )
|
||||
{
|
||||
unsigned int thisWchars = ( wcslen(pwcsData) + 1 );
|
||||
roomSyncData->players[i].m_XUID = new wchar_t[thisWchars];
|
||||
wcsncpy(roomSyncData->players[i].m_XUID, pwcsData, thisWchars);
|
||||
pwcsData += thisWchars;
|
||||
}
|
||||
// Update the room sync data with this new data. This will handle notification of new and removed players
|
||||
UpdateRoomSyncPlayers((RoomSyncData *)connectionInfo->m_pucRoomSyncData);
|
||||
|
||||
delete connectionInfo->m_pucRoomSyncData;
|
||||
connectionInfo->m_pucRoomSyncData = NULL;
|
||||
connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalHeaderByte;
|
||||
|
||||
// If we haven't actually established a connection yet for this channel, then this is the point where we can consider this active
|
||||
if( !connectionInfo->m_channelActive[connectionInfo->m_currentChannel] )
|
||||
{
|
||||
DQRNetworkManager::LogCommentFormat(L"Received data from host, channel %d considered active (%d bytes)\n",connectionInfo->m_currentChannel,connectionInfo->m_bytesRemaining);
|
||||
connectionInfo->m_channelActive[connectionInfo->m_currentChannel] = true;
|
||||
|
||||
// This is also the time (as a client) to inform the chat integration layer of the host's session address, since we can now (reliably) send data to it
|
||||
if(connectionInfo->m_currentChannel == 0)
|
||||
{
|
||||
if( m_chat )
|
||||
{
|
||||
m_chat->OnNewSessionAddressAdded(m_hostSessionAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move to starting & playing states, if we are still joining rather than adding an additional player from this client, and we have all the local players here.
|
||||
// We need to check that they are all here because we could have received a broadcast room sync data caused by another machine joining, and and so we can't assume
|
||||
// that we're ready to go just yet.
|
||||
if( m_state == DQRNetworkManager::DNM_INT_STATE_JOINING_SENDING_UNRELIABLE )
|
||||
{
|
||||
bool allLocalPlayersHere = true;
|
||||
for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ )
|
||||
{
|
||||
if( m_currentUserMask & ( 1 << i ) )
|
||||
{
|
||||
if( GetLocalPlayerByUserIndex(i) == NULL )
|
||||
{
|
||||
allLocalPlayersHere = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( allLocalPlayersHere )
|
||||
{
|
||||
DQRNetworkManager::LogComment(L"All local players present");
|
||||
SetState(DQRNetworkManager::DNM_INT_STATE_STARTING);
|
||||
SetState(DQRNetworkManager::DNM_INT_STATE_PLAYING);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Our players aren't all here yet. Going to keep on sending unreliable packets though until the connection is up and running
|
||||
DQRNetworkManager::LogComment(L"All local players not yet present");
|
||||
}
|
||||
}
|
||||
}
|
||||
pNextByte++;
|
||||
break;
|
||||
case DQRConnectionInfo::ConnectionState_InternalAddPlayerFailedData:
|
||||
connectionInfo->m_pucAddFailedPlayerData[connectionInfo->m_addFailedPlayerDataBytesRead++] = byte;
|
||||
// The failed player info is sent as a 4 byte count of the length of XUID string, then the string itself
|
||||
if( connectionInfo->m_addFailedPlayerDataBytesToRead == 0 )
|
||||
{
|
||||
// At first stage of reading the 4 byte count
|
||||
if( connectionInfo->m_addFailedPlayerDataBytesRead == 4 )
|
||||
{
|
||||
memcpy( &connectionInfo->m_addFailedPlayerDataBytesToRead, connectionInfo->m_pucAddFailedPlayerData, 4);
|
||||
delete [] connectionInfo->m_pucAddFailedPlayerData;
|
||||
connectionInfo->m_pucAddFailedPlayerData = new unsigned char[ connectionInfo->m_addFailedPlayerDataBytesToRead ];
|
||||
connectionInfo->m_addFailedPlayerDataBytesRead = 0;
|
||||
}
|
||||
}
|
||||
else if( connectionInfo->m_addFailedPlayerDataBytesRead == connectionInfo->m_addFailedPlayerDataBytesToRead )
|
||||
{
|
||||
// XUID fully read, can now handle what to do with it
|
||||
AddPlayerFailed(ref new Platform::String( (wchar_t *)connectionInfo->m_pucAddFailedPlayerData ) );
|
||||
delete [] connectionInfo->m_pucAddFailedPlayerData;
|
||||
connectionInfo->m_pucAddFailedPlayerData = NULL;
|
||||
connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalHeaderByte;
|
||||
}
|
||||
|
||||
pNextByte++;
|
||||
break;
|
||||
}
|
||||
} while (pNextByte != pEndByte);
|
||||
}
|
||||
|
||||
// This method directly sends bytes via the network communication layer, used to send both game data & data internal to the DQRNetworkManager itself.
|
||||
// This is used by higher level sending methods that wrap communications up with headers that can be processed at the receiving end.
|
||||
void DQRNetworkManager::SendBytesRaw(int smallId, BYTE *bytes, int byteCount, bool reliableAndSequential)
|
||||
{
|
||||
bool broadcast;
|
||||
unsigned int sessionAddress;
|
||||
|
||||
// app.DebugPrintf("{%d,%d - %d}\n",smallId,reliableAndSequential,byteCount);
|
||||
|
||||
if( smallId == -1 )
|
||||
{
|
||||
LogCommentFormat(L"Attempting broadcast, exception of address m_XRNS_Session->LocalSessionAddress %d %d %d", smallId, byteCount, reliableAndSequential);
|
||||
// Broadcast, used from host only
|
||||
broadcast = true;
|
||||
sessionAddress = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send to individual session address
|
||||
broadcast = false;
|
||||
if( m_isHosting )
|
||||
{
|
||||
sessionAddress = m_sessionAddressFromSmallId[ smallId ];
|
||||
}
|
||||
else
|
||||
{
|
||||
sessionAddress = m_hostSessionAddress;
|
||||
}
|
||||
}
|
||||
RTS_SendData(bytes, byteCount, sessionAddress, reliableAndSequential, reliableAndSequential, reliableAndSequential, broadcast, true);
|
||||
}
|
||||
|
||||
// This method is called by the chat integration layer to be able to send data
|
||||
void DQRNetworkManager::SendBytesChat(unsigned int address, BYTE *bytes, int byteCount, bool reliable, bool sequential, bool broadcast)
|
||||
{
|
||||
unsigned int sessionAddress;
|
||||
|
||||
if( broadcast )
|
||||
{
|
||||
sessionAddress = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send to individual session address
|
||||
sessionAddress = address;
|
||||
}
|
||||
|
||||
RTS_SendData(bytes, byteCount, sessionAddress, reliable, sequential, false, broadcast, false);
|
||||
}
|
||||
|
||||
// This is the higher level sending method for sending game data - this prefixes the send with a header so that it will get routed to the correct player.
|
||||
void DQRNetworkManager::SendBytes(int smallId, BYTE *bytes, int byteCount)
|
||||
{
|
||||
EnterCriticalSection(&m_csSendBytes);
|
||||
unsigned char *tempSendBuffer = (unsigned char *)malloc(8191 + 2);
|
||||
|
||||
BYTE *data = bytes;
|
||||
BYTE *dataEnd = bytes + byteCount;
|
||||
|
||||
// Data to be sent has a header to say which of our own internal channels it is on (2 bits), and number of bytes that are in the message.
|
||||
// The number of bytes has to be stored in 13 bits, and so a maximum of 8191 bytes can be send at a time. Split up longer messages into
|
||||
// blocks of this size.
|
||||
do
|
||||
{
|
||||
int bytesToSend = (int)(dataEnd - data);
|
||||
if( bytesToSend > 8191 ) bytesToSend = 8191;
|
||||
|
||||
// Send header with data sending mode - see full comment in DQRNetworkManagerEventHandlers::DataReceivedHandler
|
||||
tempSendBuffer[0] = ( m_channelFromSmallId[smallId] << 5 ) | ( bytesToSend >> 8 );
|
||||
tempSendBuffer[1] = bytesToSend & 0xff;
|
||||
memcpy(&tempSendBuffer[2], data, bytesToSend);
|
||||
|
||||
SendBytesRaw(smallId, tempSendBuffer, bytesToSend + 2, true);
|
||||
|
||||
data += bytesToSend;
|
||||
} while (data != dataEnd);
|
||||
|
||||
free(tempSendBuffer);
|
||||
LeaveCriticalSection(&m_csSendBytes);
|
||||
}
|
||||
|
||||
int DQRNetworkManager::GetQueueSizeBytes()
|
||||
{
|
||||
return m_RTS_Stat_totalBytes;
|
||||
}
|
||||
|
||||
int DQRNetworkManager::GetQueueSizeMessages()
|
||||
{
|
||||
return m_RTS_Stat_totalSends;
|
||||
}
|
||||
651
Minecraft.Client/Durango/Network/DQRNetworkManager_XRNSEvent.cpp
Normal file
651
Minecraft.Client/Durango/Network/DQRNetworkManager_XRNSEvent.cpp
Normal file
@@ -0,0 +1,651 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "DQRNetworkManager.h"
|
||||
#include "PartyController.h"
|
||||
#include <collection.h>
|
||||
#include <ppltasks.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include "..\Minecraft.World\StringHelpers.h"
|
||||
#include "base64.h"
|
||||
|
||||
#ifdef _DURANGO
|
||||
#include "..\Minecraft.World\DurangoStats.h"
|
||||
#endif
|
||||
|
||||
#include "ChatIntegrationLayer.h"
|
||||
|
||||
using namespace Concurrency;
|
||||
using namespace Windows::Foundation::Collections;
|
||||
|
||||
DQRNetworkManagerEventHandlers::DQRNetworkManagerEventHandlers(DQRNetworkManager *pDQRNet)
|
||||
{
|
||||
m_pDQRNet = pDQRNet;
|
||||
}
|
||||
|
||||
void DQRNetworkManagerEventHandlers::Setup(WXNRs::Session^ session)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_dataReceivedToken = session->DataReceived += ref new Windows::Foundation::EventHandler<WXNRs::DataReceivedEventArgs^>(this, &DQRNetworkManagerEventHandlers::DataReceivedHandler);
|
||||
m_sessionStatusToken = session->SessionStatusUpdate += ref new Windows::Foundation::EventHandler<WXNRs::SessionStatusUpdateEventArgs^>(this, &DQRNetworkManagerEventHandlers::SessionStatusUpdateHandler);
|
||||
m_sessionAddressToken = session->SessionAddressDataChanged += ref new Windows::Foundation::EventHandler<WXNRs::SessionAddressDataChangedEventArgs^>(this, &DQRNetworkManagerEventHandlers::SessionAddressDataChangedHandler);
|
||||
m_addedSessionToken = session->AddedSessionAddress += ref new Windows::Foundation::EventHandler<WXNRs::AddedSessionAddressEventArgs^>(this, &DQRNetworkManagerEventHandlers::AddedSessionAddressHandler);
|
||||
m_removedSessionToken = session->RemovedSessionAddress += ref new Windows::Foundation::EventHandler<WXNRs::RemovedSessionAddressEventArgs^>(this, &DQRNetworkManagerEventHandlers::RemovedSessionAddressHandler);
|
||||
m_globalDataToken = session->GlobalSessionDataChanged += ref new Windows::Foundation::EventHandler<WXNRs::GlobalSessionDataChangedEventArgs^>(this, &DQRNetworkManagerEventHandlers::GlobalSessionDataChangedHandler);
|
||||
}
|
||||
catch(Platform::COMException^ ex)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
}
|
||||
|
||||
void DQRNetworkManagerEventHandlers::Pulldown(WXNRs::Session^ session)
|
||||
{
|
||||
try
|
||||
{
|
||||
session->DataReceived -= m_dataReceivedToken;
|
||||
session->SessionStatusUpdate -= m_sessionStatusToken;
|
||||
session->SessionAddressDataChanged -= m_sessionAddressToken;
|
||||
session->AddedSessionAddress -= m_addedSessionToken;
|
||||
session->RemovedSessionAddress -= m_removedSessionToken;
|
||||
session->GlobalSessionDataChanged -= m_globalDataToken;
|
||||
}
|
||||
catch(Platform::COMException^ ex)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
}
|
||||
|
||||
// This event handler is called directly by the realtime session layer, when data is received. We split this data into into data that is meant to be
|
||||
// handled internally by the DQRNetworkManager, and that which is meant to be passed on to the game itself as communication between players.
|
||||
void DQRNetworkManagerEventHandlers::DataReceivedHandler(Platform::Object^ session, WXNRs::DataReceivedEventArgs^ args)
|
||||
{
|
||||
// DQRNetworkManager::LogCommentFormat(L"DataReceivedHandler session addr: 0x%x (%d bytes)",args->SessionAddress,args->Data->Length);
|
||||
|
||||
if (session == m_pDQRNet->m_XRNS_Session)
|
||||
{
|
||||
EnterCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming);
|
||||
DQRNetworkManager::RTS_Message rtsMessage;
|
||||
if( args->ChannelId == WXNRs::ChannelId::DefaultChatReceive )
|
||||
{
|
||||
rtsMessage.m_eType = DQRNetworkManager::eRTSMessageType::RTS_MESSAGE_DATA_RECEIVED_CHAT;
|
||||
}
|
||||
else
|
||||
{
|
||||
rtsMessage.m_eType = DQRNetworkManager::eRTSMessageType::RTS_MESSAGE_DATA_RECEIVED;
|
||||
}
|
||||
rtsMessage.m_sessionAddress = args->SessionAddress;
|
||||
rtsMessage.m_dataSize = args->Data->Length;
|
||||
rtsMessage.m_pucData = (unsigned char *)malloc(rtsMessage.m_dataSize);
|
||||
memcpy( rtsMessage.m_pucData, args->Data->Data, rtsMessage.m_dataSize );
|
||||
m_pDQRNet->m_RTSMessageQueueIncoming.push(rtsMessage);
|
||||
LeaveCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming);
|
||||
}
|
||||
}
|
||||
|
||||
// This event handler is called by the realtime session layer, when session address data is updated. We don't currently use session address data.
|
||||
void DQRNetworkManagerEventHandlers::SessionAddressDataChangedHandler(Platform::Object^ session, WXNRs::SessionAddressDataChangedEventArgs^ args)
|
||||
{
|
||||
DQRNetworkManager::LogComment(L"SessionAddressDataChangedHandler");
|
||||
}
|
||||
|
||||
// This event handler is called by the realtime session layer when a session changes status. We use this to determine that a connection has been made made to the host,
|
||||
// and the case when a connection has been terminated.
|
||||
void DQRNetworkManagerEventHandlers::SessionStatusUpdateHandler(Platform::Object^ session, WXNRs::SessionStatusUpdateEventArgs^ args)
|
||||
{
|
||||
DQRNetworkManager::LogComment(L"SessionStatusUpdateHandler");
|
||||
if (m_pDQRNet->m_XRNS_Session == session)
|
||||
{
|
||||
switch(args->NewStatus)
|
||||
{
|
||||
case WXNRs::SessionStatus::Active:
|
||||
{
|
||||
DQRNetworkManager::LogComment(L"Session active");
|
||||
m_pDQRNet->m_XRNS_LocalAddress = m_pDQRNet->m_XRNS_Session->LocalSessionAddress;
|
||||
m_pDQRNet->m_XRNS_OldestAddress = m_pDQRNet->m_XRNS_Session->OldestSessionAddress;
|
||||
EnterCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming);
|
||||
DQRNetworkManager::RTS_Message rtsMessage;
|
||||
rtsMessage.m_eType = DQRNetworkManager::eRTSMessageType::RTS_MESSAGE_STATUS_ACTIVE;
|
||||
rtsMessage.m_sessionAddress = 0;
|
||||
rtsMessage.m_dataSize = 0;
|
||||
rtsMessage.m_pucData = 0;
|
||||
m_pDQRNet->m_RTSMessageQueueIncoming.push(rtsMessage);
|
||||
LeaveCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming);
|
||||
}
|
||||
break;
|
||||
case WXNRs::SessionStatus::Terminated:
|
||||
{
|
||||
DQRNetworkManager::LogComment(L"Session terminated");
|
||||
EnterCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming);
|
||||
DQRNetworkManager::RTS_Message rtsMessage;
|
||||
rtsMessage.m_eType = DQRNetworkManager::eRTSMessageType::RTS_MESSAGE_STATUS_TERMINATED;
|
||||
rtsMessage.m_sessionAddress = 0;
|
||||
rtsMessage.m_dataSize = 0;
|
||||
rtsMessage.m_pucData = 0;
|
||||
m_pDQRNet->m_RTSMessageQueueIncoming.push(rtsMessage);
|
||||
LeaveCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming);
|
||||
}
|
||||
break;
|
||||
case WXNRs::SessionStatus::Activating:
|
||||
DQRNetworkManager::LogComment(L"Session activating");
|
||||
break;
|
||||
case WXNRs::SessionStatus::Terminating:
|
||||
DQRNetworkManager::LogComment(L"Session terminating");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This event is called from the realtime session layer to notify any clients that a new endpoint has been connected into the network mesh.
|
||||
void DQRNetworkManagerEventHandlers::AddedSessionAddressHandler(Platform::Object^ session, WXNRs::AddedSessionAddressEventArgs^ args)
|
||||
{
|
||||
DQRNetworkManager::LogCommentFormat(L"AddedSessionAddressHandler session address 0x%x",args->SessionAddress);
|
||||
EnterCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming);
|
||||
DQRNetworkManager::RTS_Message rtsMessage;
|
||||
rtsMessage.m_eType = DQRNetworkManager::eRTSMessageType::RTS_MESSAGE_ADDED_SESSION_ADDRESS;
|
||||
rtsMessage.m_sessionAddress = args->SessionAddress;
|
||||
rtsMessage.m_dataSize = 0;
|
||||
rtsMessage.m_pucData = 0;
|
||||
m_pDQRNet->m_RTSMessageQueueIncoming.push(rtsMessage);
|
||||
LeaveCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming);
|
||||
}
|
||||
|
||||
// This event is called from the realtime session layer to notify any clients that an endpoint has been removed from the network mesh.
|
||||
void DQRNetworkManagerEventHandlers::RemovedSessionAddressHandler(Platform::Object^ session, WXNRs::RemovedSessionAddressEventArgs^ args)
|
||||
{
|
||||
DQRNetworkManager::LogCommentFormat(L"RemovedSessionAddressHandler session address 0x%x", args->SessionAddress);
|
||||
EnterCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming);
|
||||
DQRNetworkManager::RTS_Message rtsMessage;
|
||||
rtsMessage.m_eType = DQRNetworkManager::eRTSMessageType::RTS_MESSAGE_REMOVED_SESSION_ADDRESS;
|
||||
rtsMessage.m_sessionAddress = args->SessionAddress;
|
||||
rtsMessage.m_dataSize = 0;
|
||||
rtsMessage.m_pucData = 0;
|
||||
m_pDQRNet->m_RTSMessageQueueIncoming.push(rtsMessage);
|
||||
LeaveCriticalSection(&m_pDQRNet->m_csRTSMessageQueueIncoming);
|
||||
}
|
||||
|
||||
// This event is called from the realtime session layer when session global data has been updated. We don't currently use global session data.
|
||||
void DQRNetworkManagerEventHandlers::GlobalSessionDataChangedHandler(Platform::Object^ session, WXNRs::GlobalSessionDataChangedEventArgs^ args)
|
||||
{
|
||||
DQRNetworkManager::LogComment(L"GlobalSessionDataChangedHandler");
|
||||
}
|
||||
|
||||
void DQRNetworkManager::UpdateRTSStats()
|
||||
{
|
||||
Platform::Array<unsigned int> ^sessionAddresses = nullptr;
|
||||
try
|
||||
{
|
||||
sessionAddresses = m_XRNS_Session->GetAllRemoteSessionAddresses(WXNRs::RemoteSessionAddressStateOptions::All, WXNRs::RemoteSessionAddressConnectivityOptions::All);
|
||||
}
|
||||
catch(Platform::COMException^ ex)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
|
||||
if( sessionAddresses )
|
||||
{
|
||||
unsigned int totalBytes = 0;
|
||||
unsigned int totalSends = 0;
|
||||
for( unsigned int i = 0; i < sessionAddresses->Length; i++ )
|
||||
{
|
||||
try
|
||||
{
|
||||
totalBytes += m_XRNS_Session->GetSendChannelOutstandingBytes(sessionAddresses->get(i), WXNRs::ChannelId::DefaultGameSend );
|
||||
totalSends += m_XRNS_Session->GetSendChannelOutstandingSends(sessionAddresses->get(i), WXNRs::ChannelId::DefaultGameSend );
|
||||
}
|
||||
catch(Platform::COMException^ ex)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
}
|
||||
m_RTS_Stat_totalBytes = totalBytes;
|
||||
m_RTS_Stat_totalSends = totalSends;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RTS_Stat_totalBytes = 0;
|
||||
m_RTS_Stat_totalSends = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DQRNetworkManager::ProcessRTSMessagesIncoming()
|
||||
{
|
||||
EnterCriticalSection(&m_csRTSMessageQueueIncoming);
|
||||
while(m_RTSMessageQueueIncoming.size() > 0 )
|
||||
{
|
||||
RTS_Message message = m_RTSMessageQueueIncoming.front();
|
||||
switch( message.m_eType )
|
||||
{
|
||||
case eRTSMessageType::RTS_MESSAGE_DATA_RECEIVED:
|
||||
Process_RTS_MESSAGE_DATA_RECEIVED(message);
|
||||
break;
|
||||
case eRTSMessageType::RTS_MESSAGE_DATA_RECEIVED_CHAT:
|
||||
Process_RTS_MESSAGE_DATA_RECEIVED_CHAT(message);
|
||||
break;
|
||||
case eRTSMessageType::RTS_MESSAGE_ADDED_SESSION_ADDRESS:
|
||||
Process_RTS_MESSAGE_ADDED_SESSION_ADDRESS(message);
|
||||
break;
|
||||
case eRTSMessageType::RTS_MESSAGE_REMOVED_SESSION_ADDRESS:
|
||||
Process_RTS_MESSAGE_REMOVED_SESSION_ADDRESS(message);
|
||||
break;
|
||||
case eRTSMessageType::RTS_MESSAGE_STATUS_ACTIVE:
|
||||
Process_RTS_MESSAGE_STATUS_ACTIVE(message);
|
||||
break;
|
||||
case eRTSMessageType::RTS_MESSAGE_STATUS_TERMINATED:
|
||||
Process_RTS_MESSAGE_STATUS_TERMINATED(message);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_RTSMessageQueueIncoming.pop();
|
||||
}
|
||||
LeaveCriticalSection(&m_csRTSMessageQueueIncoming);
|
||||
};
|
||||
|
||||
void DQRNetworkManager::Process_RTS_MESSAGE_DATA_RECEIVED(RTS_Message &message)
|
||||
{
|
||||
DQRConnectionInfo *connectionInfo;
|
||||
if( m_isHosting )
|
||||
{
|
||||
connectionInfo = m_sessionAddressToConnectionInfoMapHost[message.m_sessionAddress];
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionInfo = &m_connectionInfoClient;
|
||||
}
|
||||
|
||||
// Handle any header data, and actual data, in our stream. Data is as follows:
|
||||
// Byte 0 Byte 1
|
||||
// fccsssss ssssssss
|
||||
//
|
||||
// Where: f is 0 if this is normal data send (to be passed up to the game), or is 1 if this is to be internally processed
|
||||
// cc is the channel number that the data belongs to (0 to 3 representing actual player indices)
|
||||
// sssssssssssss is the count of data bytes to follow (range 0 - 8191)
|
||||
BYTE *pNextByte = message.m_pucData;
|
||||
BYTE *pEndByte = pNextByte + message.m_dataSize;
|
||||
do
|
||||
{
|
||||
BYTE byte = *pNextByte;
|
||||
switch( connectionInfo->m_state )
|
||||
{
|
||||
case DQRConnectionInfo::ConnectionState_HeaderByte0:
|
||||
connectionInfo->m_currentChannel = ( byte >> 5 ) & 3;
|
||||
connectionInfo->m_internalFlag = ( ( byte & 0x80 ) == 0x80 );
|
||||
|
||||
// Byte transfer mode. Bits 0-4 of this byte represent the upper 5 bits of our count of bytes to transfer... lower 8-bits will follow
|
||||
connectionInfo->m_bytesRemaining = ((int)( byte & 0x1f )) << 8;
|
||||
connectionInfo->m_state = DQRConnectionInfo::ConnectionState_HeaderByte1;
|
||||
connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalHeaderByte;
|
||||
pNextByte++;
|
||||
break;
|
||||
case DQRConnectionInfo::ConnectionState_HeaderByte1:
|
||||
// Add in the lower 8 bits of our byte count, the upper 5 were obtained from the first header byte.
|
||||
connectionInfo->m_bytesRemaining |= byte;
|
||||
|
||||
// If there isn't any data following, then just go back to the initial state expecting another header byte.
|
||||
if( connectionInfo->m_bytesRemaining == 0 )
|
||||
{
|
||||
connectionInfo->m_state = DQRConnectionInfo::ConnectionState_HeaderByte0;
|
||||
}
|
||||
else
|
||||
{
|
||||
connectionInfo->m_state = DQRConnectionInfo::ConnectionState_ReadBytes;
|
||||
}
|
||||
pNextByte++;
|
||||
break;
|
||||
case DQRConnectionInfo::ConnectionState_ReadBytes:
|
||||
// At this stage we can send up to connectionInfo->m_bytesRemaining bytes, or the number of bytes that we have remaining in the data received, whichever is lowest.
|
||||
int bytesInBuffer = (int)(pEndByte - pNextByte);
|
||||
int bytesToReceive = ( ( connectionInfo->m_bytesRemaining < bytesInBuffer ) ? connectionInfo->m_bytesRemaining : bytesInBuffer );
|
||||
|
||||
if( connectionInfo->m_internalFlag )
|
||||
{
|
||||
BytesReceivedInternal(connectionInfo, message.m_sessionAddress, pNextByte, bytesToReceive );
|
||||
}
|
||||
else
|
||||
{
|
||||
BytesReceived(connectionInfo->m_smallId[connectionInfo->m_currentChannel], pNextByte, bytesToReceive );
|
||||
}
|
||||
|
||||
// Adjust counts and pointers
|
||||
pNextByte += bytesToReceive;
|
||||
connectionInfo->m_bytesRemaining -= bytesToReceive;
|
||||
|
||||
// Set state back to expect a header if there is no more data bytes to receive
|
||||
if( connectionInfo->m_bytesRemaining == 0 )
|
||||
{
|
||||
connectionInfo->m_state = DQRConnectionInfo::ConnectionState_HeaderByte0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} while (pNextByte != pEndByte);
|
||||
|
||||
free(message.m_pucData);
|
||||
}
|
||||
|
||||
void DQRNetworkManager::Process_RTS_MESSAGE_DATA_RECEIVED_CHAT(RTS_Message &message)
|
||||
{
|
||||
if( m_chat )
|
||||
{
|
||||
m_chat->OnIncomingChatMessage(message.m_sessionAddress, Platform::ArrayReference<BYTE>(message.m_pucData, message.m_dataSize) );
|
||||
free(message.m_pucData);
|
||||
}
|
||||
}
|
||||
|
||||
void DQRNetworkManager::Process_RTS_MESSAGE_ADDED_SESSION_ADDRESS(RTS_Message &message)
|
||||
{
|
||||
if( m_chat )
|
||||
{
|
||||
m_chat->OnNewSessionAddressAdded(message.m_sessionAddress);
|
||||
}
|
||||
|
||||
// New session address - add a mapping for it
|
||||
if( m_isHosting )
|
||||
{
|
||||
auto it = m_sessionAddressToConnectionInfoMapHost.find(message.m_sessionAddress);
|
||||
DQRConnectionInfo *connectionInfo;
|
||||
if( it == m_sessionAddressToConnectionInfoMapHost.end() )
|
||||
{
|
||||
connectionInfo = new DQRConnectionInfo();
|
||||
|
||||
m_sessionAddressToConnectionInfoMapHost[message.m_sessionAddress] = connectionInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This shouldn't happen as we should be removing mappings as session addresses are removed.
|
||||
connectionInfo = it->second;
|
||||
connectionInfo->Reset();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void DQRNetworkManager::Process_RTS_MESSAGE_REMOVED_SESSION_ADDRESS(RTS_Message &message)
|
||||
{
|
||||
if( m_chat )
|
||||
{
|
||||
m_chat->RemoveRemoteConsole(message.m_sessionAddress);
|
||||
}
|
||||
|
||||
if( m_isHosting )
|
||||
{
|
||||
auto it = m_sessionAddressToConnectionInfoMapHost.find(message.m_sessionAddress);
|
||||
|
||||
if( it != m_sessionAddressToConnectionInfoMapHost.end() )
|
||||
{
|
||||
delete it->second;
|
||||
m_sessionAddressToConnectionInfoMapHost.erase(it);
|
||||
RemoveRoomSyncPlayersWithSessionAddress(message.m_sessionAddress);
|
||||
SendRoomSyncInfo();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// As the client, if we are disonnected from the host, then it is all over. Proceed as if leaving the room.
|
||||
if( message.m_sessionAddress == m_hostSessionAddress )
|
||||
{
|
||||
LeaveRoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DQRNetworkManager::Process_RTS_MESSAGE_STATUS_ACTIVE(RTS_Message &message)
|
||||
{
|
||||
// When we detect that the session has become active, we start sending unreliable packets, until we get some data back. This is because there is an issue with the
|
||||
// realtime session layer where it is telling us that the connection is active a bit to early, and it will disconnect if it receives a packet that must be reliable in this
|
||||
// state.
|
||||
if( !m_isHosting )
|
||||
{
|
||||
m_firstUnreliableSendTime = 0;
|
||||
m_hostSessionAddress = m_XRNS_OldestAddress;
|
||||
// Also initialise the status of this connection
|
||||
m_connectionInfoClient.Reset();
|
||||
SetState(DQRNetworkManager::DNM_INT_STATE_JOINING_SENDING_UNRELIABLE);
|
||||
}
|
||||
}
|
||||
|
||||
void DQRNetworkManager::Process_RTS_MESSAGE_STATUS_TERMINATED(RTS_Message &message)
|
||||
{
|
||||
if( m_state == DQRNetworkManager::DNM_INT_STATE_JOINING_WAITING_FOR_ACTIVE_SESSION )
|
||||
{
|
||||
m_joinCreateSessionAttempts++;
|
||||
if( m_joinCreateSessionAttempts > DQRNetworkManager::JOIN_CREATE_SESSION_MAX_ATTEMPTS )
|
||||
{
|
||||
SetState(DQRNetworkManager::DNM_INT_STATE_JOINING_FAILED);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetState(DQRNetworkManager::DNM_INT_STATE_JOINING_GET_SDA);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DQRNetworkManager::_RTSDoWorkThread(void* lpParameter)
|
||||
{
|
||||
DQRNetworkManager *pDQR = (DQRNetworkManager *)lpParameter;
|
||||
return pDQR->RTSDoWorkThread();
|
||||
}
|
||||
|
||||
static const DWORD XRNS_TERMINATE_LOCAL_SESSION_FLAG_IMMEDIATE = 0x00000001;
|
||||
int DQRNetworkManager::RTSDoWorkThread()
|
||||
{
|
||||
do
|
||||
{
|
||||
if( m_XRNS_Session )
|
||||
{
|
||||
try
|
||||
{
|
||||
m_XRNS_Session->DoWork(20);
|
||||
}
|
||||
catch(Platform::COMException^ ex)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
UpdateRTSStats();
|
||||
}
|
||||
else
|
||||
{
|
||||
Sleep(20);
|
||||
}
|
||||
ProcessRTSMessagesOutgoing();
|
||||
} while(true);
|
||||
}
|
||||
|
||||
void DQRNetworkManager::ProcessRTSMessagesOutgoing()
|
||||
{
|
||||
EnterCriticalSection(&m_csRTSMessageQueueOutgoing);
|
||||
while(m_RTSMessageQueueOutgoing.size() > 0 )
|
||||
{
|
||||
RTS_Message message = m_RTSMessageQueueOutgoing.front();
|
||||
switch( message.m_eType )
|
||||
{
|
||||
case eRTSMessageType::RTS_MESSAGE_START_CLIENT:
|
||||
Process_RTS_MESSAGE_START_CLIENT(message);
|
||||
break;
|
||||
case eRTSMessageType::RTS_MESSAGE_START_HOST:
|
||||
Process_RTS_MESSAGE_START_HOST(message);
|
||||
break;
|
||||
case eRTSMessageType::RTS_MESSAGE_TERMINATE:
|
||||
Process_RTS_MESSAGE_TERMINATE(message);
|
||||
break;
|
||||
case eRTSMessageType::RTS_MESSAGE_SEND_DATA:
|
||||
Process_RTS_MESSAGE_SEND_DATA(message);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_RTSMessageQueueOutgoing.pop();
|
||||
}
|
||||
LeaveCriticalSection(&m_csRTSMessageQueueOutgoing);
|
||||
};
|
||||
|
||||
void DQRNetworkManager::Process_RTS_MESSAGE_START_CLIENT(RTS_Message &message)
|
||||
{
|
||||
if( m_XRNS_Session )
|
||||
{
|
||||
m_eventHandlers->Pulldown(m_XRNS_Session);
|
||||
// Close XRNS session
|
||||
try
|
||||
{
|
||||
m_XRNS_Session->TerminateLocalSession(XRNS_TERMINATE_LOCAL_SESSION_FLAG_IMMEDIATE);
|
||||
}
|
||||
catch(Platform::COMException^ ex)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
|
||||
m_XRNS_Session = nullptr;
|
||||
}
|
||||
|
||||
m_XRNS_Session = ref new WXNRs::Session( m_localSocketAddress, m_remoteSocketAddress, MAX_PLAYERS_IN_TEMPLATE, 0);
|
||||
m_XRNS_Session->MinSendRate = 512000;
|
||||
|
||||
LogCommentFormat(L"connect retry period %d retries %d, data retry count %d, data retry timeout %d\n",m_XRNS_Session->ConnectRetryPeriod,m_XRNS_Session->MaxConnectRetries,m_XRNS_Session->MaxDataRetries,m_XRNS_Session->MinDataRetryTimeout);
|
||||
|
||||
m_XRNS_Session->MaxConnectRetries = 50; // 50 at 100ms intervals = 5 seconds of attempting to connect
|
||||
|
||||
m_eventHandlers->Setup(m_XRNS_Session);
|
||||
}
|
||||
|
||||
void DQRNetworkManager::Process_RTS_MESSAGE_START_HOST(RTS_Message &message)
|
||||
{
|
||||
m_XRNS_Session = ref new WXNRs::Session( m_localSocketAddress, MAX_PLAYERS_IN_TEMPLATE, 0);
|
||||
m_XRNS_Session->MinSendRate = 512000;
|
||||
m_XRNS_Session->MaxConnectRetries = 50; // 50 at 100ms intervals = 5 seconds of attempting to connect
|
||||
m_eventHandlers->Setup(m_XRNS_Session);
|
||||
}
|
||||
|
||||
void DQRNetworkManager::Process_RTS_MESSAGE_TERMINATE(RTS_Message &message)
|
||||
{
|
||||
if( m_XRNS_Session )
|
||||
{
|
||||
m_eventHandlers->Pulldown(m_XRNS_Session);
|
||||
// Close XRNS session
|
||||
try
|
||||
{
|
||||
m_XRNS_Session->TerminateLocalSession(XRNS_TERMINATE_LOCAL_SESSION_FLAG_IMMEDIATE);
|
||||
}
|
||||
catch(Platform::COMException^ ex)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
|
||||
m_XRNS_Session = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void DQRNetworkManager::Process_RTS_MESSAGE_SEND_DATA(RTS_Message &message)
|
||||
{
|
||||
if( m_XRNS_Session )
|
||||
{
|
||||
unsigned int sessionAddress = message.m_sessionAddress;
|
||||
|
||||
try
|
||||
{
|
||||
if( message.m_flags & eRTSFlags::RTS_MESSAGE_FLAG_BROADCAST_MODE )
|
||||
{
|
||||
sessionAddress = m_XRNS_Session->LocalSessionAddress;
|
||||
}
|
||||
|
||||
m_XRNS_Session->Send( ( message.m_flags & eRTSFlags::RTS_MESSAGE_FLAG_GAME_CHANNEL ) ? WXNRs::ChannelId::DefaultGameSend : WXNRs::ChannelId::DefaultChatSend,
|
||||
( message.m_flags & eRTSFlags::RTS_MESSAGE_FLAG_BROADCAST_MODE ) ? WXNRs::SendExceptionType::ExcludedAddresses : WXNRs::SendExceptionType::IncludedAddresses,
|
||||
Platform::ArrayReference<unsigned int>(&message.m_sessionAddress, 1),
|
||||
Platform::ArrayReference<BYTE>(message.m_pucData, message.m_dataSize),
|
||||
0,
|
||||
( message.m_flags & eRTSFlags::RTS_MESSAGE_FLAG_RELIABLE ) ? WXNRs::Send_Reliability::Reliable : WXNRs::Send_Reliability::NonReliable,
|
||||
( message.m_flags & eRTSFlags::RTS_MESSAGE_FLAG_SEQUENTIAL ) ? WXNRs::Send_Sequence::Sequential : WXNRs::Send_Sequence::NonSequential,
|
||||
WXNRs::Send_Ack::AckNormal,
|
||||
( message.m_flags & eRTSFlags::RTS_MESSAGE_FLAG_COALESCE ) ? WXNRs::Send_Coalesce::CoalesceDelay : WXNRs::Send_Coalesce::CoalesceNever,
|
||||
WXNRs::Send_MiscState::NoMiscState );
|
||||
}
|
||||
catch(Platform::COMException^ ex)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
// swallow exceptions
|
||||
}
|
||||
}
|
||||
free(message.m_pucData);
|
||||
}
|
||||
|
||||
void DQRNetworkManager::RTS_StartCient()
|
||||
{
|
||||
EnterCriticalSection(&m_csRTSMessageQueueOutgoing);
|
||||
RTS_Message message;
|
||||
message.m_eType = eRTSMessageType::RTS_MESSAGE_START_CLIENT;
|
||||
message.m_pucData = NULL;
|
||||
message.m_dataSize = 0;
|
||||
m_RTSMessageQueueOutgoing.push(message);
|
||||
LeaveCriticalSection(&m_csRTSMessageQueueOutgoing);
|
||||
}
|
||||
|
||||
void DQRNetworkManager::RTS_StartHost()
|
||||
{
|
||||
EnterCriticalSection(&m_csRTSMessageQueueOutgoing);
|
||||
RTS_Message message;
|
||||
message.m_eType = eRTSMessageType::RTS_MESSAGE_START_HOST;
|
||||
message.m_pucData = NULL;
|
||||
message.m_dataSize = 0;
|
||||
m_RTSMessageQueueOutgoing.push(message);
|
||||
LeaveCriticalSection(&m_csRTSMessageQueueOutgoing);
|
||||
}
|
||||
|
||||
void DQRNetworkManager::RTS_Terminate()
|
||||
{
|
||||
EnterCriticalSection(&m_csRTSMessageQueueOutgoing);
|
||||
RTS_Message message;
|
||||
message.m_eType = eRTSMessageType::RTS_MESSAGE_TERMINATE;
|
||||
message.m_pucData = NULL;
|
||||
message.m_dataSize = 0;
|
||||
m_RTSMessageQueueOutgoing.push(message);
|
||||
LeaveCriticalSection(&m_csRTSMessageQueueOutgoing);
|
||||
}
|
||||
|
||||
void DQRNetworkManager::RTS_SendData(unsigned char *pucData, unsigned int dataSize, unsigned int sessionAddress, bool reliable, bool sequential, bool coalesce, bool broadcastMode, bool gameChannel )
|
||||
{
|
||||
EnterCriticalSection(&m_csRTSMessageQueueOutgoing);
|
||||
RTS_Message message;
|
||||
message.m_eType = eRTSMessageType::RTS_MESSAGE_SEND_DATA;
|
||||
message.m_pucData = (unsigned char *)malloc(dataSize);
|
||||
memcpy(message.m_pucData, pucData, dataSize);
|
||||
message.m_dataSize = dataSize;
|
||||
message.m_sessionAddress = sessionAddress;
|
||||
message.m_flags = 0;
|
||||
if( reliable ) message.m_flags |= eRTSFlags::RTS_MESSAGE_FLAG_RELIABLE;
|
||||
if( sequential ) message.m_flags |= eRTSFlags::RTS_MESSAGE_FLAG_SEQUENTIAL;
|
||||
if( coalesce ) message.m_flags |= eRTSFlags::RTS_MESSAGE_FLAG_COALESCE;
|
||||
if( broadcastMode ) message.m_flags |= eRTSFlags::RTS_MESSAGE_FLAG_BROADCAST_MODE;
|
||||
if( gameChannel ) message.m_flags |= eRTSFlags::RTS_MESSAGE_FLAG_GAME_CHANNEL;
|
||||
m_RTSMessageQueueOutgoing.push(message);
|
||||
LeaveCriticalSection(&m_csRTSMessageQueueOutgoing);
|
||||
}
|
||||
204
Minecraft.Client/Durango/Network/DQRNetworkPlayer.cpp
Normal file
204
Minecraft.Client/Durango/Network/DQRNetworkPlayer.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
#include "stdafx.h"
|
||||
#include "DQRNetworkPlayer.h"
|
||||
#include "ChatIntegrationLayer.h"
|
||||
|
||||
DQRNetworkPlayer::DQRNetworkPlayer()
|
||||
{
|
||||
}
|
||||
|
||||
DQRNetworkPlayer::DQRNetworkPlayer(DQRNetworkManager *manager, eDQRNetworkPlayerType playerType, bool onHost, int localPlayerIdx, unsigned int sessionAddress)
|
||||
{
|
||||
m_localPlayerIdx = localPlayerIdx;
|
||||
m_type = playerType;
|
||||
m_host = onHost;
|
||||
m_manager = manager;
|
||||
m_customData = 0;
|
||||
m_sessionAddress = sessionAddress;
|
||||
}
|
||||
|
||||
DQRNetworkPlayer::~DQRNetworkPlayer()
|
||||
{
|
||||
}
|
||||
|
||||
PlayerUID DQRNetworkPlayer::GetUID()
|
||||
{
|
||||
return m_UID;
|
||||
}
|
||||
|
||||
void DQRNetworkPlayer::SetUID(PlayerUID UID)
|
||||
{
|
||||
m_UID = UID;
|
||||
}
|
||||
|
||||
int DQRNetworkPlayer::GetLocalPlayerIndex()
|
||||
{
|
||||
return m_localPlayerIdx;
|
||||
}
|
||||
|
||||
uintptr_t DQRNetworkPlayer::GetCustomDataValue()
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
void DQRNetworkPlayer::SetCustomDataValue(uintptr_t data)
|
||||
{
|
||||
m_customData = data;
|
||||
}
|
||||
|
||||
bool DQRNetworkPlayer::IsRemote()
|
||||
{
|
||||
return !IsLocal();
|
||||
}
|
||||
|
||||
bool DQRNetworkPlayer::IsHost()
|
||||
{
|
||||
return (m_type == DNP_TYPE_HOST);
|
||||
}
|
||||
|
||||
bool DQRNetworkPlayer::IsLocal()
|
||||
{
|
||||
// m_host determines whether this *machine* is hosting the game, not this player (which is determined by m_type)
|
||||
if( m_host )
|
||||
{
|
||||
// If we are the hosting machine, then both the host & local players are local to this machine
|
||||
return (m_type == DNP_TYPE_HOST) || (m_type == DNP_TYPE_LOCAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not hosting, just local players are actually physically local
|
||||
return (m_type == DNP_TYPE_LOCAL) ;
|
||||
}
|
||||
}
|
||||
|
||||
bool DQRNetworkPlayer::IsSameSystem(DQRNetworkPlayer *other)
|
||||
{
|
||||
return ( m_sessionAddress == other->m_sessionAddress );
|
||||
}
|
||||
|
||||
bool DQRNetworkPlayer::IsTalking()
|
||||
{
|
||||
if(m_manager->m_chat == nullptr) return false;
|
||||
Microsoft::Xbox::GameChat::ChatUser^ chatUser = m_manager->m_chat->GetChatUserByXboxUserId(ref new Platform::String(m_UID.toString().c_str()));
|
||||
|
||||
if( chatUser == nullptr ) return false;
|
||||
if( chatUser->TalkingMode == Microsoft::Xbox::GameChat::ChatUserTalkingMode::NotTalking )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool DQRNetworkPlayer::HasVoice()
|
||||
{
|
||||
if(m_manager->m_chat == nullptr) return false;
|
||||
Microsoft::Xbox::GameChat::ChatUser^ chatUser = m_manager->m_chat->GetChatUserByXboxUserId(ref new Platform::String(m_UID.toString().c_str()));
|
||||
|
||||
if( chatUser == nullptr ) return false;
|
||||
if( ((int)chatUser->ParticipantType) & ((int)Windows::Xbox::Chat::ChatParticipantTypes::Talker) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DQRNetworkPlayer::HasCamera()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LPCWSTR DQRNetworkPlayer::GetGamertag()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
int DQRNetworkPlayer::GetSmallId()
|
||||
{
|
||||
return (int)m_smallId;
|
||||
}
|
||||
|
||||
void DQRNetworkPlayer::SetSmallId(unsigned char smallId)
|
||||
{
|
||||
m_smallId = smallId;
|
||||
}
|
||||
|
||||
int DQRNetworkPlayer::GetSessionIndex()
|
||||
{
|
||||
return m_manager->GetSessionIndex(this);
|
||||
}
|
||||
|
||||
// Attempt to send data, of any size, from this player to that specified by pPlayerTarget. This may not be possible depending on the two players, due to
|
||||
// our star shaped network connectivity. Data may be any size, and is copied so on returning from this method it does not need to be preserved.
|
||||
void DQRNetworkPlayer::SendData( DQRNetworkPlayer *pPlayerTarget, const void *data, unsigned int dataSize )
|
||||
{
|
||||
// Our network is connected as a star. If we are the host, then we can send to any remote player. If we're a client, we can send only to the host.
|
||||
// The host can also send to other local players, but this doesn't need to go through Rudp.
|
||||
if( m_host )
|
||||
{
|
||||
if( ( m_type == DNP_TYPE_HOST ) && ( pPlayerTarget->m_type == DNP_TYPE_REMOTE ) )
|
||||
{
|
||||
// Rudp communication from host to remote player - handled by remote player instance
|
||||
pPlayerTarget->SendInternal(data,dataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Can't do any other types of communications
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( ( m_type == DNP_TYPE_LOCAL ) && ( pPlayerTarget->m_type == DNP_TYPE_HOST ) )
|
||||
{
|
||||
// Rudp communication from client to host - handled by this player instace
|
||||
SendInternal(data, dataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Can't do any other types of communications
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DQRNetworkPlayer::SendInternal(const void *data, unsigned int dataSize)
|
||||
{
|
||||
m_manager->SendBytes(m_smallId, (BYTE *)data, dataSize);
|
||||
}
|
||||
|
||||
int DQRNetworkPlayer::GetSendQueueSizeBytes()
|
||||
{
|
||||
return m_manager->GetQueueSizeBytes();
|
||||
}
|
||||
|
||||
int DQRNetworkPlayer::GetSendQueueSizeMessages()
|
||||
{
|
||||
return m_manager->GetQueueSizeMessages();
|
||||
}
|
||||
|
||||
wchar_t *DQRNetworkPlayer::GetName()
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void DQRNetworkPlayer::SetName(const wchar_t *name)
|
||||
{
|
||||
wcscpy_s(m_name, name);
|
||||
}
|
||||
|
||||
// Return display name (if display name is not set, return name instead)
|
||||
wstring DQRNetworkPlayer::GetDisplayName()
|
||||
{
|
||||
return (m_displayName == L"") ? m_name : m_displayName;
|
||||
}
|
||||
|
||||
// Set display name
|
||||
void DQRNetworkPlayer::SetDisplayName(wstring displayName)
|
||||
{
|
||||
m_displayName = displayName;
|
||||
}
|
||||
65
Minecraft.Client/Durango/Network/DQRNetworkPlayer.h
Normal file
65
Minecraft.Client/Durango/Network/DQRNetworkPlayer.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include "DQRNetworkManager.h"
|
||||
#include <queue>
|
||||
|
||||
// This is the lowest level class for handling the concept of a player on Durango. This is managed by DQRNetworkManager. The game shouldn't directly communicate
|
||||
// with this class, as it is wrapped by NetworkPlayerDurango which is an implementation of a platform-independent interface INetworkPlayer.
|
||||
|
||||
class DQRNetworkPlayer
|
||||
{
|
||||
public:
|
||||
friend class DQRNetworkManager;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DNP_TYPE_HOST, // This player represents the host
|
||||
DNP_TYPE_LOCAL, // On host - this player is a local player that needs communicated with specially not using rudp. On clients - this is a local player, where network communications can be used to communicate with the host
|
||||
DNP_TYPE_REMOTE, // On host - this player can be used to communicate from between the host and this player. On clients - this is a remote player that cannot be communicated with
|
||||
} eDQRNetworkPlayerType;
|
||||
|
||||
DQRNetworkPlayer();
|
||||
DQRNetworkPlayer(DQRNetworkManager *manager, eDQRNetworkPlayerType playerType, bool onHost, int localPlayerIdx, unsigned int sessionAddress);
|
||||
~DQRNetworkPlayer();
|
||||
|
||||
PlayerUID GetUID();
|
||||
void SetUID(PlayerUID UID);
|
||||
int GetLocalPlayerIndex();
|
||||
uintptr_t GetCustomDataValue();
|
||||
void SetCustomDataValue(uintptr_t data);
|
||||
bool IsRemote();
|
||||
bool IsHost();
|
||||
bool IsLocal();
|
||||
bool IsSameSystem(DQRNetworkPlayer *other);
|
||||
bool HasVoice();
|
||||
bool IsTalking();
|
||||
bool HasCamera();
|
||||
LPCWSTR GetGamertag();
|
||||
int GetSmallId();
|
||||
void SetSmallId(unsigned char smallId);
|
||||
int GetSessionIndex();
|
||||
void SendData( DQRNetworkPlayer *pPlayerTarget, const void *data, unsigned int dataSize );
|
||||
|
||||
int GetSendQueueSizeBytes();
|
||||
int GetSendQueueSizeMessages();
|
||||
|
||||
wchar_t *GetName();
|
||||
void SetName(const wchar_t *name);
|
||||
|
||||
std::wstring GetDisplayName();
|
||||
void SetDisplayName(std::wstring displayName);
|
||||
private:
|
||||
void SendInternal(const void *data, unsigned int dataSize);
|
||||
|
||||
eDQRNetworkPlayerType m_type; // The player type
|
||||
bool m_host; // Whether this actual player class is stored on a host (not whether it represents the host, or a player on the host machine)
|
||||
int m_localPlayerIdx; // Index of this player on the machine to which it belongs
|
||||
DQRNetworkManager *m_manager; // Pointer back to the manager that is managing this player
|
||||
|
||||
PlayerUID m_UID;
|
||||
uintptr_t m_customData;
|
||||
unsigned char m_smallId;
|
||||
unsigned int m_sessionAddress;
|
||||
|
||||
wchar_t m_name[21];
|
||||
std::wstring m_displayName;
|
||||
};
|
||||
113
Minecraft.Client/Durango/Network/NetworkPlayerDurango.cpp
Normal file
113
Minecraft.Client/Durango/Network/NetworkPlayerDurango.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "stdafx.h"
|
||||
#include "NetworkPlayerDurango.h"
|
||||
|
||||
NetworkPlayerDurango::NetworkPlayerDurango(DQRNetworkPlayer *qnetPlayer)
|
||||
{
|
||||
m_dqrPlayer = qnetPlayer;
|
||||
m_pSocket = NULL;
|
||||
}
|
||||
|
||||
unsigned char NetworkPlayerDurango::GetSmallId()
|
||||
{
|
||||
return m_dqrPlayer->GetSmallId();
|
||||
}
|
||||
|
||||
void NetworkPlayerDurango::SendData(INetworkPlayer *player, const void *pvData, int dataSize, bool lowPriority)
|
||||
{
|
||||
m_dqrPlayer->SendData( ((NetworkPlayerDurango *)player)->m_dqrPlayer, pvData, dataSize );
|
||||
}
|
||||
|
||||
bool NetworkPlayerDurango::IsSameSystem(INetworkPlayer *player)
|
||||
{
|
||||
return m_dqrPlayer->IsSameSystem(((NetworkPlayerDurango *)player)->m_dqrPlayer);
|
||||
}
|
||||
|
||||
int NetworkPlayerDurango::GetSendQueueSizeBytes( INetworkPlayer *player, bool lowPriority )
|
||||
{
|
||||
return m_dqrPlayer->GetSendQueueSizeBytes();
|
||||
}
|
||||
|
||||
int NetworkPlayerDurango::GetSendQueueSizeMessages( INetworkPlayer *player, bool lowPriority )
|
||||
{
|
||||
return m_dqrPlayer->GetSendQueueSizeMessages();
|
||||
}
|
||||
|
||||
int NetworkPlayerDurango::GetCurrentRtt()
|
||||
{
|
||||
return 0; // TODO
|
||||
}
|
||||
|
||||
bool NetworkPlayerDurango::IsHost()
|
||||
{
|
||||
return m_dqrPlayer->IsHost();
|
||||
}
|
||||
|
||||
bool NetworkPlayerDurango::IsGuest()
|
||||
{
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
bool NetworkPlayerDurango::IsLocal()
|
||||
{
|
||||
return m_dqrPlayer->IsLocal();
|
||||
}
|
||||
|
||||
int NetworkPlayerDurango::GetSessionIndex()
|
||||
{
|
||||
return m_dqrPlayer->GetSessionIndex();
|
||||
}
|
||||
|
||||
bool NetworkPlayerDurango::IsTalking()
|
||||
{
|
||||
return m_dqrPlayer->IsTalking();
|
||||
}
|
||||
|
||||
bool NetworkPlayerDurango::IsMutedByLocalUser(int userIndex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NetworkPlayerDurango::HasVoice()
|
||||
{
|
||||
return m_dqrPlayer->HasVoice();
|
||||
}
|
||||
|
||||
bool NetworkPlayerDurango::HasCamera()
|
||||
{
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
int NetworkPlayerDurango::GetUserIndex()
|
||||
{
|
||||
return m_dqrPlayer->GetLocalPlayerIndex();
|
||||
}
|
||||
|
||||
void NetworkPlayerDurango::SetSocket(Socket *pSocket)
|
||||
{
|
||||
m_pSocket = pSocket;
|
||||
}
|
||||
|
||||
Socket *NetworkPlayerDurango::GetSocket()
|
||||
{
|
||||
return m_pSocket;
|
||||
}
|
||||
|
||||
const wchar_t *NetworkPlayerDurango::GetOnlineName()
|
||||
{
|
||||
return m_dqrPlayer->GetName();
|
||||
}
|
||||
|
||||
wstring NetworkPlayerDurango::GetDisplayName()
|
||||
{
|
||||
return m_dqrPlayer->GetDisplayName();
|
||||
}
|
||||
|
||||
PlayerUID NetworkPlayerDurango::GetUID()
|
||||
{
|
||||
return m_dqrPlayer->GetUID();
|
||||
}
|
||||
|
||||
void NetworkPlayerDurango::SetUID(PlayerUID UID)
|
||||
{
|
||||
m_dqrPlayer->SetUID(UID);
|
||||
}
|
||||
39
Minecraft.Client/Durango/Network/NetworkPlayerDurango.h
Normal file
39
Minecraft.Client/Durango/Network/NetworkPlayerDurango.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "..\..\Common\Network\NetworkPlayerInterface.h"
|
||||
#include "DQRNetworkPlayer.h"
|
||||
|
||||
// This is an implementation of the INetworkPlayer interface, for Durango. It effectively wraps the DQRNetworkPlayer class in a non-platform-specific way.
|
||||
|
||||
class NetworkPlayerDurango : public INetworkPlayer
|
||||
{
|
||||
public:
|
||||
// Common player interface
|
||||
NetworkPlayerDurango(DQRNetworkPlayer *sqrPlayer);
|
||||
virtual unsigned char GetSmallId();
|
||||
virtual void SendData(INetworkPlayer *player, const void *pvData, int dataSize, bool lowPriority);
|
||||
virtual bool IsSameSystem(INetworkPlayer *player);
|
||||
virtual int GetSendQueueSizeBytes( INetworkPlayer *player, bool lowPriority );
|
||||
virtual int GetSendQueueSizeMessages( INetworkPlayer *player, bool lowPriority );
|
||||
virtual int GetCurrentRtt();
|
||||
virtual bool IsHost();
|
||||
virtual bool IsGuest();
|
||||
virtual bool IsLocal();
|
||||
virtual int GetSessionIndex();
|
||||
virtual bool IsTalking();
|
||||
virtual bool IsMutedByLocalUser(int userIndex);
|
||||
virtual bool HasVoice();
|
||||
virtual bool HasCamera();
|
||||
virtual int GetUserIndex();
|
||||
virtual void SetSocket(Socket *pSocket);
|
||||
virtual Socket *GetSocket();
|
||||
virtual const wchar_t *GetOnlineName();
|
||||
virtual wstring GetDisplayName();
|
||||
virtual PlayerUID GetUID();
|
||||
|
||||
void SetUID(PlayerUID UID);
|
||||
|
||||
private:
|
||||
DQRNetworkPlayer *m_dqrPlayer;
|
||||
Socket *m_pSocket;
|
||||
};
|
||||
1205
Minecraft.Client/Durango/Network/PartyController.cpp
Normal file
1205
Minecraft.Client/Durango/Network/PartyController.cpp
Normal file
File diff suppressed because it is too large
Load Diff
74
Minecraft.Client/Durango/Network/PartyController.h
Normal file
74
Minecraft.Client/Durango/Network/PartyController.h
Normal file
@@ -0,0 +1,74 @@
|
||||
//// 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 <ppltasks.h>
|
||||
class DQRNetworkManager;
|
||||
|
||||
class PartyController
|
||||
{
|
||||
public:
|
||||
PartyController(DQRNetworkManager *pDQRNet);
|
||||
|
||||
void SetPartyView( Windows::Xbox::Multiplayer::PartyView^ partyView );
|
||||
Windows::Xbox::Multiplayer::PartyView^ GetPartyView();
|
||||
|
||||
void RefreshPartyView();
|
||||
bool AddLocalUsersToParty(int userMask, Windows::Xbox::System::User^ primaryUser);
|
||||
void RemoveLocalUsersFromParty(Windows::Xbox::System::User^ primaryUser);
|
||||
void RemoveLocalUsersFromParty(Windows::Xbox::System::User^ primaryUser, int playerMask, Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionReference);
|
||||
void RemoveLocalUserFromParty(Windows::Xbox::System::User^ userToRemove);
|
||||
void RegisterEventHandlers();
|
||||
void UnregisterEventHandlers();
|
||||
void UnregisterGamePlayersEventHandler();
|
||||
void RegisterGamePlayersChangedEventHandler();
|
||||
bool CanJoinParty();
|
||||
bool CanInvitePartyToMyGame( Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ multiplayerSession );
|
||||
bool IsPartyInAnotherTitle();
|
||||
bool IsGameSessionReadyEventTriggered();
|
||||
bool DoesPartySessionExist();
|
||||
Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference ^ GetGamePartySessionReference();
|
||||
void ClearGameSessionReadyEventTriggered();
|
||||
int GetActiveAndReservedMemberPartySize();
|
||||
bool DoesPartyAndSessionPlayersMatch(
|
||||
Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ session
|
||||
);
|
||||
void CheckPartySessionFull(Windows::Xbox::System::User^ primaryUser);
|
||||
void SetJoinability(bool isJoinable);
|
||||
void DisassociateSessionFromParty( Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ sessionReference);
|
||||
|
||||
private:
|
||||
Concurrency::critical_section m_lock;
|
||||
bool m_isGameSessionReadyEventTriggered;
|
||||
bool m_isGamePlayerEventRegistered;
|
||||
DQRNetworkManager *m_pDQRNet;
|
||||
|
||||
static void DebugPrintPartyView( Windows::Xbox::Multiplayer::PartyView^ partyView );
|
||||
|
||||
void OnPartyStateChanged( Windows::Xbox::Multiplayer::PartyStateChangedEventArgs^ eventArgs );
|
||||
void OnPartyRosterChanged( Windows::Xbox::Multiplayer::PartyRosterChangedEventArgs^ eventArgs );
|
||||
void OnGamePlayersChanged( Windows::Xbox::Multiplayer::GamePlayersChangedEventArgs^ eventArgs );
|
||||
void OnGameSessionReady( Windows::Xbox::Multiplayer::GameSessionReadyEventArgs^ eventArgs );
|
||||
Windows::Xbox::Multiplayer::PartyView^ m_partyView;
|
||||
Microsoft::Xbox::Services::Multiplayer::MultiplayerSessionReference^ m_partyGameReadyRef;
|
||||
void AddAvailableGamePlayers(
|
||||
Windows::Foundation::Collections::IVectorView<Windows::Xbox::Multiplayer::GamePlayer^>^ availablePlayers,
|
||||
int& remainingSlots,
|
||||
Microsoft::Xbox::Services::Multiplayer::MultiplayerSession^ currentSession
|
||||
);
|
||||
|
||||
Windows::Foundation::DateTime GetCurrentTime();
|
||||
double GetTimeBetweenInSeconds(Windows::Foundation::DateTime dt1, Windows::Foundation::DateTime dt2);
|
||||
|
||||
// Party/Session events.
|
||||
Windows::Foundation::EventRegistrationToken m_partyRosterChangedToken;
|
||||
Windows::Foundation::EventRegistrationToken m_partyStateChangedToken;
|
||||
Windows::Foundation::EventRegistrationToken m_partyGamePlayersChangedToken;
|
||||
Windows::Foundation::EventRegistrationToken m_partyGameSessionReadyToken;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,940 @@
|
||||
#include "stdafx.h"
|
||||
#include "..\..\..\Minecraft.World\Socket.h"
|
||||
#include "..\..\..\Minecraft.World\StringHelpers.h"
|
||||
#include "PlatformNetworkManagerDurango.h"
|
||||
#include "NetworkPlayerDurango.h"
|
||||
|
||||
CPlatformNetworkManagerDurango *g_pPlatformNetworkManager;
|
||||
|
||||
void CPlatformNetworkManagerDurango::HandleStateChange(DQRNetworkManager::eDQRNetworkManagerState oldState, DQRNetworkManager::eDQRNetworkManagerState newState)
|
||||
{
|
||||
static const char * c_apszStateNames[] =
|
||||
{
|
||||
"DNM_STATE_INITIALISING",
|
||||
"DNM_STATE_INITIALISE_FAILED",
|
||||
"DNM_STATE_IDLE",
|
||||
"DNM_STATE_HOSTING",
|
||||
"DNM_STATE_JOINING",
|
||||
"DNM_STATE_STARTING",
|
||||
"DNM_STATE_PLAYING",
|
||||
"DNM_STATE_LEAVING",
|
||||
"DNM_STATE_ENDING",
|
||||
};
|
||||
|
||||
app.DebugPrintf( "Network State: %s ==> %s\n",
|
||||
c_apszStateNames[ oldState ],
|
||||
c_apszStateNames[ newState ] );
|
||||
|
||||
if( newState == DQRNetworkManager::DNM_STATE_HOSTING )
|
||||
{
|
||||
m_bLeavingGame = false;
|
||||
m_bLeaveGameOnTick = false;
|
||||
m_bHostChanged = false;
|
||||
g_NetworkManager.StateChange_AnyToHosting();
|
||||
}
|
||||
else if( newState == DQRNetworkManager::DNM_STATE_JOINING )
|
||||
{
|
||||
m_bLeavingGame = false;
|
||||
m_bLeaveGameOnTick = false;
|
||||
m_bHostChanged = false;
|
||||
g_NetworkManager.StateChange_AnyToJoining();
|
||||
}
|
||||
else if( newState == DQRNetworkManager::DNM_STATE_IDLE && oldState == DQRNetworkManager::DNM_STATE_JOINING )
|
||||
{
|
||||
g_NetworkManager.StateChange_JoiningToIdle(JOIN_FAILED_NONSPECIFIC);
|
||||
}
|
||||
else if( newState == DQRNetworkManager::DNM_STATE_IDLE && oldState == DQRNetworkManager::DNM_STATE_HOSTING )
|
||||
{
|
||||
m_bLeavingGame = true;
|
||||
}
|
||||
else if( newState == DQRNetworkManager::DNM_STATE_STARTING )
|
||||
{
|
||||
m_lastPlayerEventTimeStart = app.getAppTime();
|
||||
|
||||
g_NetworkManager.StateChange_AnyToStarting();
|
||||
}
|
||||
// Fix for #93148 - TCR 001: BAS Game Stability: Title will crash for the multiplayer client if host of the game will exit during the clients loading to created world.
|
||||
// 4J Stu - If the client joins just as the host is exiting, then they can skip to leaving without passing through ending
|
||||
else if( newState == DQRNetworkManager::DNM_STATE_ENDING )
|
||||
{
|
||||
g_NetworkManager.StateChange_AnyToEnding( oldState == DQRNetworkManager::DNM_STATE_PLAYING );
|
||||
|
||||
if( m_pDQRNet->IsHost() )
|
||||
{
|
||||
m_bLeavingGame = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( newState == DQRNetworkManager::DNM_STATE_IDLE )
|
||||
{
|
||||
g_NetworkManager.StateChange_AnyToIdle();
|
||||
}
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::HandlePlayerJoined(DQRNetworkPlayer *pDQRPlayer)
|
||||
{
|
||||
const char * pszDescription;
|
||||
|
||||
// If this is a local player, we need to inform the chat system that it has joined
|
||||
if( pDQRPlayer->IsLocal() )
|
||||
{
|
||||
m_pDQRNet->ChatPlayerJoined(pDQRPlayer->GetLocalPlayerIndex());
|
||||
}
|
||||
|
||||
// 4J Stu - We create a fake socket for every where that we need an INBOUND queue of game data. Outbound
|
||||
// is all handled by QNet so we don't need that. Therefore each client player has one, and the host has one
|
||||
// for each client player.
|
||||
bool createFakeSocket = false;
|
||||
bool localPlayer = false;
|
||||
|
||||
NetworkPlayerDurango *networkPlayer = (NetworkPlayerDurango *)addNetworkPlayer(pDQRPlayer);
|
||||
|
||||
// Request full display name for this player
|
||||
m_pDQRNet->RequestDisplayName(pDQRPlayer);
|
||||
|
||||
if( pDQRPlayer->IsLocal() )
|
||||
{
|
||||
localPlayer = true;
|
||||
if( pDQRPlayer->IsHost() )
|
||||
{
|
||||
pszDescription = "local host";
|
||||
// 4J Stu - No socket for the localhost as it uses a special loopback queue
|
||||
|
||||
m_machineDQRPrimaryPlayers.push_back( pDQRPlayer );
|
||||
}
|
||||
else
|
||||
{
|
||||
pszDescription = "local";
|
||||
|
||||
// We need an inbound queue on all local players to receive data from the host
|
||||
createFakeSocket = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( pDQRPlayer->IsHost() )
|
||||
{
|
||||
pszDescription = "remote host";
|
||||
}
|
||||
else
|
||||
{
|
||||
pszDescription = "remote";
|
||||
|
||||
// If we are the host, then create a fake socket for every remote player
|
||||
if( m_pDQRNet->IsHost() )
|
||||
{
|
||||
createFakeSocket = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( m_pDQRNet->IsHost() && !m_bHostChanged )
|
||||
{
|
||||
// Do we already have a primary player for this system?
|
||||
bool systemHasPrimaryPlayer = false;
|
||||
for(AUTO_VAR(it, m_machineDQRPrimaryPlayers.begin()); it < m_machineDQRPrimaryPlayers.end(); ++it)
|
||||
{
|
||||
DQRNetworkPlayer *pQNetPrimaryPlayer = *it;
|
||||
if( pDQRPlayer->IsSameSystem(pQNetPrimaryPlayer) )
|
||||
{
|
||||
systemHasPrimaryPlayer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( !systemHasPrimaryPlayer )
|
||||
m_machineDQRPrimaryPlayers.push_back( pDQRPlayer );
|
||||
}
|
||||
}
|
||||
g_NetworkManager.PlayerJoining( networkPlayer );
|
||||
|
||||
if( createFakeSocket == true && !m_bHostChanged )
|
||||
{
|
||||
g_NetworkManager.CreateSocket( networkPlayer, localPlayer );
|
||||
}
|
||||
|
||||
app.DebugPrintf( "Player 0x%p \"%ls\" joined; %s; voice %i; camera %i.\n",
|
||||
pDQRPlayer,
|
||||
pDQRPlayer->GetGamertag(),
|
||||
pszDescription,
|
||||
(int) pDQRPlayer->HasVoice(),
|
||||
(int) pDQRPlayer->HasCamera() );
|
||||
|
||||
|
||||
if( m_pDQRNet->IsHost() )
|
||||
{
|
||||
g_NetworkManager.UpdateAndSetGameSessionData();
|
||||
SystemFlagAddPlayer( networkPlayer );
|
||||
}
|
||||
|
||||
for( int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
|
||||
{
|
||||
if(playerChangedCallback[idx] != NULL)
|
||||
playerChangedCallback[idx]( playerChangedCallbackParam[idx], networkPlayer, false );
|
||||
}
|
||||
|
||||
if(m_pDQRNet->GetState() == QNET_STATE_GAME_PLAY)
|
||||
{
|
||||
int localPlayerCount = 0;
|
||||
for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
|
||||
{
|
||||
if( m_pDQRNet->GetLocalPlayerByUserIndex(idx) != NULL ) ++localPlayerCount;
|
||||
}
|
||||
|
||||
float appTime = app.getAppTime();
|
||||
|
||||
// Only record stats for the primary player here
|
||||
m_lastPlayerEventTimeStart = appTime;
|
||||
}
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::HandlePlayerLeaving(DQRNetworkPlayer *pDQRPlayer)
|
||||
{
|
||||
//__debugbreak();
|
||||
|
||||
app.DebugPrintf( "Player 0x%p leaving.\n",
|
||||
pDQRPlayer );
|
||||
|
||||
INetworkPlayer *networkPlayer = getNetworkPlayer(pDQRPlayer);
|
||||
|
||||
if( networkPlayer )
|
||||
{
|
||||
// Get our wrapper object associated with this player.
|
||||
Socket *socket = networkPlayer->GetSocket();
|
||||
if( socket != NULL )
|
||||
{
|
||||
// If we are in game then remove this player from the game as well.
|
||||
// We may get here either from the player requesting to exit the game,
|
||||
// in which case we they will already have left the game server, or from a disconnection
|
||||
// where we then have to remove them from the game server
|
||||
if( m_pDQRNet->IsHost() && !m_bHostChanged )
|
||||
{
|
||||
g_NetworkManager.CloseConnection(networkPlayer);
|
||||
}
|
||||
|
||||
// Free the wrapper object memory.
|
||||
// TODO 4J Stu - We may still be using this at the point that the player leaves the session.
|
||||
// We need this as long as the game server still needs to communicate with the player
|
||||
//delete socket;
|
||||
|
||||
networkPlayer->SetSocket( NULL );
|
||||
}
|
||||
|
||||
if( m_pDQRNet->IsHost() && !m_bHostChanged )
|
||||
{
|
||||
if( isSystemPrimaryPlayer(pDQRPlayer) )
|
||||
{
|
||||
DQRNetworkPlayer *pNewDQRPrimaryPlayer = NULL;
|
||||
for(unsigned int i = 0; i < m_pDQRNet->GetPlayerCount(); ++i )
|
||||
{
|
||||
DQRNetworkPlayer *pDQRPlayer2 = m_pDQRNet->GetPlayerByIndex( i );
|
||||
|
||||
if( pDQRPlayer2 != pDQRPlayer && pDQRPlayer2->IsSameSystem( pDQRPlayer ) )
|
||||
{
|
||||
pNewDQRPrimaryPlayer = pDQRPlayer2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
AUTO_VAR(it, find( m_machineDQRPrimaryPlayers.begin(), m_machineDQRPrimaryPlayers.end(), pDQRPlayer));
|
||||
if( it != m_machineDQRPrimaryPlayers.end() )
|
||||
{
|
||||
m_machineDQRPrimaryPlayers.erase( it );
|
||||
}
|
||||
|
||||
if( pNewDQRPrimaryPlayer != NULL )
|
||||
m_machineDQRPrimaryPlayers.push_back( pNewDQRPrimaryPlayer );
|
||||
}
|
||||
|
||||
g_NetworkManager.UpdateAndSetGameSessionData( networkPlayer );
|
||||
SystemFlagRemovePlayer( networkPlayer );
|
||||
|
||||
}
|
||||
|
||||
g_NetworkManager.PlayerLeaving( networkPlayer );
|
||||
|
||||
for( int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
|
||||
{
|
||||
if(playerChangedCallback[idx] != NULL)
|
||||
playerChangedCallback[idx]( playerChangedCallbackParam[idx], networkPlayer, true );
|
||||
}
|
||||
|
||||
if(m_pDQRNet->GetState() == DQRNetworkManager::DNM_STATE_PLAYING)
|
||||
{
|
||||
int localPlayerCount = 0;
|
||||
for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx)
|
||||
{
|
||||
if( m_pDQRNet->GetLocalPlayerByUserIndex(idx) != NULL ) ++localPlayerCount;
|
||||
}
|
||||
|
||||
float appTime = app.getAppTime();
|
||||
m_lastPlayerEventTimeStart = appTime;
|
||||
}
|
||||
|
||||
removeNetworkPlayer(pDQRPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::HandleDataReceived(DQRNetworkPlayer *playerFrom, DQRNetworkPlayer *playerTo, unsigned char *data, unsigned int dataSize)
|
||||
{
|
||||
#if 0
|
||||
// TODO - put this back in
|
||||
if(m_pSQRNet->GetState() == SQRNetworkManager::SNM_STATE_ENDING)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if( playerTo->IsHost() )
|
||||
{
|
||||
// If we are the host we care who this came from
|
||||
//app.DebugPrintf( "Pushing data into host read queue for user \"%ls\"\n", pPlayerFrom->GetGamertag());
|
||||
// Push this data into the read queue for the player that sent it
|
||||
INetworkPlayer *pPlayerFrom = getNetworkPlayer(playerFrom);
|
||||
Socket *socket = pPlayerFrom->GetSocket();
|
||||
|
||||
if(socket != NULL)
|
||||
socket->pushDataToQueue(data, dataSize, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we are not the host the message must have come from the host, so we care more about who it is addressed to
|
||||
INetworkPlayer *pPlayerTo = getNetworkPlayer(playerTo);
|
||||
Socket *socket = pPlayerTo->GetSocket();
|
||||
//app.DebugPrintf( "Pushing data into read queue for user \"%ls\"\n", apPlayersTo[dwPlayer]->GetGamertag());
|
||||
if(socket != NULL)
|
||||
socket->pushDataToQueue(data, dataSize);
|
||||
}
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::HandleInviteReceived(int playerIndex, DQRNetworkManager::SessionInfo *pInviteInfo)
|
||||
{
|
||||
g_NetworkManager.GameInviteReceived(playerIndex, pInviteInfo);
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::Initialise(CGameNetworkManager *pGameNetworkManager, int flagIndexSize)
|
||||
{
|
||||
m_pDQRNet = new DQRNetworkManager(this);
|
||||
m_pDQRNet->Initialise();
|
||||
|
||||
m_hostGameSessionIsJoinable = false;
|
||||
m_pGameNetworkManager = pGameNetworkManager;
|
||||
m_flagIndexSize = flagIndexSize;
|
||||
g_pPlatformNetworkManager = this;
|
||||
for( int i = 0; i < XUSER_MAX_COUNT; i++ )
|
||||
{
|
||||
playerChangedCallback[ i ] = NULL;
|
||||
}
|
||||
|
||||
m_bLeavingGame = false;
|
||||
m_bLeaveGameOnTick = false;
|
||||
m_bHostChanged = false;
|
||||
|
||||
m_bSearchResultsReady = false;
|
||||
m_bSearchPending = false;
|
||||
|
||||
m_bIsOfflineGame = false;
|
||||
m_pSearchParam = NULL;
|
||||
m_SessionsUpdatedCallback = NULL;
|
||||
|
||||
m_searchResultsCount = 0;
|
||||
m_lastSearchStartTime = 0;
|
||||
|
||||
// The results that will be filled in with the current search
|
||||
m_pSearchResults = NULL;
|
||||
|
||||
Windows::Networking::Connectivity::NetworkInformation::NetworkStatusChanged += ref new Windows::Networking::Connectivity::NetworkStatusChangedEventHandler( []( Platform::Object^ pxObject )
|
||||
{
|
||||
app.DebugPrintf("NetworkStatusChanged callback\n" );
|
||||
auto internetProfile = Windows::Networking::Connectivity::NetworkInformation::GetInternetConnectionProfile();
|
||||
if (internetProfile != nullptr)
|
||||
{
|
||||
auto connectionLevel = internetProfile->GetNetworkConnectivityLevel();
|
||||
app.DebugPrintf("Connection level has changed to (%d)\n", connectionLevel);
|
||||
|
||||
//int iPrimaryPlayer = g_NetworkManager.GetPrimaryPad();
|
||||
//bool bConnected = (connectionLevel == Windows::Networking::Connectivity::NetworkConnectivityLevel::XboxLiveAccess)?true:false;
|
||||
//if((g_NetworkManager.GetLockedProfile()!=-1) && iPrimaryPlayer!=-1 && bConnected == false && g_NetworkManager.IsInSession() )
|
||||
//{
|
||||
// app.SetAction(iPrimaryPlayer,eAppAction_EthernetDisconnected);
|
||||
//}
|
||||
}
|
||||
});
|
||||
|
||||
// Success!
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::Terminate()
|
||||
{
|
||||
}
|
||||
|
||||
int CPlatformNetworkManagerDurango::GetJoiningReadyPercentage()
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
int CPlatformNetworkManagerDurango::CorrectErrorIDS(int IDS)
|
||||
{
|
||||
return IDS;
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::isSystemPrimaryPlayer(DQRNetworkPlayer *pDQRPlayer)
|
||||
{
|
||||
bool playerIsSystemPrimary = false;
|
||||
for(auto it = m_machineDQRPrimaryPlayers.begin(); it < m_machineDQRPrimaryPlayers.end(); ++it)
|
||||
{
|
||||
DQRNetworkPlayer *pDQRPrimaryPlayer = *it;
|
||||
if( pDQRPrimaryPlayer == pDQRPlayer )
|
||||
{
|
||||
playerIsSystemPrimary = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return playerIsSystemPrimary;
|
||||
}
|
||||
|
||||
// We call this twice a frame, either side of the render call so is a good place to "tick" things
|
||||
void CPlatformNetworkManagerDurango::DoWork()
|
||||
{
|
||||
m_pDQRNet->Tick();
|
||||
|
||||
TickSearch();
|
||||
|
||||
if( m_bLeaveGameOnTick )
|
||||
{
|
||||
m_pDQRNet->LeaveRoom();
|
||||
m_bLeaveGameOnTick = false;
|
||||
}
|
||||
}
|
||||
|
||||
int CPlatformNetworkManagerDurango::GetPlayerCount()
|
||||
{
|
||||
return m_pDQRNet->GetPlayerCount();
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::ShouldMessageForFullSession()
|
||||
{
|
||||
return m_pDQRNet->ShouldMessageForFullSession();
|
||||
}
|
||||
|
||||
int CPlatformNetworkManagerDurango::GetOnlinePlayerCount()
|
||||
{
|
||||
return m_pDQRNet->GetOnlinePlayerCount();
|
||||
}
|
||||
|
||||
int CPlatformNetworkManagerDurango::GetLocalPlayerMask(int playerIndex)
|
||||
{
|
||||
return 1 << playerIndex;
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::AddLocalPlayerByUserIndex( int userIndex )
|
||||
{
|
||||
return m_pDQRNet->AddLocalPlayerByUserIndex( userIndex );
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::RemoveLocalPlayerByUserIndex( int userIndex )
|
||||
{
|
||||
return m_pDQRNet->RemoveLocalPlayerByUserIndex( userIndex );
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::IsInStatsEnabledSession()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::SessionHasSpace(unsigned int spaceRequired /*= 1*/)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::SendInviteGUI(int quadrant)
|
||||
{
|
||||
m_pDQRNet->SendInviteGUI(quadrant);
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::IsAddingPlayer()
|
||||
{
|
||||
return m_pDQRNet->IsAddingPlayer();
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::LeaveGame(bool bMigrateHost)
|
||||
{
|
||||
if( m_bLeavingGame ) return true;
|
||||
|
||||
m_bLeavingGame = true;
|
||||
|
||||
// If we are the host wait for the game server to end
|
||||
if(m_pDQRNet->IsHost() && g_NetworkManager.ServerStoppedValid())
|
||||
{
|
||||
// m_pDQRNet->EndGame();
|
||||
g_NetworkManager.ServerStoppedWait();
|
||||
g_NetworkManager.ServerStoppedDestroy();
|
||||
}
|
||||
return _LeaveGame(bMigrateHost, true);;
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::_LeaveGame(bool bMigrateHost, bool bLeaveRoom)
|
||||
{
|
||||
m_bLeavingGame = true;
|
||||
m_bLeaveGameOnTick = true;
|
||||
m_migrateHostOnLeave = bMigrateHost;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots /*= MINECRAFT_NET_MAX_PLAYERS*/, unsigned char privateSlots /*= 0*/)
|
||||
{
|
||||
// #ifdef _XBOX
|
||||
// 4J Stu - We probably did this earlier as well, but just to be sure!
|
||||
SetLocalGame( !bOnlineGame );
|
||||
SetPrivateGame( bIsPrivate );
|
||||
SystemFlagReset();
|
||||
|
||||
// Make sure that the Primary Pad is in by default
|
||||
localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() );
|
||||
|
||||
m_bLeavingGame = false;
|
||||
|
||||
_HostGame( localUsersMask, publicSlots, privateSlots );
|
||||
//#endif
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::_HostGame(int usersMask, unsigned char publicSlots /*= MINECRAFT_NET_MAX_PLAYERS*/, unsigned char privateSlots /*= 0*/)
|
||||
{
|
||||
memset(&m_hostGameSessionData,0,sizeof(m_hostGameSessionData));
|
||||
m_hostGameSessionData.netVersion = MINECRAFT_NET_VERSION;
|
||||
m_hostGameSessionData.isReadyToJoin = false;
|
||||
m_hostGameSessionData.m_uiGameHostSettings = app.GetGameHostOption(eGameHostOption_All);
|
||||
m_hostGameSessionIsJoinable = !IsPrivateGame();
|
||||
|
||||
m_pDQRNet->CreateAndJoinSession(usersMask, (unsigned char *)&m_hostGameSessionData, sizeof(m_hostGameSessionData), IsLocalGame() );
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::_StartGame()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int CPlatformNetworkManagerDurango::JoinGame(FriendSessionInfo *searchResult, int localUsersMask, int primaryUserIndex)
|
||||
{
|
||||
app.DebugPrintf("Joining game party from search result\n");
|
||||
|
||||
int joinPlayerCount = 0;
|
||||
for( int i = 0; i < DQRNetworkManager::MAX_LOCAL_PLAYER_COUNT; i++ )
|
||||
{
|
||||
if( localUsersMask & ( 1 << i ) )
|
||||
{
|
||||
// Check if this joining user is already in the session, in which case we don't need to count it
|
||||
bool isJoiningUser = false;
|
||||
for( int j = 0; j < searchResult->searchResult.m_usedSlotCount; j++ )
|
||||
{
|
||||
Platform::String^ xuid = ref new Platform::String(searchResult->searchResult.m_sessionXuids[j].c_str());
|
||||
if( xuid == ProfileManager.GetUser(i)->XboxUserId )
|
||||
{
|
||||
app.DebugPrintf("Joining user found to be already in session, so won't be counting to our total\n");
|
||||
isJoiningUser = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( !isJoiningUser )
|
||||
{
|
||||
joinPlayerCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
app.DebugPrintf("Used slots: %d, fully playing players: %d, trying to join %d\n", searchResult->searchResult.m_usedSlotCount, searchResult->searchResult.m_playerCount, joinPlayerCount);
|
||||
GameSessionData *gameSession = (GameSessionData *)(&searchResult->data);
|
||||
if( ( searchResult->searchResult.m_usedSlotCount + joinPlayerCount ) > DQRNetworkManager::MAX_ONLINE_PLAYER_COUNT )
|
||||
{
|
||||
return CGameNetworkManager::JOINGAME_FAIL_SERVER_FULL;
|
||||
}
|
||||
|
||||
if(m_pDQRNet->JoinPartyFromSearchResult(&searchResult->searchResult, localUsersMask))
|
||||
{
|
||||
app.DebugPrintf("Join success\n");
|
||||
return CGameNetworkManager::JOINGAME_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
app.DebugPrintf("Join fail\n");
|
||||
return CGameNetworkManager::JOINGAME_FAIL_GENERAL;
|
||||
}
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::CancelJoinGame()
|
||||
{
|
||||
m_pDQRNet->CancelJoinPartyFromSearchResult();
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::SetLocalGame(bool isLocal)
|
||||
{
|
||||
m_bIsOfflineGame = isLocal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::SetPrivateGame(bool isPrivate)
|
||||
{
|
||||
app.DebugPrintf("Setting as private game: %s\n", isPrivate ? "yes" : "no" );
|
||||
m_bIsPrivateGame = isPrivate;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::RegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam)
|
||||
{
|
||||
playerChangedCallback[iPad] = callback;
|
||||
playerChangedCallbackParam[iPad] = callbackParam;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::UnRegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam)
|
||||
{
|
||||
if(playerChangedCallbackParam[iPad] == callbackParam)
|
||||
{
|
||||
playerChangedCallback[iPad] = NULL;
|
||||
playerChangedCallbackParam[iPad] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::HandleSignInChange()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::HandleAddLocalPlayerFailed(int idx, bool serverFull)
|
||||
{
|
||||
g_NetworkManager.AddLocalPlayerFailed(idx, serverFull);
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::HandleDisconnect(bool bLostRoomOnly)
|
||||
{
|
||||
g_NetworkManager.HandleDisconnect(bLostRoomOnly);
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::_RunNetworkGame()
|
||||
{
|
||||
m_pDQRNet->StartGame();
|
||||
m_hostGameSessionData.isReadyToJoin = true;
|
||||
m_pDQRNet->UpdateCustomSessionData();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving /*= NULL*/)
|
||||
{
|
||||
if( this->m_bLeavingGame )
|
||||
return;
|
||||
|
||||
if( GetHostPlayer() == NULL )
|
||||
return;
|
||||
|
||||
m_hostGameSessionData.m_uiGameHostSettings = app.GetGameHostOption(eGameHostOption_All);
|
||||
|
||||
m_pDQRNet->UpdateCustomSessionData();
|
||||
}
|
||||
|
||||
int CPlatformNetworkManagerDurango::RemovePlayerOnSocketClosedThreadProc( void* lpParam )
|
||||
{
|
||||
INetworkPlayer *pNetworkPlayer = (INetworkPlayer *)lpParam;
|
||||
|
||||
Socket *socket = pNetworkPlayer->GetSocket();
|
||||
|
||||
if( socket != NULL )
|
||||
{
|
||||
//printf("Waiting for socket closed event\n");
|
||||
socket->m_socketClosedEvent->WaitForSignal(INFINITE);
|
||||
|
||||
//printf("Socket closed event has fired\n");
|
||||
// 4J Stu - Clear our reference to this socket
|
||||
pNetworkPlayer->SetSocket( NULL );
|
||||
delete socket;
|
||||
}
|
||||
|
||||
return g_pPlatformNetworkManager->RemoveLocalPlayer( pNetworkPlayer );
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::RemoveLocalPlayer( INetworkPlayer *pNetworkPlayer )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
CPlatformNetworkManagerDurango::PlayerFlags::PlayerFlags(INetworkPlayer *pNetworkPlayer, unsigned int count)
|
||||
{
|
||||
// 4J Stu - Don't assert, just make it a multiple of 8! This count is calculated from a load of separate values,
|
||||
// and makes tweaking world/render sizes a pain if we hit an assert here
|
||||
count = (count + 8 - 1) & ~(8 - 1);
|
||||
//assert( ( count % 8 ) == 0 );
|
||||
this->m_pNetworkPlayer = pNetworkPlayer;
|
||||
this->flags = new unsigned char [ count / 8 ];
|
||||
memset( this->flags, 0, count / 8 );
|
||||
this->count = count;
|
||||
}
|
||||
|
||||
CPlatformNetworkManagerDurango::PlayerFlags::~PlayerFlags()
|
||||
{
|
||||
delete [] flags;
|
||||
}
|
||||
|
||||
// Add a player to the per system flag storage - if we've already got a player from that system, copy its flags over
|
||||
void CPlatformNetworkManagerDurango::SystemFlagAddPlayer(INetworkPlayer *pNetworkPlayer)
|
||||
{
|
||||
PlayerFlags *newPlayerFlags = new PlayerFlags( pNetworkPlayer, m_flagIndexSize);
|
||||
// If any of our existing players are on the same system, then copy over flags from that one
|
||||
for( unsigned int i = 0; i < m_playerFlags.size(); i++ )
|
||||
{
|
||||
if( pNetworkPlayer->IsSameSystem(m_playerFlags[i]->m_pNetworkPlayer) )
|
||||
{
|
||||
memcpy( newPlayerFlags->flags, m_playerFlags[i]->flags, m_playerFlags[i]->count / 8 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_playerFlags.push_back(newPlayerFlags);
|
||||
}
|
||||
|
||||
// Remove a player from the per system flag storage - just maintains the m_playerFlags vector without any gaps in it
|
||||
void CPlatformNetworkManagerDurango::SystemFlagRemovePlayer(INetworkPlayer *pNetworkPlayer)
|
||||
{
|
||||
for( unsigned int i = 0; i < m_playerFlags.size(); i++ )
|
||||
{
|
||||
if( m_playerFlags[i]->m_pNetworkPlayer == pNetworkPlayer )
|
||||
{
|
||||
delete m_playerFlags[i];
|
||||
m_playerFlags[i] = m_playerFlags.back();
|
||||
m_playerFlags.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::SystemFlagReset()
|
||||
{
|
||||
for( unsigned int i = 0; i < m_playerFlags.size(); i++ )
|
||||
{
|
||||
delete m_playerFlags[i];
|
||||
}
|
||||
m_playerFlags.clear();
|
||||
}
|
||||
|
||||
// Set a per system flag - this is done by setting the flag on every player that shares that system
|
||||
void CPlatformNetworkManagerDurango::SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index)
|
||||
{
|
||||
if( ( index < 0 ) || ( index >= m_flagIndexSize ) ) return;
|
||||
if( pNetworkPlayer == NULL ) return;
|
||||
|
||||
for( unsigned int i = 0; i < m_playerFlags.size(); i++ )
|
||||
{
|
||||
if( pNetworkPlayer->IsSameSystem(m_playerFlags[i]->m_pNetworkPlayer) )
|
||||
{
|
||||
m_playerFlags[i]->flags[ index / 8 ] |= ( 128 >> ( index % 8 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get value of a per system flag - can be read from the flags of the passed in player as anything else sent to that
|
||||
// system should also have been duplicated here
|
||||
bool CPlatformNetworkManagerDurango::SystemFlagGet(INetworkPlayer *pNetworkPlayer, int index)
|
||||
{
|
||||
if( ( index < 0 ) || ( index >= m_flagIndexSize ) ) return false;
|
||||
if( pNetworkPlayer == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for( unsigned int i = 0; i < m_playerFlags.size(); i++ )
|
||||
{
|
||||
if( m_playerFlags[i]->m_pNetworkPlayer == pNetworkPlayer )
|
||||
{
|
||||
return ( ( m_playerFlags[i]->flags[ index / 8 ] & ( 128 >> ( index % 8 ) ) ) != 0 );
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
wstring CPlatformNetworkManagerDurango::GatherStats()
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
|
||||
wstring CPlatformNetworkManagerDurango::GatherRTTStats()
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::TickSearch()
|
||||
{
|
||||
if( m_bSearchPending )
|
||||
{
|
||||
if( !m_pDQRNet->FriendPartyManagerIsBusy() )
|
||||
{
|
||||
m_searchResultsCount = m_pDQRNet->FriendPartyManagerGetCount();
|
||||
delete [] m_pSearchResults;
|
||||
m_pSearchResults = new DQRNetworkManager::SessionSearchResult[m_searchResultsCount];
|
||||
|
||||
for( int i = 0; i < m_searchResultsCount; i++ )
|
||||
{
|
||||
m_pDQRNet->FriendPartyManagerGetSessionInfo(i, &m_pSearchResults[i] );
|
||||
}
|
||||
m_bSearchPending = false;
|
||||
|
||||
if( m_SessionsUpdatedCallback != NULL ) m_SessionsUpdatedCallback(m_pSearchParam);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !m_pDQRNet->FriendPartyManagerIsBusy() )
|
||||
{
|
||||
// Don't start searches unless we have registered a callback
|
||||
if( m_SessionsUpdatedCallback != NULL && (m_lastSearchStartTime + MINECRAFT_DURANGO_PARTY_SEARCH_DELAY_MILLISECONDS) < GetTickCount() )
|
||||
{
|
||||
if( m_pDQRNet->FriendPartyManagerSearch() );
|
||||
{
|
||||
m_bSearchPending = true;
|
||||
m_lastSearchStartTime = GetTickCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vector<FriendSessionInfo *> *CPlatformNetworkManagerDurango::GetSessionList(int iPad, int localPlayers, bool partyOnly)
|
||||
{
|
||||
vector<FriendSessionInfo *> *filteredList = new vector<FriendSessionInfo *>();
|
||||
for( int i = 0; i < m_searchResultsCount; i++ )
|
||||
{
|
||||
GameSessionData *gameSessionData = (GameSessionData *)m_pSearchResults[i].m_extData;
|
||||
if( ( gameSessionData->netVersion == MINECRAFT_NET_VERSION ) &&
|
||||
( gameSessionData->isReadyToJoin ) )
|
||||
{
|
||||
FriendSessionInfo *session = new FriendSessionInfo();
|
||||
session->searchResult = m_pSearchResults[i];
|
||||
session->searchResult.m_extData = NULL; // We have another copy of the GameSessionData, so don't need to make another copy of this here
|
||||
session->displayLabelLength = session->searchResult.m_playerNames[0].size();
|
||||
session->displayLabel = new wchar_t[ session->displayLabelLength + 1 ];
|
||||
memcpy(&session->data, gameSessionData, sizeof(GameSessionData));
|
||||
wcscpy_s(session->displayLabel, session->displayLabelLength + 1, session->searchResult.m_playerNames[0].c_str() );
|
||||
filteredList->push_back(session);
|
||||
}
|
||||
}
|
||||
return filteredList;
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::GetGameSessionInfo(int iPad, SessionID sessionId, FriendSessionInfo *foundSessionInfo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::SetSessionsUpdatedCallback( void (*SessionsUpdatedCallback)(LPVOID pParam), LPVOID pSearchParam )
|
||||
{
|
||||
m_SessionsUpdatedCallback = SessionsUpdatedCallback; m_pSearchParam = pSearchParam;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam )
|
||||
{
|
||||
FriendSessionUpdatedFn(true, pParam);
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::ForceFriendsSessionRefresh()
|
||||
{
|
||||
app.DebugPrintf("Resetting friends session search data\n");
|
||||
m_lastSearchStartTime = 0;
|
||||
m_searchResultsCount = 0;
|
||||
delete [] m_pSearchResults;
|
||||
m_pSearchResults = NULL;
|
||||
}
|
||||
|
||||
INetworkPlayer *CPlatformNetworkManagerDurango::addNetworkPlayer(DQRNetworkPlayer *pDQRPlayer)
|
||||
{
|
||||
NetworkPlayerDurango *pNetworkPlayer = new NetworkPlayerDurango(pDQRPlayer);
|
||||
pDQRPlayer->SetCustomDataValue((ULONG_PTR)pNetworkPlayer);
|
||||
currentNetworkPlayers.push_back( pNetworkPlayer );
|
||||
return pNetworkPlayer;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::removeNetworkPlayer(DQRNetworkPlayer *pDQRPlayer)
|
||||
{
|
||||
INetworkPlayer *pNetworkPlayer = getNetworkPlayer(pDQRPlayer);
|
||||
for( AUTO_VAR(it, currentNetworkPlayers.begin()); it != currentNetworkPlayers.end(); it++ )
|
||||
{
|
||||
if( *it == pNetworkPlayer )
|
||||
{
|
||||
currentNetworkPlayers.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
INetworkPlayer *CPlatformNetworkManagerDurango::getNetworkPlayer(DQRNetworkPlayer *pDQRPlayer)
|
||||
{
|
||||
return pDQRPlayer ? (INetworkPlayer *)(pDQRPlayer->GetCustomDataValue()) : NULL;
|
||||
}
|
||||
|
||||
|
||||
INetworkPlayer *CPlatformNetworkManagerDurango::GetLocalPlayerByUserIndex(int userIndex )
|
||||
{
|
||||
return getNetworkPlayer(m_pDQRNet->GetLocalPlayerByUserIndex(userIndex));
|
||||
}
|
||||
|
||||
INetworkPlayer *CPlatformNetworkManagerDurango::GetPlayerByIndex(int playerIndex)
|
||||
{
|
||||
return getNetworkPlayer(m_pDQRNet->GetPlayerByIndex(playerIndex));
|
||||
}
|
||||
|
||||
INetworkPlayer * CPlatformNetworkManagerDurango::GetPlayerByXuid(PlayerUID xuid)
|
||||
{
|
||||
return getNetworkPlayer(m_pDQRNet->GetPlayerByXuid(xuid)) ;
|
||||
}
|
||||
|
||||
INetworkPlayer * CPlatformNetworkManagerDurango::GetPlayerBySmallId(unsigned char smallId)
|
||||
{
|
||||
return getNetworkPlayer(m_pDQRNet->GetPlayerBySmallId(smallId));
|
||||
}
|
||||
|
||||
wstring CPlatformNetworkManagerDurango::GetDisplayNameByGamertag(wstring gamertag)
|
||||
{
|
||||
return m_pDQRNet->GetDisplayNameByGamertag(gamertag);
|
||||
}
|
||||
|
||||
INetworkPlayer *CPlatformNetworkManagerDurango::GetHostPlayer()
|
||||
{
|
||||
return getNetworkPlayer(m_pDQRNet->GetHostPlayer());
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::IsHost()
|
||||
{
|
||||
return m_pDQRNet->IsHost() && !m_bHostChanged;
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::JoinGameFromInviteInfo( int userIndex, int userMask, const INVITE_INFO *pInviteInfo)
|
||||
{
|
||||
m_pDQRNet->m_currentUserMask = userMask;
|
||||
m_pDQRNet->JoinSessionFromInviteInfo(userMask);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::SetSessionTexturePackParentId( int id )
|
||||
{
|
||||
m_hostGameSessionData.texturePackParentId = id;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::SetSessionSubTexturePackId( int id )
|
||||
{
|
||||
m_hostGameSessionData.subTexturePackId = id;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerDurango::Notify(int ID, ULONG_PTR Param)
|
||||
{
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::IsInSession()
|
||||
{
|
||||
return m_pDQRNet->IsInSession();
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::IsInGameplay()
|
||||
{
|
||||
return m_pDQRNet->GetState() == DQRNetworkManager::DNM_STATE_PLAYING;
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::IsReadyToPlayOrIdle()
|
||||
{
|
||||
return m_pDQRNet->IsReadyToPlayOrIdle();
|
||||
}
|
||||
|
||||
bool CPlatformNetworkManagerDurango::IsSessionJoinable()
|
||||
{
|
||||
return m_hostGameSessionIsJoinable;
|
||||
}
|
||||
168
Minecraft.Client/Durango/Network/PlatformNetworkManagerDurango.h
Normal file
168
Minecraft.Client/Durango/Network/PlatformNetworkManagerDurango.h
Normal file
@@ -0,0 +1,168 @@
|
||||
#pragma once
|
||||
using namespace std;
|
||||
#include <vector>
|
||||
#include "..\..\..\Minecraft.World\C4JThread.h"
|
||||
#include "..\..\Common\Network\NetworkPlayerInterface.h"
|
||||
#include "..\..\Common\Network\PlatformNetworkManagerInterface.h"
|
||||
#include "..\..\Common\Network\SessionInfo.h"
|
||||
#include "DQRNetworkManager.h"
|
||||
|
||||
#define MINECRAFT_DURANGO_PARTY_SEARCH_DELAY_MILLISECONDS 30000
|
||||
|
||||
class CPlatformNetworkManagerDurango : public CPlatformNetworkManager, IDQRNetworkManagerListener
|
||||
{
|
||||
friend class CGameNetworkManager;
|
||||
public:
|
||||
virtual bool Initialise(CGameNetworkManager *pGameNetworkManager, int flagIndexSize);
|
||||
virtual void Terminate();
|
||||
virtual int GetJoiningReadyPercentage();
|
||||
virtual int CorrectErrorIDS(int IDS);
|
||||
|
||||
virtual void DoWork();
|
||||
virtual int GetPlayerCount();
|
||||
virtual int GetOnlinePlayerCount();
|
||||
virtual int GetLocalPlayerMask(int playerIndex);
|
||||
virtual bool AddLocalPlayerByUserIndex( int userIndex );
|
||||
virtual bool RemoveLocalPlayerByUserIndex( int userIndex );
|
||||
virtual INetworkPlayer *GetLocalPlayerByUserIndex( int userIndex );
|
||||
virtual INetworkPlayer *GetPlayerByIndex(int playerIndex);
|
||||
virtual INetworkPlayer * GetPlayerByXuid(PlayerUID xuid);
|
||||
virtual INetworkPlayer * GetPlayerBySmallId(unsigned char smallId);
|
||||
virtual wstring GetDisplayNameByGamertag(wstring gamertag);
|
||||
virtual bool ShouldMessageForFullSession();
|
||||
|
||||
virtual INetworkPlayer *GetHostPlayer();
|
||||
virtual bool IsHost();
|
||||
virtual bool JoinGameFromInviteInfo( int userIndex, int userMask, const INVITE_INFO *pInviteInfo);
|
||||
virtual bool LeaveGame(bool bMigrateHost);
|
||||
|
||||
virtual bool IsInSession();
|
||||
virtual bool IsInGameplay();
|
||||
virtual bool IsReadyToPlayOrIdle();
|
||||
virtual bool IsInStatsEnabledSession();
|
||||
virtual bool SessionHasSpace(unsigned int spaceRequired = 1);
|
||||
virtual void SendInviteGUI(int quadrant);
|
||||
virtual bool IsAddingPlayer();
|
||||
|
||||
virtual void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0);
|
||||
virtual int JoinGame(FriendSessionInfo *searchResult, int localUsersMask, int primaryUserIndex );
|
||||
virtual void CancelJoinGame();
|
||||
virtual bool SetLocalGame(bool isLocal);
|
||||
virtual bool IsLocalGame() { return m_bIsOfflineGame; }
|
||||
virtual void SetPrivateGame(bool isPrivate);
|
||||
virtual bool IsPrivateGame() { return m_bIsPrivateGame; }
|
||||
virtual bool IsLeavingGame() { return m_bLeavingGame; }
|
||||
virtual void ResetLeavingGame() { m_bLeavingGame = false; }
|
||||
|
||||
virtual void RegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam);
|
||||
virtual void UnRegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam);
|
||||
|
||||
virtual void HandleSignInChange();
|
||||
|
||||
virtual bool _RunNetworkGame();
|
||||
|
||||
private:
|
||||
bool isSystemPrimaryPlayer(DQRNetworkPlayer *pDQRPlayer);
|
||||
virtual bool _LeaveGame(bool bMigrateHost, bool bLeaveRoom);
|
||||
virtual void _HostGame(int dwUsersMask, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0);
|
||||
virtual bool _StartGame();
|
||||
|
||||
DQRNetworkManager * m_pDQRNet; // pointer to SQRNetworkManager interface
|
||||
|
||||
HANDLE m_notificationListener;
|
||||
|
||||
vector<DQRNetworkPlayer *> m_machineDQRPrimaryPlayers; // collection of players that we deem to be the main one for that system
|
||||
|
||||
bool m_bLeavingGame;
|
||||
bool m_bLeaveGameOnTick;
|
||||
bool m_migrateHostOnLeave;
|
||||
bool m_bHostChanged;
|
||||
|
||||
bool m_bIsOfflineGame;
|
||||
bool m_bIsPrivateGame;
|
||||
int m_flagIndexSize;
|
||||
|
||||
// This is only maintained by the host, and is not valid on client machines
|
||||
GameSessionData m_hostGameSessionData;
|
||||
bool m_hostGameSessionIsJoinable;
|
||||
CGameNetworkManager *m_pGameNetworkManager;
|
||||
public:
|
||||
virtual void UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving = NULL);
|
||||
|
||||
private:
|
||||
// TODO 4J Stu - Do we need to be able to have more than one of these?
|
||||
void (*playerChangedCallback[XUSER_MAX_COUNT])(void *callbackParam, INetworkPlayer *pPlayer, bool leaving);
|
||||
void *playerChangedCallbackParam[XUSER_MAX_COUNT];
|
||||
|
||||
static int RemovePlayerOnSocketClosedThreadProc( void* lpParam );
|
||||
virtual bool RemoveLocalPlayer( INetworkPlayer *pNetworkPlayer );
|
||||
|
||||
// Things for handling per-system flags
|
||||
class PlayerFlags
|
||||
{
|
||||
public:
|
||||
INetworkPlayer *m_pNetworkPlayer;
|
||||
unsigned char *flags;
|
||||
unsigned int count;
|
||||
PlayerFlags(INetworkPlayer *pNetworkPlayer, unsigned int count);
|
||||
~PlayerFlags();
|
||||
};
|
||||
vector<PlayerFlags *> m_playerFlags;
|
||||
void SystemFlagAddPlayer(INetworkPlayer *pNetworkPlayer);
|
||||
void SystemFlagRemovePlayer(INetworkPlayer *pNetworkPlayer);
|
||||
void SystemFlagReset();
|
||||
public:
|
||||
virtual void SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index);
|
||||
virtual bool SystemFlagGet(INetworkPlayer *pNetworkPlayer, int index);
|
||||
|
||||
// For telemetry
|
||||
private:
|
||||
float m_lastPlayerEventTimeStart;
|
||||
|
||||
public:
|
||||
wstring GatherStats();
|
||||
wstring GatherRTTStats();
|
||||
|
||||
private:
|
||||
vector<FriendSessionInfo *> friendsSessions[XUSER_MAX_COUNT];
|
||||
int m_searchResultsCount;
|
||||
int m_lastSearchStartTime;
|
||||
|
||||
// The results that will be filled in with the current search
|
||||
DQRNetworkManager::SessionSearchResult *m_pSearchResults;
|
||||
|
||||
int m_lastSearchPad;
|
||||
bool m_bSearchResultsReady;
|
||||
bool m_bSearchPending;
|
||||
LPVOID m_pSearchParam;
|
||||
void (*m_SessionsUpdatedCallback)(LPVOID pParam);
|
||||
|
||||
void TickSearch();
|
||||
|
||||
vector<INetworkPlayer *>currentNetworkPlayers;
|
||||
INetworkPlayer *addNetworkPlayer(DQRNetworkPlayer *pDQRPlayer);
|
||||
void removeNetworkPlayer(DQRNetworkPlayer *pDQRPlayer);
|
||||
static INetworkPlayer *getNetworkPlayer(DQRNetworkPlayer *pDQRPlayer);
|
||||
|
||||
virtual void SetSessionTexturePackParentId( int id );
|
||||
virtual void SetSessionSubTexturePackId( int id );
|
||||
virtual void Notify(int ID, ULONG_PTR Param);
|
||||
|
||||
public:
|
||||
virtual vector<FriendSessionInfo *> *GetSessionList(int iPad, int localPlayers, bool partyOnly);
|
||||
virtual bool GetGameSessionInfo(int iPad, SessionID sessionId,FriendSessionInfo *foundSession);
|
||||
virtual void SetSessionsUpdatedCallback( void (*SessionsUpdatedCallback)(LPVOID pParam), LPVOID pSearchParam );
|
||||
virtual void GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam );
|
||||
virtual void ForceFriendsSessionRefresh();
|
||||
|
||||
// ... and the new ones that have been converted to IDQRNetworkManagerListener
|
||||
virtual void HandleDataReceived(DQRNetworkPlayer *playerFrom, DQRNetworkPlayer *playerTo, unsigned char *data, unsigned int dataSize);
|
||||
virtual void HandlePlayerJoined(DQRNetworkPlayer *player);
|
||||
virtual void HandlePlayerLeaving(DQRNetworkPlayer *player);
|
||||
virtual void HandleStateChange(DQRNetworkManager::eDQRNetworkManagerState oldState, DQRNetworkManager::eDQRNetworkManagerState newState);
|
||||
// virtual void HandleResyncPlayerRequest(DQRNetworkPlayer **aPlayers);
|
||||
virtual void HandleAddLocalPlayerFailed(int idx, bool serverFull);
|
||||
virtual void HandleDisconnect(bool bLostRoomOnly);
|
||||
virtual void HandleInviteReceived(int playerIndex, DQRNetworkManager::SessionInfo *pInviteInfo);
|
||||
virtual bool IsSessionJoinable();
|
||||
};
|
||||
156
Minecraft.Client/Durango/Network/base64.cpp
Normal file
156
Minecraft.Client/Durango/Network/base64.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
base64.cpp and base64.h
|
||||
|
||||
Copyright (C) 2004-2008 Ren<65> Nyffenegger
|
||||
|
||||
This source code is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this source code must not be misrepresented; you must not
|
||||
claim that you wrote the original source code. If you use this source code
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original source code.
|
||||
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
|
||||
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "base64.h"
|
||||
#include <iostream>
|
||||
|
||||
static const std::wstring base64_chars =
|
||||
L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
L"abcdefghijklmnopqrstuvwxyz"
|
||||
L"0123456789+/";
|
||||
|
||||
|
||||
static inline bool is_base64(wchar_t c)
|
||||
{
|
||||
return (isalnum(c) || (c == L'+') || (c == L'/'));
|
||||
}
|
||||
|
||||
// 4J changed to use Platform::String
|
||||
Platform::String^ base64_encode(unsigned char* chars_to_encode, unsigned int in_len)
|
||||
{
|
||||
std::wstring ret;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
unsigned char char_array_3[3];
|
||||
unsigned char char_array_4[4];
|
||||
|
||||
while (in_len--)
|
||||
{
|
||||
char_array_3[i++] = *(chars_to_encode++);
|
||||
if (i == 3)
|
||||
{
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for(int ii = 0; (ii <4) ; ii++)
|
||||
{
|
||||
ret += base64_chars[char_array_4[ii]];
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
for(j = i; j < 3; j++)
|
||||
{
|
||||
char_array_3[j] = '\0';
|
||||
}
|
||||
|
||||
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
|
||||
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
|
||||
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
|
||||
char_array_4[3] = char_array_3[2] & 0x3f;
|
||||
|
||||
for (j = 0; (j < i + 1); j++)
|
||||
{
|
||||
ret += base64_chars[char_array_4[j]];
|
||||
}
|
||||
|
||||
while((i++ < 3))
|
||||
{
|
||||
ret += L'=';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ref new Platform::String(ret.c_str());
|
||||
|
||||
}
|
||||
|
||||
void base64_decode(Platform::String ^encoded_string, unsigned char *output, unsigned int out_len)
|
||||
{
|
||||
int in_len = encoded_string->Length();
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int in_ = 0;
|
||||
unsigned char char_array_4[4];
|
||||
unsigned char char_array_3[3];
|
||||
|
||||
unsigned char *pucOut = output;
|
||||
|
||||
while (in_len-- && ( encoded_string->Data()[in_] != L'=') && is_base64(encoded_string->Data()[in_]))
|
||||
{
|
||||
char_array_4[i++] = (unsigned char)(encoded_string->Data()[in_]);
|
||||
in_++;
|
||||
if (i ==4)
|
||||
{
|
||||
for (i = 0; i <4; i++)
|
||||
{
|
||||
char_array_4[i] = (unsigned char )base64_chars.find(char_array_4[i]);
|
||||
}
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (i = 0; (i < 3); i++)
|
||||
{
|
||||
*pucOut++ = char_array_3[i];
|
||||
if( ( pucOut - output ) >= out_len ) return;
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(i)
|
||||
{
|
||||
for (j = i; j <4; j++)
|
||||
{
|
||||
char_array_4[j] = 0;
|
||||
}
|
||||
|
||||
for (j = 0; j <4; j++)
|
||||
{
|
||||
char_array_4[j] = (unsigned char )base64_chars.find(char_array_4[j]);
|
||||
}
|
||||
|
||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
||||
|
||||
for (j = 0; (j < i - 1); j++)
|
||||
{
|
||||
*pucOut++ = char_array_3[j];
|
||||
if( ( pucOut - output ) >= out_len ) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Minecraft.Client/Durango/Network/base64.h
Normal file
6
Minecraft.Client/Durango/Network/base64.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
Platform::String^ base64_encode(unsigned char* chars_to_encode, unsigned int in_len);
|
||||
void base64_decode(Platform::String ^encoded_string, unsigned char *output, unsigned int out_len);
|
||||
Binary file not shown.
Reference in New Issue
Block a user