/*---------------------------------------------------------------------------* Project: Revolution MPDS simple demo File: mpdssimple.c Copyright 2007 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Log: mpdssimple.c,v $ Revision 1.12 2007/11/29 03:20:27 seiki_masashi Added the const modifier. Revision 1.11 2007/10/26 09:31:17 seiki_masashi Cleaned up the code Revision 1.10 2007/10/26 07:29:56 seiki_masashi Cleaned up the code Revision 1.9 2007/10/26 07:05:28 seiki_masashi Revised the link-level display Revision 1.6 2007/10/24 09:30:41 okubata_ryoma Small fix Revision 1.5 2007/10/22 09:50:11 seiki_masashi Made the MPDSStep function into a synchronous function, and added the new function MPDSTryStep Revision 1.1 2007/10/10 08:38:45 seiki_masashi Added the MPDS library $NoKeywords: $ *---------------------------------------------------------------------------*/ // // This sample performs communications with the demos/wm/dataShare-1 demo in the NITRO-SDK. // // 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. // // #include #include #include #include #include #include "rexdemo/demokpad.h" #include "rexdemo/graphic.h" #define USERHEAP_SIZE ( 65536 ) #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) #define MY_DS_NODES 16 // the number of nodes, including parent devices, that perform DataSharing (matches the dataShare-1 demo in the NITRO-SDK) // for a normal application (MY_MAX_NODES+1) #define MY_DS_LENGTH 8 // the shared data size per device for DataSharing // When DataSharing is performed, the required transmission size is the shared size per device times the number of devices, plus 4 bytes. #define MY_PARENT_MAX_SIZE ( (MY_DS_LENGTH * MY_DS_NODES) + MPDS_HEADER_SIZE ) /*===========================================================================*/ static MEMHeapHandle sUserHeap; static u16 sRecvBuf[MY_MAX_NODES+1][MY_DS_LENGTH/sizeof(u16)]; static BOOL sRecvFlag[MY_MAX_NODES+1]; static MPDSContext sDSContext; /*===========================================================================*/ static const GXColor white = { 0xFF, 0xFF, 0xFF, }; static const GXColor yellow = { 0xFF, 0xFF, 0x00, }; static const GXColor gray = { 0x80, 0x80, 0x80, }; static const GXColor black = { 0x00, 0x00, 0x00, }; static const GXColor red = { 0xFF, 0x00, 0x18 }; static const GXColor green = { 0x00, 0xFF, 0x00 }; /*===========================================================================*/ static void DoMPCommunication( void ); static void* mpAlloc( u32 size ); static void mpFree( void* ptr ); static void PortCallbackFunction( s32 type, MPPortCallbackInfo* info ); /*===========================================================================*/ // Toggles whether to use doubleMode for DataSharing. // Using doubleMode makes it possible to achieve synchronization of data at 60 fps, even when the MP frequency is set to once per frame. // // The dataShare-1 demo in NITRO-SDK does not use doubleMode by default. // If trying out doubleMode, change from 2 to 1 in the dataShare-1 demo. PICTURE_FRAME_PER_GAME_FRAME: // //#define DATA_SHARING_DOUBLE_MODE 1 // The GGID is a unique ID for MP communications assigned to each application. // You must receive a GGID assignment from Nintendo for an actual application. // Here, the GGID is used that is identical to that of the dataShare-1 demo. #define MY_GGID 0x003fff11 // MP communication settings static MPConfig sMpConfig = { mpAlloc, mpFree, 8, // threadPriority; set to a priority that is higher than that of the main thread by 4 or more MP_MODE_PARENT, // mode MY_GGID, // ggid MP_TGID_AUTO, // tgid 1, // channel // Because the dataShare-1 demo of NITRO-SDK is fixed to ch1, the parent device is also fixed to ch1 // // Normally applications should use automatic settings by specifying MP_CHANNEL_AUTO MP_LIFETIME_DEFAULT,// lifeTime; usually equal to MP_LIFETIME_DEFAULT MP_BEACON_PERIOD_AUTO, // beaconPeriod; normally MP_BEACON_PERIOD_AUTO MY_MAX_NODES, // maxNodes MY_PARENT_MAX_SIZE, // parentMaxSize MY_DS_LENGTH, // childMaxSize TRUE, // entryFlag FALSE, // multiBootFlag; usually FALSE 1, // frequency 0, // userGameInfoLength { 0, }, // userGameInfo NULL, // indicationCallbackFunction /// ... // port configuration (can be set up using MPSetPortConfig) }; // DataSharing configuration static MPDSConfig sMpdsConfig = { MY_DS_LENGTH, // dataLength 12, // port (must use one of sequential communications ports 12-15) MP_PRIORITY_HIGH, // priority TRUE, // isParent; always TRUE (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) #ifdef DATA_SHARING_DOUBLE_MODE TRUE, // isDoubleMode #else FALSE, // isDoubleMode #endif TRUE, // isAutoStart; always TRUE NULL // mpdsCallback }; void main(void) { /* Initialize OS and memory heap */ OSReport("test program started.\n"); REXDEMOKPadInit(); REXDEMOInitScreen( FALSE ); REXDEMOSetGroundColor( black ); REXDEMOSetFontSize( 10, 20 ); REXDEMOBeginRender(); REXDEMOWaitRetrace(); /* Initialize heap for MP library */ { void* heapAddress; heapAddress = OSGetMEM2ArenaLo(); OSSetMEM2ArenaLo( (void*)OSRoundUp32B( (u32)heapAddress + USERHEAP_SIZE ) ); sUserHeap = MEMCreateExpHeapEx( heapAddress, USERHEAP_SIZE, MEM_HEAP_OPT_THREAD_SAFE ); if( sUserHeap == NULL ) { OSHalt( "Could not create heap.\n" ); } } (void)PADInit(); OSReport("push button to restart.\n"); while (TRUE) { DoMPCommunication(); } } void DoMPCommunication( void ) { s32 result; result = MPDSInit( &sDSContext, &sMpdsConfig ); if ( result < 0 ) { OSReport( "MPDSInit returns %08x\n", result ); OSHalt("** panic **"); } result = MPDSSetupPortConfig( &sDSContext, &sMpConfig ); if ( result < 0 ) { OSReport( "MPDSSetupPortConfig returns %08x\n", result ); OSHalt("** panic **"); } OSReport("MPStartup()\n"); result = MPStartup( &sMpConfig ); if( result != MP_RESULT_OK ) { OSReport( "MPStartup returns %08x\n", result ); OSHalt( "** panic **\n" ); } { static u16 frameData[MY_DS_LENGTH/sizeof(u16)]; u32 counter1 = 0; u32 counter2 = 0; #ifndef DATA_SHARING_DOUBLE_MODE // 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. // // In this case, a flag will be required for determining whether the current frame coincides with the timing of a game state update. // BOOL updateFlag = TRUE; #endif for( counter1 = 0; TRUE; counter1++ ) { s32 i; MPDSDataSet recvDataSet; REXDEMOKPadRead(); if( REXDEMOGetAnyMixedPadTrigger() & (KPAD_BUTTON_A | (PAD_BUTTON_A << 16)) ) { break; } #ifndef DATA_SHARING_DOUBLE_MODE // Update the game state at a frequency of once every two frames if ( updateFlag ) #else // When using doubleMode, it is possible to update the game state every frame #endif { // Note that the Wii and DS have a different endianness. // The data order is being made to match the dataShare-1 demo in NITRO-SDK frameData[0] = MPHToMP16((u16)(counter1 >> 16)); frameData[1] = MPHToMP16((u16)(counter1 & 0xffff)); frameData[2] = MPHToMP16((u16)(counter2 >> 16)); frameData[3] = MPHToMP16((u16)(counter2 & 0xffff)); // Increment counter2 to match the dataShare-1 demo in NITRO-SDK counter2++; for ( i = 0; i < MY_MAX_NODES+1; i++ ) { sRecvFlag[i] = FALSE; } // Share data using DataSharing result = MPDSTryStep( &sDSContext, frameData, &recvDataSet ); if( result == MP_RESULT_OK ) { // Successfully received shared data const u8* data; // Parse the contents of the shared data for ( i = 0; i < MY_MAX_NODES+1; i++ ) { data = MPDSGetData( &sDSContext, &recvDataSet, (u32)i ); if ( data != NULL ) { // Was able to share data from the i-th sharing partner (void)NETMemCpy( sRecvBuf[i], data, MY_DS_LENGTH ); sRecvFlag[i] = TRUE; } else { // There are a number of cases in which NULL is returned. // 1. Empty data is read immediately after DataSharing started // 2. The child device with the corresponding AID is not connected // (3. Some kind of internal error has occurred) // You can use this information to check if a child device is connected or disconnected. // 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. // // // Immediately after the start of the game, use the MPSetEntryFlag and MPUpdateBeacon functions to cut off new child connections. // } } #ifndef DATA_SHARING_DOUBLE_MODE // The game state was updated successfully, so do not update at the next frame // updateFlag = FALSE; #endif // // // Data could have been shared between the child and the parent, so update the in-game state. // The logic for using the shared data to update the in-game state is placed here. // // 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. // // 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. // // // } else if ( result == MP_RESULT_NO_DATA ) { // This is an error that occurs normally if the data was not complete due to the communication status being wrong. // #ifndef DATA_SHARING_DOUBLE_MODE // Try updating again at the next frame updateFlag = TRUE; #endif // Decrement counter1 in order to match the dataShare-1 demo in NITRO-SDK counter1--; // // Data sharing could not be achieved between the parent and children, so wait for the next frame without updating the in-game state. // // // 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. // // // // } else { // error OSReport("MPDSTryStep failed: %08x\n", result); break; } } #ifndef DATA_SHARING_DOUBLE_MODE else { // An update did not occur at this frame, so update at the next frame updateFlag = TRUE; } #endif REXDEMOBeginRender(); REXDEMOSetTextColor(green); REXDEMOPrintf(40, 8, 0, "Parent mode"); REXDEMOSetTextColor(white); REXDEMOPrintf(4, 400, 0, "push button to restart."); REXDEMOSetTextColor(green); { s32 linkLevel = MPGetLinkLevel(); REXDEMOPrintf(480, 400, 0, "Link Level: %d", (linkLevel >= 0) ? linkLevel : 0); } REXDEMOSetTextColor(yellow); REXDEMOPrintf(4, 30, 0, "Send: %08X-%08X", counter1, counter2); REXDEMOPrintf(4, 52, 0, "Receive:"); { s32 i; for (i = 0; i < MY_MAX_NODES+1; i++) { if (sRecvFlag[i]) { REXDEMOSetTextColor(yellow); REXDEMOPrintf(40, (s16)(80 + i * 20), 0, "AID(%02d): %04X%04X-%04X%04X", i, MPMPToH16(sRecvBuf[i][0]), MPMPToH16(sRecvBuf[i][1]), MPMPToH16(sRecvBuf[i][2]), MPMPToH16(sRecvBuf[i][3]) ); } else { REXDEMOSetTextColor(red); REXDEMOPrintf(40, (s16)(80 + i * 20), 0, "No Data"); } } } REXDEMOSetTextColor(white); REXDEMOWaitRetrace(); } } OSReport("MPCleanup()\n"); (void)MPCleanup(); } /*===========================================================================*/ void* mpAlloc( u32 size ) { return MEMAllocFromExpHeapEx( sUserHeap, size, 32 ); } void mpFree( void* ptr ) { MEMFreeToExpHeap( sUserHeap, ptr ); }