1 /*---------------------------------------------------------------------------*
2 Project: Revolution MPDS simple demo
3 File: mpdssimple.c
4
5 Copyright 2007 Nintendo. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Log: mpdssimple.c,v $
14 Revision 1.12 2007/11/29 03:20:27 seiki_masashi
15 Added the const modifier.
16
17 Revision 1.11 2007/10/26 09:31:17 seiki_masashi
18 Cleaned up the code
19
20 Revision 1.10 2007/10/26 07:29:56 seiki_masashi
21 Cleaned up the code
22
23 Revision 1.9 2007/10/26 07:05:28 seiki_masashi
24 Revised the link-level display
25
26 Revision 1.6 2007/10/24 09:30:41 okubata_ryoma
27 Small fix
28
29 Revision 1.5 2007/10/22 09:50:11 seiki_masashi
30 Made the MPDSStep function into a synchronous function, and added the new function MPDSTryStep
31
32 Revision 1.1 2007/10/10 08:38:45 seiki_masashi
33 Added the MPDS library
34
35 $NoKeywords: $
36 *---------------------------------------------------------------------------*/
37
38 //
39 // This sample performs communications with the demos/wm/dataShare-1 demo in the NITRO-SDK.
40 //
41 // Only the minimum amount of processing to check the state of communications is being done, so if creating an actual application, refer to demos like mpdsmodel.
42 //
43 //
44
45 #include <string.h>
46 #include <revolution.h>
47 #include <revolution/mp.h>
48 #include <revolution/mpds.h>
49 #include <revolution/mem.h>
50
51 #include "rexdemo/demokpad.h"
52 #include "rexdemo/graphic.h"
53
54 #define USERHEAP_SIZE ( 65536 )
55
56 #define MY_MAX_NODES 8 // maximum number of child devices that can be connected (up to a maximum of 8 devices are guaranteed in Wii Sequential communications)
57 #define MY_DS_NODES 16 // the number of nodes, including parent devices, that perform DataSharing (matches the dataShare-1 demo in the NITRO-SDK)
58 // for a normal application (MY_MAX_NODES+1)
59
60 #define MY_DS_LENGTH 8 // the shared data size per device for DataSharing
61
62 // When DataSharing is performed, the required transmission size is the shared size per device times the number of devices, plus 4 bytes.
63 #define MY_PARENT_MAX_SIZE ( (MY_DS_LENGTH * MY_DS_NODES) + MPDS_HEADER_SIZE )
64
65 /*===========================================================================*/
66
67 static MEMHeapHandle sUserHeap;
68
69 static u16 sRecvBuf[MY_MAX_NODES+1][MY_DS_LENGTH/sizeof(u16)];
70 static BOOL sRecvFlag[MY_MAX_NODES+1];
71
72 static MPDSContext sDSContext;
73
74 /*===========================================================================*/
75
76 static const GXColor white = { 0xFF, 0xFF, 0xFF, };
77 static const GXColor yellow = { 0xFF, 0xFF, 0x00, };
78 static const GXColor gray = { 0x80, 0x80, 0x80, };
79 static const GXColor black = { 0x00, 0x00, 0x00, };
80 static const GXColor red = { 0xFF, 0x00, 0x18 };
81 static const GXColor green = { 0x00, 0xFF, 0x00 };
82
83 /*===========================================================================*/
84
85 static void DoMPCommunication( void );
86
87 static void* mpAlloc( u32 size );
88 static void mpFree( void* ptr );
89 static void PortCallbackFunction( s32 type, MPPortCallbackInfo* info );
90
91 /*===========================================================================*/
92
93 // Toggles whether to use doubleMode for DataSharing.
94 // Using doubleMode makes it possible to achieve synchronization of data at 60 fps, even when the MP frequency is set to once per frame.
95 //
96 // The dataShare-1 demo in NITRO-SDK does not use doubleMode by default.
97 // If trying out doubleMode, change from 2 to 1 in the dataShare-1 demo. PICTURE_FRAME_PER_GAME_FRAME:
98 //
99 //#define DATA_SHARING_DOUBLE_MODE 1
100
101 // The GGID is a unique ID for MP communications assigned to each application.
102 // You must receive a GGID assignment from Nintendo for an actual application.
103 // Here, the GGID is used that is identical to that of the dataShare-1 demo.
104 #define MY_GGID 0x003fff11
105
106 // MP communication settings
107 static MPConfig sMpConfig =
108 {
109 mpAlloc,
110 mpFree,
111
112 8, // threadPriority; set to a priority that is higher than that of the main thread by 4 or more
113
114 MP_MODE_PARENT, // mode
115
116 MY_GGID, // ggid
117 MP_TGID_AUTO, // tgid
118
119 1, // channel
120 // Because the dataShare-1 demo of NITRO-SDK is fixed to ch1, the parent device is also fixed to ch1
121 //
122 // Normally applications should use automatic settings by specifying MP_CHANNEL_AUTO
123
124 MP_LIFETIME_DEFAULT,// lifeTime; usually equal to MP_LIFETIME_DEFAULT
125
126 MP_BEACON_PERIOD_AUTO, // beaconPeriod; normally MP_BEACON_PERIOD_AUTO
127 MY_MAX_NODES, // maxNodes
128 MY_PARENT_MAX_SIZE, // parentMaxSize
129 MY_DS_LENGTH, // childMaxSize
130 TRUE, // entryFlag
131 FALSE, // multiBootFlag; usually FALSE
132
133 1, // frequency
134
135 0, // userGameInfoLength
136 { 0, }, // userGameInfo
137
138 NULL, // indicationCallbackFunction
139
140 /// ... // port configuration (can be set up using MPSetPortConfig)
141 };
142
143 // DataSharing configuration
144 static MPDSConfig sMpdsConfig =
145 {
146 MY_DS_LENGTH, // dataLength
147
148 12, // port (must use one of sequential communications ports 12-15)
149 MP_PRIORITY_HIGH, // priority
150
151 TRUE, // isParent; always TRUE
152 (1 << MY_DS_NODES)-1, // aidBits (a binary number that is a row of 1's. The number of 1's is equal to MY_DS_NODES)
153 #ifdef DATA_SHARING_DOUBLE_MODE
154 TRUE, // isDoubleMode
155 #else
156 FALSE, // isDoubleMode
157 #endif
158 TRUE, // isAutoStart; always TRUE
159 NULL // mpdsCallback
160 };
161
main(void)162 void main(void)
163 {
164 /* Initialize OS and memory heap */
165 OSReport("test program started.\n");
166 REXDEMOKPadInit();
167 REXDEMOInitScreen( FALSE );
168 REXDEMOSetGroundColor( black );
169 REXDEMOSetFontSize( 10, 20 );
170 REXDEMOBeginRender();
171 REXDEMOWaitRetrace();
172
173 /* Initialize heap for MP library */
174 {
175 void* heapAddress;
176
177 heapAddress = OSGetMEM2ArenaLo();
178 OSSetMEM2ArenaLo( (void*)OSRoundUp32B( (u32)heapAddress + USERHEAP_SIZE ) );
179 sUserHeap = MEMCreateExpHeapEx( heapAddress, USERHEAP_SIZE, MEM_HEAP_OPT_THREAD_SAFE );
180 if( sUserHeap == NULL )
181 {
182 OSHalt( "Could not create heap.\n" );
183 }
184 }
185
186 (void)PADInit();
187
188 OSReport("push <A> button to restart.\n");
189 while (TRUE)
190 {
191 DoMPCommunication();
192 }
193 }
194
DoMPCommunication(void)195 void DoMPCommunication( void )
196 {
197 s32 result;
198
199 result = MPDSInit( &sDSContext, &sMpdsConfig );
200 if ( result < 0 )
201 {
202 OSReport( "MPDSInit returns %08x\n", result );
203 OSHalt("** panic **");
204 }
205
206 result = MPDSSetupPortConfig( &sDSContext, &sMpConfig );
207 if ( result < 0 )
208 {
209 OSReport( "MPDSSetupPortConfig returns %08x\n", result );
210 OSHalt("** panic **");
211 }
212
213 OSReport("MPStartup()\n");
214 result = MPStartup( &sMpConfig );
215 if( result != MP_RESULT_OK )
216 {
217 OSReport( "MPStartup returns %08x\n", result );
218 OSHalt( "** panic **\n" );
219 }
220
221 {
222 static u16 frameData[MY_DS_LENGTH/sizeof(u16)];
223 u32 counter1 = 0;
224 u32 counter2 = 0;
225
226 #ifndef DATA_SHARING_DOUBLE_MODE
227 // If performing DataSharing without using doubleMode when the MP frequency set to once per frame, updates to the game state can only be made at 30 fps at maximum.
228 //
229 // In this case, a flag will be required for determining whether the current frame coincides with the timing of a game state update.
230 //
231 BOOL updateFlag = TRUE;
232 #endif
233
234 for( counter1 = 0; TRUE; counter1++ )
235 {
236 s32 i;
237 MPDSDataSet recvDataSet;
238
239 REXDEMOKPadRead();
240
241 if( REXDEMOGetAnyMixedPadTrigger() & (KPAD_BUTTON_A | (PAD_BUTTON_A << 16)) )
242 {
243 break;
244 }
245
246 #ifndef DATA_SHARING_DOUBLE_MODE
247 // Update the game state at a frequency of once every two frames
248 if ( updateFlag )
249 #else
250 // When using doubleMode, it is possible to update the game state every frame
251 #endif
252 {
253 // Note that the Wii and DS have a different endianness.
254 // The data order is being made to match the dataShare-1 demo in NITRO-SDK
255 frameData[0] = MPHToMP16((u16)(counter1 >> 16));
256 frameData[1] = MPHToMP16((u16)(counter1 & 0xffff));
257 frameData[2] = MPHToMP16((u16)(counter2 >> 16));
258 frameData[3] = MPHToMP16((u16)(counter2 & 0xffff));
259
260 // Increment counter2 to match the dataShare-1 demo in NITRO-SDK
261 counter2++;
262
263 for ( i = 0; i < MY_MAX_NODES+1; i++ )
264 {
265 sRecvFlag[i] = FALSE;
266 }
267
268 // Share data using DataSharing
269 result = MPDSTryStep( &sDSContext, frameData, &recvDataSet );
270
271 if( result == MP_RESULT_OK )
272 {
273 // Successfully received shared data
274 const u8* data;
275
276 // Parse the contents of the shared data
277 for ( i = 0; i < MY_MAX_NODES+1; i++ )
278 {
279 data = MPDSGetData( &sDSContext, &recvDataSet, (u32)i );
280 if ( data != NULL )
281 {
282 // Was able to share data from the i-th sharing partner
283 (void)NETMemCpy( sRecvBuf[i], data, MY_DS_LENGTH );
284 sRecvFlag[i] = TRUE;
285 }
286 else
287 {
288 // There are a number of cases in which NULL is returned.
289 // 1. Empty data is read immediately after DataSharing started
290 // 2. The child device with the corresponding AID is not connected
291 // (3. Some kind of internal error has occurred)
292
293 // You can use this information to check if a child device is connected or disconnected.
294 // However, in states in which children can connect freely, it is not possible to detect when a child disconnects and a separate child makes a connection immediately afterward.
295 //
296 //
297 // Immediately after the start of the game, use the MPSetEntryFlag and MPUpdateBeacon functions to cut off new child connections.
298 //
299 }
300 }
301
302 #ifndef DATA_SHARING_DOUBLE_MODE
303 // The game state was updated successfully, so do not update at the next frame
304 //
305 updateFlag = FALSE;
306 #endif
307
308 //
309 //
310 // Data could have been shared between the child and the parent, so update the in-game state.
311 // The logic for using the shared data to update the in-game state is placed here.
312 //
313 // If using DataSharing, you can completely synchronize the parent and children by updating the in-game state using only the data that could be obtained from the MPDS[Try]Step function.
314 //
315 // If the local data for the current pad input, etc. is used directly, the synchronization will be lost, so be sure to pass even your own data to the MPDS[Try]Step function once.
316 //
317 //
318 //
319
320 }
321 else if ( result == MP_RESULT_NO_DATA )
322 {
323 // This is an error that occurs normally if the data was not complete due to the communication status being wrong.
324 //
325
326 #ifndef DATA_SHARING_DOUBLE_MODE
327 // Try updating again at the next frame
328 updateFlag = TRUE;
329 #endif
330
331 // Decrement counter1 in order to match the dataShare-1 demo in NITRO-SDK
332 counter1--;
333
334 //
335 // Data sharing could not be achieved between the parent and children, so wait for the next frame without updating the in-game state.
336 //
337 //
338 // How long this state will continue depends on the participants in the communications, so you must not carry out any operations that change the program's internal state, such as updating the coordinate data using the velocity data, or reading in random values.
339 //
340 //
341 //
342 //
343 }
344 else
345 {
346 // error
347 OSReport("MPDSTryStep failed: %08x\n", result);
348 break;
349 }
350 }
351 #ifndef DATA_SHARING_DOUBLE_MODE
352 else
353 {
354 // An update did not occur at this frame, so update at the next frame
355 updateFlag = TRUE;
356 }
357 #endif
358
359 REXDEMOBeginRender();
360
361 REXDEMOSetTextColor(green);
362 REXDEMOPrintf(40, 8, 0, "Parent mode");
363 REXDEMOSetTextColor(white);
364 REXDEMOPrintf(4, 400, 0, "push <A> button to restart.");
365
366 REXDEMOSetTextColor(green);
367 {
368 s32 linkLevel = MPGetLinkLevel();
369 REXDEMOPrintf(480, 400, 0, "Link Level: %d", (linkLevel >= 0) ? linkLevel : 0);
370 }
371
372 REXDEMOSetTextColor(yellow);
373 REXDEMOPrintf(4, 30, 0, "Send: %08X-%08X", counter1, counter2);
374 REXDEMOPrintf(4, 52, 0, "Receive:");
375 {
376 s32 i;
377
378 for (i = 0; i < MY_MAX_NODES+1; i++)
379 {
380 if (sRecvFlag[i])
381 {
382 REXDEMOSetTextColor(yellow);
383 REXDEMOPrintf(40, (s16)(80 + i * 20), 0,
384 "AID(%02d): %04X%04X-%04X%04X",
385 i,
386 MPMPToH16(sRecvBuf[i][0]), MPMPToH16(sRecvBuf[i][1]),
387 MPMPToH16(sRecvBuf[i][2]), MPMPToH16(sRecvBuf[i][3]) );
388 }
389 else
390 {
391 REXDEMOSetTextColor(red);
392 REXDEMOPrintf(40, (s16)(80 + i * 20), 0, "No Data");
393 }
394 }
395 }
396
397 REXDEMOSetTextColor(white);
398 REXDEMOWaitRetrace();
399 }
400 }
401
402 OSReport("MPCleanup()\n");
403 (void)MPCleanup();
404 }
405
406 /*===========================================================================*/
407
mpAlloc(u32 size)408 void* mpAlloc( u32 size )
409 {
410 return MEMAllocFromExpHeapEx( sUserHeap, size, 32 );
411 }
412
mpFree(void * ptr)413 void mpFree( void* ptr )
414 {
415 MEMFreeToExpHeap( sUserHeap, ptr );
416 }
417