the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
at main 409 lines 18 kB view raw
1#include "stdafx.h" 2 3#include "DQRNetworkManager.h" 4#include "PartyController.h" 5#include <collection.h> 6#include <ppltasks.h> 7#include <ws2tcpip.h> 8#include "..\Minecraft.World\StringHelpers.h" 9#include "base64.h" 10 11#ifdef _DURANGO 12#include "..\Minecraft.World\DurangoStats.h" 13#endif 14 15#include "ChatIntegrationLayer.h" 16 17using namespace Concurrency; 18using namespace Windows::Foundation::Collections; 19 20// 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 21// that it was received for. 22void DQRNetworkManager::BytesReceived(int smallId, BYTE *bytes, int byteCount) 23{ 24 DQRNetworkPlayer *host = GetPlayerBySmallId(m_hostSmallId); 25 DQRNetworkPlayer *client = GetPlayerBySmallId(smallId); 26 27 if( ( host == NULL ) || ( client == NULL ) ) 28 { 29 return; 30 } 31 32 if( m_isHosting ) 33 { 34 m_listener->HandleDataReceived(client, host, bytes, byteCount ); 35 } 36 else 37 { 38 m_listener->HandleDataReceived(host, client, bytes, byteCount ); 39 } 40// app.DebugPrintf("%d bytes received: %s\n", byteCount, bytes); 41} 42 43// This method is called when network data is received, that is to be processed by the DQRNetworkManager itself. This is for handling internal 44// updates such as assigning & unassigning of small Ids, transmission of the table of players currently in the session etc. 45// 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 46// the underlying communcation layer split data up somehow. 47void DQRNetworkManager::BytesReceivedInternal(DQRConnectionInfo *connectionInfo, unsigned int sessionAddress, BYTE *bytes, int byteCount) 48{ 49 BYTE *pNextByte = bytes; 50 BYTE *pEndByte = pNextByte + byteCount; 51 52 do 53 { 54 BYTE byte = *pNextByte; 55 switch( connectionInfo->m_internalDataState ) 56 { 57 case DQRConnectionInfo::ConnectionState_InternalHeaderByte: 58 switch( byte ) 59 { 60 case DQR_INTERNAL_ASSIGN_SMALL_IDS: 61 connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallIdMask; 62 break; 63 case DQR_INTERNAL_UNASSIGN_SMALL_ID: 64 // Host only 65 if( connectionInfo->m_channelActive[connectionInfo->m_currentChannel] ) 66 { 67 int smallId = connectionInfo->m_smallId[connectionInfo->m_currentChannel]; 68 connectionInfo->m_channelActive[connectionInfo->m_currentChannel] = false; 69 m_sessionAddressFromSmallId[smallId] = 0; 70 DQRNetworkPlayer *pPlayer = GetPlayerBySmallId(smallId); 71 if( pPlayer ) 72 { 73 RemoveRoomSyncPlayer(pPlayer); 74 SendRoomSyncInfo(); 75 } 76 } 77 break; 78 case DQR_INTERNAL_PLAYER_TABLE: 79 connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalRoomSyncData; 80 connectionInfo->m_pucRoomSyncData = new unsigned char[4]; 81 connectionInfo->m_roomSyncDataBytesToRead = 0; 82 connectionInfo->m_roomSyncDataBytesRead = 0; 83 break; 84 case DQR_INTERNAL_ADD_PLAYER_FAILED: 85 connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAddPlayerFailedData; 86 connectionInfo->m_pucAddFailedPlayerData = new unsigned char[4]; 87 connectionInfo->m_addFailedPlayerDataBytesToRead = 0; 88 connectionInfo->m_addFailedPlayerDataBytesRead = 0; 89 break; 90 default: 91 break; 92 } 93 pNextByte++; 94 break; 95 case DQRConnectionInfo::ConnectionState_InternalAssignSmallIdMask: 96 // 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. 97 // The small Ids themselves follow, always 4 bytes, and any that are masked as being assigned are processed, the other bytes ignored. 98 // In order work around a bug with the networking library, this particular packet (being the first this that is sent from a client) 99 // 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 100 // to handle (and ignore) this being received more than once 101 DQRNetworkManager::LogCommentFormat(L"Small Ids being received"); 102 connectionInfo->m_smallIdReadMask = byte; 103 // 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 104 connectionInfo->m_pucsmallIdReadMaskResolved = new unsigned char; 105 *connectionInfo->m_pucsmallIdReadMaskResolved = 0; 106 connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallId0; 107 connectionInfo->m_initialPacketReceived = true; 108 109 pNextByte++; 110 break; 111 case DQRConnectionInfo::ConnectionState_InternalAssignSmallId0: 112 case DQRConnectionInfo::ConnectionState_InternalAssignSmallId1: 113 case DQRConnectionInfo::ConnectionState_InternalAssignSmallId2: 114 case DQRConnectionInfo::ConnectionState_InternalAssignSmallId3: 115 { 116 int channel = ((int)connectionInfo->m_internalDataState) - DQRConnectionInfo::ConnectionState_InternalAssignSmallId0; 117 118 if( ( connectionInfo->m_smallIdReadMask & ( 1 << channel ) ) && ( !connectionInfo->m_channelActive[channel] ) ) 119 { 120 // HOST ONLY 121 // Store the small Id that is associated with this send channel. In order work around a bug with the networking library, this particular packet 122 // (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. 123 // We therefore have to be able to handle (and ignore) this being received more than once - hence the check of the bool above. 124 // At this point, the connection is considered properly active from the point of view of the host. 125 126 int sessionIndex = GetSessionIndexForSmallId(byte); 127 if( sessionIndex != -1 ) 128 { 129 connectionInfo->m_channelActive[channel] = true; 130 connectionInfo->m_smallId[channel] = byte; 131 132 m_sessionAddressFromSmallId[byte] = sessionAddress; 133 m_channelFromSmallId[byte] = channel; 134 135 auto pAsyncOp = m_primaryUserXboxLiveContext->ProfileService->GetUserProfileAsync(m_multiplayerSession->Members->GetAt(sessionIndex)->XboxUserId); 136 DQRNetworkManager::LogCommentFormat(L"Session index of %d found for player with small id %d - attempting to resolve display name\n",sessionIndex,byte); 137 138 DQRNetworkPlayer *pPlayer = new DQRNetworkPlayer(this, DQRNetworkPlayer::DNP_TYPE_REMOTE, true, 0, sessionAddress); 139 pPlayer->SetSmallId(byte); 140 pPlayer->SetUID(PlayerUID(m_multiplayerSession->Members->GetAt(sessionIndex)->XboxUserId->Data())); 141 142 HostGamertagResolveDetails *resolveDetails = new HostGamertagResolveDetails(); 143 resolveDetails->m_pPlayer = pPlayer; 144 resolveDetails->m_sessionAddress = sessionAddress; 145 resolveDetails->m_channel = channel; 146 resolveDetails->m_sync = false; 147 148 int mask = 1 << channel; 149 unsigned char *pucsmallIdReadMaskResolved = connectionInfo->m_pucsmallIdReadMaskResolved; 150 unsigned char ucsmallIdReadMask = connectionInfo->m_smallIdReadMask; 151 create_task( pAsyncOp ).then( [this,resolveDetails,mask,pucsmallIdReadMaskResolved,ucsmallIdReadMask] (task<Microsoft::Xbox::Services::Social::XboxUserProfile^> resultTask) 152 { 153 try 154 { 155 Microsoft::Xbox::Services::Social::XboxUserProfile^ result = resultTask.get(); 156 157 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 158 159 EnterCriticalSection(&m_csHostGamertagResolveResults); 160 // 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 161 *pucsmallIdReadMaskResolved |= mask; 162 LogCommentFormat(L"<<>> Compare %d to %d",*pucsmallIdReadMaskResolved,ucsmallIdReadMask); 163 if(ucsmallIdReadMask == *pucsmallIdReadMaskResolved) 164 { 165 resolveDetails->m_sync = true; 166 delete pucsmallIdReadMaskResolved; 167 } 168 m_hostGamertagResolveResults.push(resolveDetails); 169 LeaveCriticalSection(&m_csHostGamertagResolveResults); 170 } 171 172 catch (Platform::Exception^ ex) 173 { 174 LogComment("Name resolve exception raised"); 175 // TODO - handle errors more usefully than just not setting the name... 176 EnterCriticalSection(&m_csHostGamertagResolveResults); 177 // 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 178 *pucsmallIdReadMaskResolved |= mask; 179 if(ucsmallIdReadMask == *pucsmallIdReadMaskResolved) 180 { 181 resolveDetails->m_sync = true; 182 delete pucsmallIdReadMaskResolved; 183 } 184 m_hostGamertagResolveResults.push(resolveDetails); 185 LeaveCriticalSection(&m_csHostGamertagResolveResults); 186 } 187 }); 188 } 189 } 190 } 191 192 switch(connectionInfo->m_internalDataState) 193 { 194 case DQRConnectionInfo::ConnectionState_InternalAssignSmallId0: 195 connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallId1; 196 break; 197 case DQRConnectionInfo::ConnectionState_InternalAssignSmallId1: 198 connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallId2; 199 break; 200 case DQRConnectionInfo::ConnectionState_InternalAssignSmallId2: 201 connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalAssignSmallId3; 202 break; 203 case DQRConnectionInfo::ConnectionState_InternalAssignSmallId3: 204 connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalHeaderByte; 205 break; 206 } 207 208 pNextByte++; 209 break; 210 case DQRConnectionInfo::ConnectionState_InternalRoomSyncData: 211 connectionInfo->m_pucRoomSyncData[connectionInfo->m_roomSyncDataBytesRead++] = byte; 212 // The room sync info is sent as a 4 byte count of the length of XUID strings, then the RoomSyncData, then the XUID strings 213 if( connectionInfo->m_roomSyncDataBytesToRead == 0 ) 214 { 215 // At first stage of reading the 4 byte count 216 if( connectionInfo->m_roomSyncDataBytesRead == 4 ) 217 { 218 memcpy( &connectionInfo->m_roomSyncDataBytesToRead, connectionInfo->m_pucRoomSyncData, 4); 219 delete [] connectionInfo->m_pucRoomSyncData; 220 connectionInfo->m_roomSyncDataBytesToRead += sizeof(RoomSyncData); 221 connectionInfo->m_pucRoomSyncData = new unsigned char[ connectionInfo->m_roomSyncDataBytesToRead ]; 222 connectionInfo->m_roomSyncDataBytesRead = 0; 223 } 224 } 225 else if( connectionInfo->m_roomSyncDataBytesRead == connectionInfo->m_roomSyncDataBytesToRead ) 226 { 227 // 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 228 RoomSyncData *roomSyncData = (RoomSyncData *)connectionInfo->m_pucRoomSyncData; 229 wchar_t *pwcsData = (wchar_t *)((unsigned char *)connectionInfo->m_pucRoomSyncData + sizeof(RoomSyncData)); 230 for( int i = 0; i < roomSyncData->playerCount; i++ ) 231 { 232 unsigned int thisWchars = ( wcslen(pwcsData) + 1 ); 233 roomSyncData->players[i].m_XUID = new wchar_t[thisWchars]; 234 wcsncpy(roomSyncData->players[i].m_XUID, pwcsData, thisWchars); 235 pwcsData += thisWchars; 236 } 237 // Update the room sync data with this new data. This will handle notification of new and removed players 238 UpdateRoomSyncPlayers((RoomSyncData *)connectionInfo->m_pucRoomSyncData); 239 240 delete connectionInfo->m_pucRoomSyncData; 241 connectionInfo->m_pucRoomSyncData = NULL; 242 connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalHeaderByte; 243 244 // If we haven't actually established a connection yet for this channel, then this is the point where we can consider this active 245 if( !connectionInfo->m_channelActive[connectionInfo->m_currentChannel] ) 246 { 247 DQRNetworkManager::LogCommentFormat(L"Received data from host, channel %d considered active (%d bytes)\n",connectionInfo->m_currentChannel,connectionInfo->m_bytesRemaining); 248 connectionInfo->m_channelActive[connectionInfo->m_currentChannel] = true; 249 250 // 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 251 if(connectionInfo->m_currentChannel == 0) 252 { 253 if( m_chat ) 254 { 255 m_chat->OnNewSessionAddressAdded(m_hostSessionAddress); 256 } 257 } 258 } 259 260 // 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. 261 // 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 262 // that we're ready to go just yet. 263 if( m_state == DQRNetworkManager::DNM_INT_STATE_JOINING_SENDING_UNRELIABLE ) 264 { 265 bool allLocalPlayersHere = true; 266 for( int i = 0; i < MAX_LOCAL_PLAYERS; i++ ) 267 { 268 if( m_currentUserMask & ( 1 << i ) ) 269 { 270 if( GetLocalPlayerByUserIndex(i) == NULL ) 271 { 272 allLocalPlayersHere = false; 273 } 274 } 275 } 276 if( allLocalPlayersHere ) 277 { 278 DQRNetworkManager::LogComment(L"All local players present"); 279 SetState(DQRNetworkManager::DNM_INT_STATE_STARTING); 280 SetState(DQRNetworkManager::DNM_INT_STATE_PLAYING); 281 } 282 else 283 { 284 // Our players aren't all here yet. Going to keep on sending unreliable packets though until the connection is up and running 285 DQRNetworkManager::LogComment(L"All local players not yet present"); 286 } 287 } 288 } 289 pNextByte++; 290 break; 291 case DQRConnectionInfo::ConnectionState_InternalAddPlayerFailedData: 292 connectionInfo->m_pucAddFailedPlayerData[connectionInfo->m_addFailedPlayerDataBytesRead++] = byte; 293 // The failed player info is sent as a 4 byte count of the length of XUID string, then the string itself 294 if( connectionInfo->m_addFailedPlayerDataBytesToRead == 0 ) 295 { 296 // At first stage of reading the 4 byte count 297 if( connectionInfo->m_addFailedPlayerDataBytesRead == 4 ) 298 { 299 memcpy( &connectionInfo->m_addFailedPlayerDataBytesToRead, connectionInfo->m_pucAddFailedPlayerData, 4); 300 delete [] connectionInfo->m_pucAddFailedPlayerData; 301 connectionInfo->m_pucAddFailedPlayerData = new unsigned char[ connectionInfo->m_addFailedPlayerDataBytesToRead ]; 302 connectionInfo->m_addFailedPlayerDataBytesRead = 0; 303 } 304 } 305 else if( connectionInfo->m_addFailedPlayerDataBytesRead == connectionInfo->m_addFailedPlayerDataBytesToRead ) 306 { 307 // XUID fully read, can now handle what to do with it 308 AddPlayerFailed(ref new Platform::String( (wchar_t *)connectionInfo->m_pucAddFailedPlayerData ) ); 309 delete [] connectionInfo->m_pucAddFailedPlayerData; 310 connectionInfo->m_pucAddFailedPlayerData = NULL; 311 connectionInfo->m_internalDataState = DQRConnectionInfo::ConnectionState_InternalHeaderByte; 312 } 313 314 pNextByte++; 315 break; 316 } 317 } while (pNextByte != pEndByte); 318} 319 320// This method directly sends bytes via the network communication layer, used to send both game data & data internal to the DQRNetworkManager itself. 321// This is used by higher level sending methods that wrap communications up with headers that can be processed at the receiving end. 322void DQRNetworkManager::SendBytesRaw(int smallId, BYTE *bytes, int byteCount, bool reliableAndSequential) 323{ 324 bool broadcast; 325 unsigned int sessionAddress; 326 327// app.DebugPrintf("{%d,%d - %d}\n",smallId,reliableAndSequential,byteCount); 328 329 if( smallId == -1 ) 330 { 331 LogCommentFormat(L"Attempting broadcast, exception of address m_XRNS_Session->LocalSessionAddress %d %d %d", smallId, byteCount, reliableAndSequential); 332 // Broadcast, used from host only 333 broadcast = true; 334 sessionAddress = 0; 335 } 336 else 337 { 338 // Send to individual session address 339 broadcast = false; 340 if( m_isHosting ) 341 { 342 sessionAddress = m_sessionAddressFromSmallId[ smallId ]; 343 } 344 else 345 { 346 sessionAddress = m_hostSessionAddress; 347 } 348 } 349 RTS_SendData(bytes, byteCount, sessionAddress, reliableAndSequential, reliableAndSequential, reliableAndSequential, broadcast, true); 350} 351 352// This method is called by the chat integration layer to be able to send data 353void DQRNetworkManager::SendBytesChat(unsigned int address, BYTE *bytes, int byteCount, bool reliable, bool sequential, bool broadcast) 354{ 355 unsigned int sessionAddress; 356 357 if( broadcast ) 358 { 359 sessionAddress = 0; 360 } 361 else 362 { 363 // Send to individual session address 364 sessionAddress = address; 365 } 366 367 RTS_SendData(bytes, byteCount, sessionAddress, reliable, sequential, false, broadcast, false); 368} 369 370// 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. 371void DQRNetworkManager::SendBytes(int smallId, BYTE *bytes, int byteCount) 372{ 373 EnterCriticalSection(&m_csSendBytes); 374 unsigned char *tempSendBuffer = (unsigned char *)malloc(8191 + 2); 375 376 BYTE *data = bytes; 377 BYTE *dataEnd = bytes + byteCount; 378 379 // 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. 380 // 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 381 // blocks of this size. 382 do 383 { 384 int bytesToSend = (int)(dataEnd - data); 385 if( bytesToSend > 8191 ) bytesToSend = 8191; 386 387 // Send header with data sending mode - see full comment in DQRNetworkManagerEventHandlers::DataReceivedHandler 388 tempSendBuffer[0] = ( m_channelFromSmallId[smallId] << 5 ) | ( bytesToSend >> 8 ); 389 tempSendBuffer[1] = bytesToSend & 0xff; 390 memcpy(&tempSendBuffer[2], data, bytesToSend); 391 392 SendBytesRaw(smallId, tempSendBuffer, bytesToSend + 2, true); 393 394 data += bytesToSend; 395 } while (data != dataEnd); 396 397 free(tempSendBuffer); 398 LeaveCriticalSection(&m_csSendBytes); 399} 400 401int DQRNetworkManager::GetQueueSizeBytes() 402{ 403 return m_RTS_Stat_totalBytes; 404} 405 406int DQRNetworkManager::GetQueueSizeMessages() 407{ 408 return m_RTS_Stat_totalSends; 409}