the game where you go into mines and start crafting! but for consoles (forked directly from smartcmd's github)
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}