1 /*---------------------------------------------------------------------------*
2 Project: Revolution MPDS demo
3 File: mpdsmodel.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: mpdsmodel.c,v $
14 Revision 1.19 2007/11/29 03:20:27 seiki_masashi
15 Added the const modifier.
16
17 Revision 1.18 2007/11/21 06:53:14 yosizaki
18 Reapplied the rolled-back content.
19
20 Revision 1.16 2007/10/27 11:20:46 seiki_masashi
21 Small fix
22
23 Revision 1.15 2007/10/26 12:15:00 yosizaki
24 Added ball display.
25
26 Revision 1.14 2007/10/26 09:31:17 seiki_masashi
27 Cleaned up the code
28
29 Revision 1.13 2007/10/26 07:29:56 seiki_masashi
30 Cleaned up the code
31
32 Revision 1.12 2007/10/26 07:05:28 seiki_masashi
33 Revised the link-level display
34
35 Revision 1.10 2007/10/25 03:20:53 seiki_masashi
36 Support for transformations that correspond to the ROM font encoding used by child device nicknames when they are displayed.
37
38 Revision 1.8 2007/10/24 13:50:49 seiki_masashi
39 Added a feature to display nicknames in the Lobby.
40
41 Revision 1.5 2007/10/24 01:38:51 seiki_masashi
42 Revised the method by which connection status is displayed.
43
44 Revision 1.3 2007/10/23 13:43:34 seiki_masashi
45 Revised so that pad input is sent.
46
47 Revision 1.2 2007/10/23 13:08:49 seiki_masashi
48 Changed to be multithreaded.
49
50 Revision 1.1 2007/10/23 00:05:57 seiki_masashi
51 Added the mpdsmodel demo.
52
53 $NoKeywords: $
54 *---------------------------------------------------------------------------*/
55
56 //
57 // This sample performs communications with the demos/wm/dataShare-Model demo in the NITRO-SDK.
58 //
59 // This is a demo that shows the sequence of accepting a new connection from a child at the lobby screen, and then starting a battle once the connection is closed.
60 //
61 //
62
63 #include <string.h>
64 #include <revolution.h>
65 #include <revolution/mp.h>
66 #include <revolution/mpds.h>
67 #include <revolution/mem.h>
68 #include <revolution/enc.h>
69
70 #include "rexdemo/demokpad.h"
71 #include "rexdemo/graphic.h"
72
73 #include "ball.h"
74
75 #define USERHEAP_SIZE ( 65536 )
76
77 #define MY_NICKNAME L"Wii" // the nickname to display in the parent device selection screen on the child device
78 #define MY_NICKNAME_LENGTH 3 // the number of UTF16BE characters in MY_NICKNAME (maximum of 10)
79
80 #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)
81 #define MY_DS_NODES 16 // the number of nodes, including parent devices, that perform DataSharing (matches the dataShare-Model demo in the NITRO-SDK)
82 // for a normal application (MY_MAX_NODES+1)
83
84 #define MY_DS_LENGTH 12 // the shared data size per device for DataSharing
85 #define MY_DS_PORT 13 // the port number to use for DataSharing (matches the dataShare-Model demo in the NITRO-SDK)
86
87 // When DataSharing is performed, the required transmission size is the shared size per device times the number of devices, plus 4 bytes.
88 #define MY_PARENT_MAX_SIZE ( (MY_DS_LENGTH * MY_DS_NODES) + MPDS_HEADER_SIZE )
89
90 #define STACK_SIZE 8192 // thread stack size; you must also consider the amount used by interrupt processing
91 #define THREAD_PRIORITY 14 // thread priority; specify a higher priority than the main thread (16)
92
93 /*===========================================================================*/
94
95 // application-specific UserGameInfo
96 // note that processing must be done in Little Endian to match the DS
97 #pragma pack(push,1)
98 typedef struct MyGameInfo
99 {
100 u8 nickName[10 * 2];
101 u8 nickNameLength;
102 u8 entryCount;
103 u16 periodicalCount;
104 }
105 MyGameInfo;
106 #pragma pack(pop)
107
108 // shared data structure
109 // note that processing must be done in Little Endian to match the DS
110 #pragma pack(push,1)
111 typedef struct ShareData
112 {
113 u8 data:3; // For graphing
114 u8 level:2; // Radio reception strength
115 u8 command:3; // Instruction (used for switching the game states at once, etc.)
116 u16 key; // Key data
117 u32 count:24; // Frame count
118 }
119 ShareData;
120 #pragma pack(pop)
121
122 enum
123 {
124 SHARECMD_NONE = 0, // Nothing specific (normal)
125 SHARECMD_EXITLOBBY, // Signal for ending the lobby screen
126 SHARECMD_NUM
127 };
128
129 enum
130 {
131 STATE_IDLE = 0,
132 STATE_GOING_TO_LOBBY,
133 STATE_LOBBY,
134 STATE_GOING_TO_COMMUNICATION,
135 STATE_COMMUNICATION,
136 STATE_GOING_TO_IDLE
137 };
138
139 enum
140 {
141 RESULT_OK = 0,
142 RESULT_ERROR = -1,
143 RESULT_QUIT = -2
144 };
145
146 /*===========================================================================*/
147
148 static const GXColor white = { 0xFF, 0xFF, 0xFF, };
149 static const GXColor yellow = { 0xFF, 0xFF, 0x00, };
150 static const GXColor gray = { 0x80, 0x80, 0x80, };
151 static const GXColor black = { 0x00, 0x00, 0x00, };
152 static const GXColor red = { 0xFF, 0x00, 0x18 };
153 static const GXColor green = { 0x00, 0xFF, 0x00 };
154
155 static MEMHeapHandle sUserHeap;
156
157 static ShareData sRecvData[MY_MAX_NODES+1];
158 static BOOL sRecvFlag[MY_MAX_NODES+1];
159 static BOOL sConnectedFlag[MY_MAX_NODES+1];
160 static u8 sConnectedMacAddress[MY_MAX_NODES+1][MP_SIZE_MACADDRESS];
161 static u16 sConnectedNickName[MY_MAX_NODES+1][10 + 1];
162
163 static u32 sFrameCount = 0;
164 static s32 sState = STATE_IDLE;
165 static OSThreadQueue sStateChangeThreadQueue;
166 static BOOL sQuitFlag = FALSE;
167
168 static OSMutex sGlobalMutex;
169
170 static OSThread sStateManagementThread;
171 static OSThread sDataSharingThread;
172 static OSThread sUpdateGameInfoThread;
173
174 static u8 sStateManagementThreadStack[STACK_SIZE] ATTRIBUTE_ALIGN(32);
175 static u8 sDataSharingThreadStack[STACK_SIZE] ATTRIBUTE_ALIGN(32);
176 static u8 sUpdateGameInfoThreadStack[STACK_SIZE] ATTRIBUTE_ALIGN(32);
177
178 static MPDSContext sDSContext;
179 static MyGameInfo sMyGameInfo;
180 static ENCContext sEncContext;
181
182 /*===========================================================================*/
183
184 static void* StateManagementThreadFunc( void* arg );
185 static void* DataSharingThreadFunc( void* arg );
186 static void* UpdateGameInfoThreadFunc( void* arg );
187
188 static void Initialize( void );
189 static void MainLoop( void );
190
191 static void Draw( void );
192 static void SetState( s32 state );
193 static inline s32 GetState( void );
194 static s32 WaitForStateChange( void );
195 static void QuitMP( void );
196 static BOOL IsQuitting( void );
197 static s32 TransToLobby( void );
198 static s32 TransToCommunication( void );
199 static s32 TransToIdle( void );
200 static s32 DoLobbyMode( void );
201 static s32 DoCommunicationMode( void );
202 static void DataSharingCallback( s32 type, MPPortCallbackInfo* info );
203
204 static void* mpAlloc( u32 size );
205 static void mpFree( void* ptr );
206
207 static u16 ConvKPadKeyToDSPad(u32 mixedKey);
208
EnterCriticalSection(void)209 static inline void EnterCriticalSection( void )
210 {
211 (void)OSLockMutex( &sGlobalMutex );
212 }
213
LeaveCriticalSection(void)214 static void LeaveCriticalSection( void )
215 {
216 (void)OSUnlockMutex( &sGlobalMutex );
217 }
218
219 /*===========================================================================*/
220
221 // The GGID is a unique ID for MP communications assigned to each application.
222 // You must receive a GGID assignment from Nintendo for an actual application.
223 // Here, the GGID is used that is identical to that of the dataShare-Model demo.
224 #define MY_GGID 0x003fff13
225
226 // MP communication settings
227 static MPConfig sMpConfig =
228 {
229 mpAlloc,
230 mpFree,
231
232 8, // threadPriority; set to a priority that is higher than that of the main thread by 4 or more
233
234 MP_MODE_PARENT, // mode
235
236 MY_GGID, // ggid
237 MP_TGID_AUTO, // tgid
238
239 MP_CHANNEL_AUTO, // channel; normally MP_CHANNEL_AUTO
240
241 MP_LIFETIME_DEFAULT,// lifeTime; usually equal to MP_LIFETIME_DEFAULT
242
243 MP_BEACON_PERIOD_AUTO, // beaconPeriod; normally MP_BEACON_PERIOD_AUTO
244 MY_MAX_NODES, // maxNodes
245 MY_PARENT_MAX_SIZE, // parentMaxSize
246 MY_DS_LENGTH, // childMaxSize
247 TRUE, // entryFlag
248 FALSE, // multiBootFlag; usually FALSE
249
250 1, // frequency
251
252 0, // userGameInfoLength
253 { 0, }, // userGameInfo; set MyGameInfo in the program
254
255 NULL, // indicationCallbackFunction
256
257 /// ... // port configuration (can be set up using MPSetPortConfig)
258 };
259
260 // DataSharing configuration
261 static MPDSConfig sMpdsConfig =
262 {
263 MY_DS_LENGTH, // dataLength
264
265 MY_DS_PORT, // port (must use one of sequential communications ports 12-15)
266 MP_PRIORITY_HIGH, // priority
267
268 TRUE, // isParent; always TRUE
269 (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)
270 TRUE, // isDoubleMode
271 TRUE, // isAutoStart; always TRUE
272 DataSharingCallback // mpdsCallback; NULL if unnecessary
273 };
274
275
main(void)276 void main(void)
277 {
278 OSReport("test program started.\n");
279
280 Initialize();
281
282 MainLoop();
283 }
284
Initialize(void)285 void Initialize( void )
286 {
287 /* Initialize OS and memory heap */
288 REXDEMOKPadInit();
289 REXDEMOInitScreen( FALSE );
290 REXDEMOSetGroundColor( black );
291 REXDEMOSetFontSize( 10, 20 );
292 REXDEMOBeginRender();
293 REXDEMOWaitRetrace();
294 (void)PADInit();
295
296 /* Initialize heap for MP library */
297 {
298 void* heapAddress;
299
300 heapAddress = OSGetMEM2ArenaLo();
301 OSSetMEM2ArenaLo( (void*)OSRoundUp32B( (u32)heapAddress + USERHEAP_SIZE ) );
302 sUserHeap = MEMCreateExpHeapEx( heapAddress, USERHEAP_SIZE, MEM_HEAP_OPT_THREAD_SAFE );
303 if( sUserHeap == NULL )
304 {
305 OSHalt( "Could not create heap.\n" );
306 }
307 }
308
309 OSInitMutex( &sGlobalMutex );
310 OSInitThreadQueue( &sStateChangeThreadQueue );
311
312 // Prepare ENCContext to match the ROM font's encoding, since string display uses the ROM font
313 //
314 (void)ENCInitContext(&sEncContext);
315 (void)ENCSetExternalEncoding(&sEncContext,
316 ( OSGetFontEncode() == OS_FONT_ENCODE_SJIS )
317 ? (const u8*)"Shift_JIS" : (const u8*)"ISO-8859-1");
318 (void)ENCSetBreakType(&sEncContext, ENC_BR_KEEP);
319 (void)ENCSetAlternativeCharacter(&sEncContext, L'?', L'?');
320
321 sState = STATE_IDLE;
322
323 // Create a status management thread
324 (void)OSCreateThread( &sStateManagementThread, StateManagementThreadFunc, NULL,
325 (void*)((u32)sStateManagementThreadStack + STACK_SIZE), STACK_SIZE,
326 THREAD_PRIORITY, OS_THREAD_ATTR_DETACH );
327 (void)OSResumeThread( &sStateManagementThread );
328 }
329
MainLoop(void)330 void MainLoop( void )
331 {
332 while (TRUE)
333 {
334 // Use exclusive access when touching global variables shared with other threads.
335 EnterCriticalSection();
336 REXDEMOKPadRead();
337 LeaveCriticalSection();
338
339 if ( OSIsThreadTerminated(&sStateManagementThread) )
340 {
341 // Exit the main loop because all threads have terminated.
342 OSReport("All threads are terminated; exit from MainLoop\n");
343 break;
344 }
345
346 Draw();
347
348 REXDEMOWaitRetrace();
349 sFrameCount++;
350 }
351 }
352
Draw(void)353 void Draw( void )
354 {
355 s32 state;
356 s32 i;
357
358 REXDEMOBeginRender();
359
360 {
361 // Use exclusive access when touching global variables shared with other threads.
362 EnterCriticalSection(); // Begin exclusive access
363 state = GetState();
364
365 switch ( state )
366 {
367 case STATE_LOBBY:
368 REXDEMOSetTextColor(green);
369 REXDEMOPrintf(40, 8, 0, "Lobby mode");
370 REXDEMOSetTextColor(white);
371 REXDEMOPrintf(4, 400, 0, "push <A> button to start.");
372
373 for (i = 0; i < MY_MAX_NODES+1; i++)
374 {
375 if (sConnectedFlag[i])
376 {
377 char nickName[20+1];
378 {
379 ENCContext convCtx;
380 s32 dstlen, srclen;
381 dstlen = sizeof(nickName) - 1 /* NULL termination*/;
382 srclen = -1; // Convert up to the NULL terminator
383 (void)NETMemSet(nickName, 0, sizeof(nickName));
384 (void)ENCDuplicateContext(&convCtx, &sEncContext);
385 (void)ENCConvertFromInternalEncoding(&convCtx, (u8*)nickName, &dstlen, sConnectedNickName[i], &srclen);
386 }
387 REXDEMOSetTextColor(yellow);
388 REXDEMOPrintf(40, (s16)(80 + i * 20), 0,
389 "AID(%02d): entry %02X:%02X:%02X:%02X:%02X:%02X %s",
390 i,
391 sConnectedMacAddress[i][0],
392 sConnectedMacAddress[i][1],
393 sConnectedMacAddress[i][2],
394 sConnectedMacAddress[i][3],
395 sConnectedMacAddress[i][4],
396 sConnectedMacAddress[i][5],
397 nickName);
398 }
399 else
400 {
401 REXDEMOSetTextColor(gray);
402 REXDEMOPrintf(40, (s16)(80 + i * 20), 0, "AID(%02d): --------", i);
403 }
404 }
405 break;
406
407 case STATE_COMMUNICATION:
408 REXDEMOSetTextColor(green);
409 REXDEMOPrintf(40, 8, 0, "Parent mode");
410 REXDEMOSetTextColor(white);
411 REXDEMOPrintf(4, 400, 0, "push <B> button to restart.");
412
413 REXDEMOSetTextColor(yellow);
414 REXDEMOPrintf(4, 30, 0, "Send: 0x%04X-0x%04X", 0, sFrameCount & 0xffff);
415 REXDEMOPrintf(4, 52, 0, "Receive:");
416
417 for (i = 0; i < MY_MAX_NODES+1; i++)
418 {
419 if (sRecvFlag[i])
420 {
421 REXDEMOSetTextColor(yellow);
422 REXDEMOPrintf(40, (s16)(80 + i * 20), 0,
423 "AID(%02d): %04X-%04X",
424 i,
425 MPMPToH16((u16)sRecvData[i].key), MPMPToH16((u16)(sRecvData[i].count & 0xffff)) );
426 }
427 else
428 {
429 REXDEMOSetTextColor(red);
430 REXDEMOPrintf(40, (s16)(80 + i * 20), 0, "No Data");
431 }
432 }
433 {
434 static const GXColor paletteTable[] =
435 {
436 { 0x80, 0x80, 0x80, }, // gray
437 { 0x00, 0xFF, 0x00, }, // green
438 { 0xFF, 0xFF, 0x00, }, // yellow
439 };
440 s16 x = 320;
441 s16 y = 10;
442 s16 z = -1023;
443 for (i = 0; i < BALL_PLAYER_MAX; ++i)
444 {
445 REXDEMOSetTextColor(paletteTable[shared->ball[i].plt]);
446 REXDEMOPrintf(x + shared->ball[i].x, y + shared->ball[i].y, 0, "%c", shared->ball[i].chr);
447 }
448 {
449 static const u32 font[] ATTRIBUTE_ALIGN(32) =
450 {
451 0xFFFFFFFF,
452 0xFFFFFFFF,
453 0xFFFFFFFF,
454 0xFFFFFFFF,
455 0xFFFFFFFF,
456 0xFFFFFFFF,
457 0xFFFFFFFF,
458 0xFFFFFFFF,
459 };
460 static const u16 pal[16] ATTRIBUTE_ALIGN(32) =
461 {
462 GXPackedRGB5A3(0x00, 0x00, 0x00, 0xFF), /* 0 : black */
463 GXPackedRGB5A3(0x80, 0x00, 0x00, 0xFF), /* 1 : dark red*/
464 GXPackedRGB5A3(0x00, 0x80, 0x00, 0xFF), /* 2 : dark green*/
465 GXPackedRGB5A3(0x80, 0x80, 0x00, 0xFF), /* 3 : dark yellow*/
466 GXPackedRGB5A3(0x00, 0x00, 0x80, 0xFF), /* 4 : dark blue*/
467 GXPackedRGB5A3(0x80, 0x00, 0x80, 0xFF), /* 5 : dark purple*/
468 GXPackedRGB5A3(0x00, 0x80, 0x80, 0x00), /* 6 : (clear) */
469 GXPackedRGB5A3(0x80, 0x80, 0x80, 0xFF), /* 7 : gray */
470 GXPackedRGB5A3(0xC0, 0xC0, 0xC0, 0xFF), /* 8 : light gray*/
471 GXPackedRGB5A3(0xFF, 0x00, 0x00, 0xFF), /* 9 : red */
472 GXPackedRGB5A3(0x00, 0xFF, 0x00, 0xFF), /* A : green*/
473 GXPackedRGB5A3(0xFF, 0xFF, 0x00, 0xFF), /* B : yellow*/
474 GXPackedRGB5A3(0x00, 0x00, 0xFF, 0xFF), /* C : blue*/
475 GXPackedRGB5A3(0xFF, 0x00, 0xFF, 0xFF), /* D : purple*/
476 GXPackedRGB5A3(0x00, 0xFF, 0xFF, 0xFF), /* E : cyan*/
477 GXPackedRGB5A3(0xFF, 0xFF, 0xFF, 0xFF), /* F : white*/
478 };
479 static GXTlutObj tlut;
480 Mtx mtx;
481 GXTexObj obj;
482 GXInitTlutObj(&tlut, (void *)pal, GX_TL_RGB5A3, 16);
483 GXLoadTlut(&tlut, GX_TLUT0);
484 GXInitTexObjCI(&obj, (void *)font,
485 8, 8, GX_TF_C4,
486 GX_CLAMP, GX_CLAMP, GX_FALSE, GX_TLUT0);
487 GXInitTexObjLOD(&obj, GX_NEAR, GX_NEAR,
488 0.0f, 0.0f, 0.0f, GX_DISABLE,
489 GX_FALSE, GX_ANISO_1);
490 GXLoadTexObj(&obj, GX_TEXMAP4);
491 MTXScale(mtx, 1.0f / (float)8, 1.0f / (float)8, 1.0f);
492 GXLoadTexMtxImm(mtx, GX_TEXMTX4, GX_MTX2x4);
493 // Temporarily change the REXDEMOBeginRender setting
494 GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX4);
495 GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP4, GX_COLOR0A0);
496 GXBegin(GX_QUADS, GX_VTXFMT0, 4);
497 {
498 GXPosition3s16((s16) (x), (s16) (y), (s16) z);
499 GXTexCoord2s16((s16) (0), (s16) (0));
500 GXPosition3s16((s16) (x + BALL_FIELD_WIDTH), (s16) (y), (s16) z);
501 GXTexCoord2s16((s16) (8), (s16) (0));
502 GXPosition3s16((s16) (x + BALL_FIELD_WIDTH), (s16) (y + BALL_FIELD_HEIGHT), (s16) z);
503 GXTexCoord2s16((s16) (8), (s16) (8));
504 GXPosition3s16((s16) (x), (s16) (y + BALL_FIELD_HEIGHT), (s16) z);
505 GXTexCoord2s16((s16) (0), (s16) (8));
506 }
507 GXEnd();
508 // Restore the REXDEMOBeginRender setting
509 GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_TEXMTX0);
510 GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);
511 }
512 }
513
514 break;
515
516 default:
517 REXDEMOSetTextColor(green);
518 REXDEMOPrintf(40, 8, 0, "Working...");
519 break;
520 }
521
522 // The link-level display is not required in the Wii Guidelines.
523 REXDEMOSetTextColor(green);
524 {
525 s32 linkLevel = MPGetLinkLevel();
526 REXDEMOPrintf(480, 400, 0, "Link Level: %d", (linkLevel >= 0) ? linkLevel : 0);
527 }
528
529 REXDEMOSetTextColor(white);
530
531 LeaveCriticalSection(); // End exclusive access
532 }
533 }
534
SetState(s32 state)535 void SetState( s32 state )
536 {
537 // Use exclusive access when touching global variables shared with other threads.
538 EnterCriticalSection(); // Begin exclusive access
539 sState = state;
540 LeaveCriticalSection(); // End exclusive access
541 OSWakeupThread( &sStateChangeThreadQueue );
542 }
543
GetState(void)544 inline s32 GetState( void )
545 {
546 return sState;
547 }
548
WaitForStateChange(void)549 s32 WaitForStateChange( void )
550 {
551 OSSleepThread( &sStateChangeThreadQueue );
552 // Note that with high-priority threads, in situations such as when SetState is called multiple times, only the final change to sState will be detected.
553 //
554 return GetState();
555 }
556
QuitMP(void)557 void QuitMP( void )
558 {
559 sQuitFlag = TRUE;
560 }
561
IsQuitting(void)562 BOOL IsQuitting( void )
563 {
564 return sQuitFlag;
565 }
566
567 /////////////////////////////////////////////////////////////////////
568 // Management thread for game state
StateManagementThreadFunc(void * arg)569 void* StateManagementThreadFunc( void* arg )
570 {
571 #pragma unused( arg )
572 s32 result;
573
574 ///////////////////////////////////////////////////////
575 // STATE_IDLE
576 ///////////////////////////////////////////////////////
577 state_idle:
578 SetState( STATE_IDLE );
579
580 ///////////////////////////////////////////////////////
581 // STATE_GOING_TO_LOBBY
582 ///////////////////////////////////////////////////////
583 state_going_to_lobby:
584 SetState( STATE_GOING_TO_LOBBY );
585
586 result = TransToLobby();
587
588 switch ( result )
589 {
590 case RESULT_OK:
591 // go to next state
592 break;
593
594 case RESULT_ERROR:
595 goto state_error;
596
597 case RESULT_QUIT:
598 goto state_going_to_idle;
599 }
600
601 ///////////////////////////////////////////////////////
602 // STATE_LOBBY
603 ///////////////////////////////////////////////////////
604 state_lobby:
605 SetState( STATE_LOBBY );
606
607 result = DoLobbyMode();
608
609 switch ( result )
610 {
611 case RESULT_OK:
612 // go to next state
613 break;
614
615 case RESULT_ERROR:
616 goto state_error;
617
618 case RESULT_QUIT:
619 goto state_going_to_idle;
620 }
621
622 ///////////////////////////////////////////////////////
623 // STATE_GOING_TO_COMMUNICATION
624 ///////////////////////////////////////////////////////
625 state_going_to_communication:
626 SetState( STATE_GOING_TO_COMMUNICATION );
627
628 (void)TransToCommunication();
629
630 ///////////////////////////////////////////////////////
631 // STATE_COMMUNICATION
632 ///////////////////////////////////////////////////////
633 state_communication:
634 SetState( STATE_COMMUNICATION );
635
636 result = DoCommunicationMode();
637
638 switch ( result )
639 {
640 case RESULT_OK:
641 // go to next state
642 goto state_going_to_idle;
643
644 case RESULT_ERROR:
645 goto state_error;
646
647 case RESULT_QUIT:
648 goto state_going_to_idle;
649 }
650
651 ///////////////////////////////////////////////////////
652 // STATE_GOING_TO_IDLE
653 ///////////////////////////////////////////////////////
654 state_going_to_idle:
655 SetState( STATE_GOING_TO_IDLE );
656
657 result = TransToIdle();
658
659 switch ( result )
660 {
661 case RESULT_ERROR:
662 goto state_error;
663 }
664
665 goto state_idle;
666
667 state_error:
668 OSReport("Error occurred.\n");
669
670 return NULL;
671 }
672
673 ////////////////////////////////////////////
674 // Transition from the idle state to lobby mode
TransToLobby(void)675 s32 TransToLobby( void )
676 {
677 s32 result;
678 int i;
679
680 // There are no other threads touching these global variables because MP communications have not yet started.
681 // Initialization without exclusive access can be performed.
682 for ( i = 0; i < MY_MAX_NODES+1; i++ )
683 {
684 sRecvFlag[i] = FALSE;
685 sConnectedFlag[i] = FALSE;
686 (void)NETMemSet(sConnectedMacAddress[i], 0, MP_SIZE_MACADDRESS);
687 (void)NETMemSet(sConnectedNickName[i], 0, sizeof(sConnectedNickName[0]));
688 }
689 // Configuration for this device
690 sConnectedFlag[0] = TRUE;
691 (void)NETGetWirelessMacAddress(sConnectedMacAddress[0]);
692 (void)NETMemSet(sConnectedNickName[0], 0, sizeof(sConnectedNickName[0]));
693 (void)NETMemCpy(sConnectedNickName[0], MY_NICKNAME, MY_NICKNAME_LENGTH * 2);
694 ASSERT(MY_NICKNAME_LENGTH * 2 <= sizeof(sConnectedNickName[0]) - 2);
695
696 sQuitFlag = FALSE;
697
698
699 // All session members should be maintained locally, so initialize the game information
700 // (Convert the input data through MP communications, and start updating the content using the same rules)
701 InitBall(NULL, 0, 1, 2);
702
703
704 // Initialize UserGameInfo
705 (void)NETMemSet(&sMyGameInfo, 0, sizeof(sMyGameInfo));
706 sMyGameInfo.nickNameLength = MY_NICKNAME_LENGTH;
707 /* Note that endian conversion must be performed for the data being sent to the DS*/
708 NETSwapAndCopyMemory16(sMyGameInfo.nickName, MY_NICKNAME, MY_NICKNAME_LENGTH*sizeof(u16));
709 sMyGameInfo.entryCount = 0;
710 sMyGameInfo.periodicalCount = 0;
711 (void)NETMemCpy( sMpConfig.userGameInfo, &sMyGameInfo, sizeof(sMyGameInfo) );
712 sMpConfig.userGameInfoLength = sizeof(MyGameInfo);
713
714 result = MPDSInit( &sDSContext, &sMpdsConfig );
715 if ( result < MP_RESULT_OK )
716 {
717 OSReport( "MPDSInit returns %08x\n", result );
718 return RESULT_ERROR;
719 }
720
721 result = MPDSSetupPortConfig( &sDSContext, &sMpConfig );
722 if ( result < MP_RESULT_OK )
723 {
724 OSReport( "MPDSSetupPortConfig returns %08x\n", result );
725 return RESULT_ERROR;
726 }
727
728 // Start the MP communication state according to config
729 // Block in order to wait for the MP state to occur.
730 OSReport("MPStartup()\n");
731 result = MPStartup( &sMpConfig );
732 if( result != MP_RESULT_OK )
733 {
734 OSReport( "MPStartup returns %08x\n", result );
735 return RESULT_ERROR;
736 }
737
738 // Create a thread for communications
739 (void)OSCreateThread( &sDataSharingThread, DataSharingThreadFunc, NULL,
740 (void*)((u32)sDataSharingThreadStack + STACK_SIZE), STACK_SIZE,
741 THREAD_PRIORITY, 0 );
742 (void)OSResumeThread( &sDataSharingThread );
743 (void)OSCreateThread( &sUpdateGameInfoThread, UpdateGameInfoThreadFunc, NULL,
744 (void*)((u32)sUpdateGameInfoThreadStack + STACK_SIZE), STACK_SIZE,
745 THREAD_PRIORITY, 0 );
746 (void)OSResumeThread( &sUpdateGameInfoThread );
747
748 return IsQuitting() ? RESULT_QUIT : RESULT_OK;
749 }
750
751 ////////////////////////////////////////////
752 // Transition from lobby mode to communications mode
TransToCommunication(void)753 s32 TransToCommunication( void )
754 {
755 s32 result;
756 int i;
757
758 // Stop accepting new connections
759 result = MPSetEntryFlag( FALSE );
760 if ( result < MP_RESULT_OK )
761 {
762 OSReport( "MPSetEntryFlag returns %08x\n", result );
763 return RESULT_ERROR;
764 }
765
766 // Update the beacon and send a notification that connections will no longer be accepted.
767 // Block for a long period of time, until the next beacon is sent.
768 result = MPUpdateBeacon();
769 if ( result < MP_RESULT_OK )
770 {
771 OSReport( "MPUpdateBeacon returns %08x\n", result );
772 return RESULT_ERROR;
773 }
774
775 // Use exclusive access when touching global variables shared with other threads.
776 EnterCriticalSection();
777 for ( i = 0; i < MY_MAX_NODES+1; i++ )
778 {
779 sRecvFlag[i] = FALSE;
780 }
781 LeaveCriticalSection();
782
783 return IsQuitting() ? RESULT_QUIT : RESULT_OK;
784 }
785
786 ////////////////////////////////////////////
787 // Transition to the idle state
TransToIdle(void)788 s32 TransToIdle( void )
789 {
790 s32 result;
791
792 // Send a termination command to the thread
793 QuitMP();
794
795 // Ends the MP communication state
796 // Block for a long period of time to wait for all child devices to disconnect.
797 OSReport("MPCleanup()\n");
798 result = MPCleanup();
799 if ( result < MP_RESULT_OK )
800 {
801 OSReport( "MPCleanup returns %08x\n", result );
802 return RESULT_ERROR;
803 }
804
805 (void)OSJoinThread( &sDataSharingThread, NULL );
806 (void)OSJoinThread( &sUpdateGameInfoThread, NULL );
807
808 return RESULT_OK;
809 }
810
811 ////////////////////////////////////////////
812 // Conditions for state transitions in lobby mode
DoLobbyMode(void)813 s32 DoLobbyMode( void )
814 {
815 BOOL fBreak = FALSE;
816
817 // Accept new connections from child devices
818 {
819 while ( !fBreak )
820 {
821 // Use exclusive access when touching global variables shared with other threads.
822 EnterCriticalSection();
823 if( REXDEMOGetAnyMixedPadTrigger() & (KPAD_BUTTON_A | (PAD_BUTTON_A << 16)) )
824 {
825 // Using the A Button, transition to the next state
826 fBreak = TRUE;
827 }
828 LeaveCriticalSection();
829
830 VIWaitForRetrace();
831 }
832 }
833
834 return RESULT_OK;
835 }
836
837 ////////////////////////////////////////////
838 // Conditions for state transitions in communication mode
DoCommunicationMode(void)839 s32 DoCommunicationMode( void )
840 {
841 BOOL fBreak = FALSE;
842
843 // Communicating
844 {
845 while ( !fBreak )
846 {
847 // Use exclusive access when touching global variables shared with other threads.
848 EnterCriticalSection();
849 if( REXDEMOGetAnyMixedPadTrigger() & (KPAD_BUTTON_B | (PAD_BUTTON_B << 16)) )
850 {
851 // Using the B Button, transition to the next state
852 fBreak = TRUE;
853 }
854 LeaveCriticalSection();
855
856 VIWaitForRetrace();
857 }
858 }
859
860 return RESULT_OK;
861 }
862
863
864 /////////////////////////////////////////////////////////////////////
865 // Thread to process DataSharing
DataSharingThreadFunc(void * arg)866 void* DataSharingThreadFunc( void* arg )
867 {
868 #pragma unused( arg )
869 s32 result;
870 ShareData sendData;
871 MPDSDataSet recvDataSet;
872 BOOL fError;
873 s32 i;
874 s32 state;
875
876 // Because the V-Blank periods are different for the Wii and the DS, the period for MP communications carried out between DS systems will not match the Wii's V-Blank period.
877 //
878 // Consequently, we need a reserved thread that runs at the MP communications period for DataSharing (the period with which the MPDSStep function succeeds), which is separate from the main rendering loop that is running at the Wii's V-Blank period.
879 //
880 //
881
882 // It is also possible to perform DataSharing by using the MPDSTryStep function within the main loop, without creating a thread. However, in this case, the timing with which data is set will not match between the parent and children, and DataSharing will fail with high frequency.
883 //
884 //
885 //
886
887 fError = FALSE;
888
889 while ( !IsQuitting() && fError == FALSE )
890 {
891 // Use exclusive access when touching global variables shared with other threads.
892 EnterCriticalSection(); // Begin exclusive access
893
894 state = GetState();
895
896 switch ( state )
897 {
898 case STATE_LOBBY:
899 case STATE_COMMUNICATION:
900 // When in a state in which DataSharing is running:
901
902 // Create data to send from the current game state
903 // Note that the Wii and DS have a different endianness.
904 (void)NETMemSet( &sendData, 0, sizeof(sendData) );
905 sendData.count = MPHToMP16( (u16)sFrameCount ); // Endian conversion
906 if ( state == STATE_LOBBY )
907 {
908 sendData.command = SHARECMD_NONE;
909 }
910 else
911 {
912 sendData.command = SHARECMD_EXITLOBBY;
913 }
914 sendData.key = MPHToMP16(ConvKPadKeyToDSPad(REXDEMOGetAnyMixedPadHold()));
915 sendData.level = MPGetLinkLevel();
916
917 // We must not block while maintaining exclusive access
918 LeaveCriticalSection(); // End exclusive access
919
920 // Share data using DataSharing
921 // The MPDSStep function blocks for a long period of time until the data is complete.
922 result = MPDSStep( &sDSContext, &sendData, &recvDataSet );
923
924 // Parse the received data and update the game state
925 {
926 // Use exclusive access when touching global variables shared with other threads.
927 EnterCriticalSection(); // Begin exclusive access
928
929 if ( state != GetState() )
930 {
931 // The state changed while calling the MPDS[Try]Step function.
932 // Ignore because regardless of the state, there is no disparity in data parsing at this time.
933 //
934 // The data that was shared must be parsed in the same way by all parents and children, so depending on the extent of the processing, it is probably best to include the state in the shared data, and break up the processing depending on the state that is included in the shared data.
935 //
936 //
937 }
938
939 for ( i = 0; i < MY_MAX_NODES+1; i++ )
940 {
941 sRecvFlag[i] = FALSE;
942 }
943
944 if( result == MP_RESULT_OK )
945 {
946 // Successfully received shared data
947 const u8* data;
948
949 // Parse the contents of the shared data
950 for ( i = 0; i < MY_MAX_NODES+1; i++ )
951 {
952 data = MPDSGetData( &sDSContext, &recvDataSet, (u32)i );
953 if ( data != NULL )
954 {
955 // Was able to share data from the i-th sharing partner
956 (void)NETMemCpy( (void*)&sRecvData[i], data, MY_DS_LENGTH );
957 sRecvFlag[i] = TRUE;
958 }
959 else
960 {
961 // There are a number of cases in which NULL is returned.
962 // 1. Empty data is read immediately after DataSharing started
963 // 2. The child device with the corresponding AID is not connected
964 // (3. Some kind of internal error has occurred)
965
966 // You can use this information to check if a child device is connected or disconnected.
967 // 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.
968 //
969 //
970 // Immediately after the start of the game, use the MPSetEntryFlag and MPUpdateBeacon functions to cut off new child connections.
971 //
972 }
973 }
974
975 //
976 //
977 // Data could have been shared between the child and the parent, so update the in-game state.
978 // The logic for using the shared data to update the in-game state is placed here.
979 //
980 // 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.
981 //
982 // 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.
983 //
984 //
985 //
986
987 for (i = 0; i < BALL_PLAYER_MAX; ++i)
988 {
989 if ((i < MY_MAX_NODES+1) && sRecvFlag[i])
990 {
991 InputBallKey((int)i, (int)MPMPToH16((u16)sRecvData[i].key));
992 }
993 }
994 UpdateBalls(0);
995
996 }
997 else if ( result == MP_RESULT_NO_DATA )
998 {
999 // This is an error that occurs normally if the MPDSTryStep function was used and the data was not complete due to the communication status being wrong.
1000 //
1001 //
1002 // This will not occur if the MPDSStep function was used.
1003
1004 //
1005 // Data sharing could not be achieved between the parent and children, so wait for the next frame without updating the in-game state.
1006 //
1007 //
1008 // 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.
1009 //
1010 //
1011 //
1012 //
1013 }
1014 else
1015 {
1016 // error
1017 OSReport("MPDSStep failed: %08x\n", result);
1018 fError = TRUE;
1019 }
1020
1021 LeaveCriticalSection(); // End exclusive access
1022 }
1023 break;
1024
1025 default:
1026 // if not in a communication state
1027 LeaveCriticalSection(); // End exclusive access
1028
1029 // block and wait until the state changes
1030 (void)WaitForStateChange();
1031 break;
1032 }
1033 }
1034
1035 return NULL;
1036 }
1037
1038 /////////////////////////////////////////////////////////////////////
1039 // Thread for beacon updates
UpdateGameInfoThreadFunc(void * arg)1040 void* UpdateGameInfoThreadFunc( void* arg )
1041 {
1042 #pragma unused( arg )
1043 while ( !IsQuitting() )
1044 {
1045 switch ( GetState() )
1046 {
1047 case STATE_LOBBY:
1048 case STATE_COMMUNICATION:
1049 // Update beacon contents periodically during communication
1050 sMyGameInfo.entryCount = MPCountPopulation(MPGetConnectedAIDs());
1051 sMyGameInfo.periodicalCount++;
1052 (void)MPSetUserGameInfo(&sMyGameInfo, sizeof(sMyGameInfo));
1053
1054 // Apply the configured UserGameInfo to the beacon
1055 // Block for a long period of time, until the updated beacon is sent.
1056 (void)MPUpdateBeacon();
1057 break;
1058
1059 default:
1060 (void)WaitForStateChange();
1061 break;
1062 }
1063 }
1064
1065 return NULL;
1066 }
1067
1068 /////////////////////////////////////////////////////////////////////
1069 // Event notification callback
DataSharingCallback(s32 type,MPPortCallbackInfo * info)1070 void DataSharingCallback( s32 type, MPPortCallbackInfo* info )
1071 {
1072 // The MPDS library forwards notifications from the MP library.
1073 // In this demo, callbacks are used to obtain detailed information about the children that have connected, but if only DataSharing is being used, there is no particular necessarily to prepare a callback function.
1074 //
1075 //
1076
1077 // This function is called from a thread for MP library callbacks.
1078 // Because it is not an interrupt callback, it is also possible to call a thread syncrhonization function, but if blocking occurs for a long period of time, MP communications will be affected.
1079 //
1080 // Limit blocking to the minimum required to implement exclusive access.
1081 // Depending on the circumstances, you may also want to look into a method of receiving and passing data in a way that doesn't block, using OSMessageQueue or other such functions.
1082 //
1083
1084 u32 aid;
1085
1086 switch ( type )
1087 {
1088 case MP_PORT_CB_TYPE_CONNECTED:
1089 // a child device has connected
1090 aid = info->connected.fromAid;
1091
1092 EnterCriticalSection();
1093 (void)NETMemCpy(sConnectedMacAddress[aid], info->connected.macAddress, MP_SIZE_MACADDRESS);
1094 (void)NETMemSet(sConnectedNickName[aid], 0, sizeof(sConnectedNickName[0]));
1095 /* Note that data from the DS has a different endianness.*/
1096 NETSwapAndCopyMemory16(sConnectedNickName[aid], info->connected.ssidUserData, info->connected.ssidUserDataLength);
1097 sConnectedFlag[aid] = TRUE;
1098 LeaveCriticalSection();
1099
1100 break;
1101
1102 case MP_PORT_CB_TYPE_DISCONNECTED:
1103 // a child device has disconnected
1104 aid = info->disconnected.fromAid;
1105
1106 EnterCriticalSection();
1107 sConnectedFlag[aid] = FALSE;
1108 (void)NETMemSet(sConnectedMacAddress[aid], 0, MP_SIZE_MACADDRESS);
1109 (void)NETMemSet(sConnectedNickName[aid], 0, sizeof(sConnectedNickName[0]));
1110 LeaveCriticalSection();
1111
1112 break;
1113
1114 case MP_PORT_CB_TYPE_STARTUP:
1115 case MP_PORT_CB_TYPE_CLEANUP:
1116 case MPDS_PORT_CB_TYPE_DATASET_RECEIVED:
1117 default:
1118 // there is nothing to do
1119 break;
1120 }
1121 }
1122
1123 /*===========================================================================*/
1124
mpAlloc(u32 size)1125 void* mpAlloc( u32 size )
1126 {
1127 return MEMAllocFromExpHeapEx( sUserHeap, size, 32 );
1128 }
1129
mpFree(void * ptr)1130 void mpFree( void* ptr )
1131 {
1132 MEMFreeToExpHeap( sUserHeap, ptr );
1133 }
1134
1135 /*===========================================================================*/
1136
1137 // PAD constants for the DS
1138 #define DSPAD_BUTTON_A 0x0001 // A
1139 #define DSPAD_BUTTON_B 0x0002 // B
1140 #define DSPAD_BUTTON_SELECT 0x0004 // SELECT
1141 #define DSPAD_BUTTON_START 0x0008 // START
1142 #define DSPAD_KEY_RIGHT 0x0010 // +Control Pad, RIGHT
1143 #define DSPAD_KEY_LEFT 0x0020 // +Control Pad, LEFT
1144 #define DSPAD_KEY_UP 0x0040 // +Control Pad, UP
1145 #define DSPAD_KEY_DOWN 0x0080 // +Control Pad, DOWN
1146 #define DSPAD_BUTTON_R 0x0100 // R
1147 #define DSPAD_BUTTON_L 0x0200 // L
1148 #define DSPAD_BUTTON_X 0x0400 // X
1149 #define DSPAD_BUTTON_Y 0x0800 // Y
1150
ConvKPadKeyToDSPad(u32 mixedKey)1151 u16 ConvKPadKeyToDSPad(u32 mixedKey)
1152 {
1153 u16 key = 0;
1154
1155 if ( mixedKey & (KPAD_BUTTON_LEFT | (PAD_BUTTON_LEFT << 16)) )
1156 {
1157 key |= DSPAD_KEY_LEFT;
1158 }
1159 if ( mixedKey & (KPAD_BUTTON_RIGHT | (PAD_BUTTON_RIGHT << 16)) )
1160 {
1161 key |= DSPAD_KEY_RIGHT;
1162 }
1163 if ( mixedKey & (KPAD_BUTTON_UP | (PAD_BUTTON_UP << 16)) )
1164 {
1165 key |= DSPAD_KEY_UP;
1166 }
1167 if ( mixedKey & (KPAD_BUTTON_DOWN | (PAD_BUTTON_DOWN << 16)) )
1168 {
1169 key |= DSPAD_KEY_DOWN;
1170 }
1171 if( mixedKey & (KPAD_BUTTON_A | (PAD_BUTTON_A << 16)) )
1172 {
1173 key |= DSPAD_BUTTON_A;
1174 }
1175 if( mixedKey & (KPAD_BUTTON_B | (PAD_BUTTON_B << 16)) )
1176 {
1177 key |= DSPAD_BUTTON_A;
1178 }
1179 if( mixedKey & (KPAD_BUTTON_1 | (PAD_BUTTON_X << 16)) )
1180 {
1181 key |= DSPAD_BUTTON_X;
1182 }
1183 if( mixedKey & (KPAD_BUTTON_2 | (PAD_BUTTON_Y << 16)) )
1184 {
1185 key |= DSPAD_BUTTON_Y;
1186 }
1187 if( mixedKey & (KPAD_BUTTON_PLUS | (PAD_TRIGGER_R << 16)) )
1188 {
1189 key |= DSPAD_BUTTON_R;
1190 }
1191 if( mixedKey & (KPAD_BUTTON_MINUS | (PAD_TRIGGER_L << 16)) )
1192 {
1193 key |= DSPAD_BUTTON_L;
1194 }
1195 if( mixedKey & (KPAD_BUTTON_HOME | (PAD_BUTTON_START << 16)) )
1196 {
1197 key |= DSPAD_BUTTON_START;
1198 }
1199
1200 return key;
1201 }
1202