I've further abstracted the linkkit code to my own handshake routine, which I think handles the host/client detection better and also attempts to sync to the same "game" on the other side of the cable - this might be a bit clunky still, but it seems to be consistent from the couple of dozen tests I've run - I should probably make that counter a u16 as it wraps pretty quickly. For my purposes I don't really mind much - as long as the handshake consistently nominates one machine as the host and the other as the client.
Worked example in ahchay/HEADTOHEAD.ngp: Head to head classics for the NGPC - Codeberg.org
Worked example in ahchay/HEADTOHEAD.ngp: Head to head classics for the NGPC - Codeberg.org
Code:
/*******************************************************************
* Set up the link to the other console
*
* The four Char* parameters here are the unique code for the game
* P-O-N-G for Pong, N-O-O-N for High Noon etc
*
********************************************************************/
u8 HeadToHeadHandshake(u8 Char1, u8 Char2, u8 Char3, u8 Char4)
{
u8 myBuffer[8];
u8 theirBuffer[8];
bool ReadyHost;
bool ReadyClient;
u8 iLoop;
u8 link_state;
u8 ConnectionTime;
u8 retval;
ReadyHost=false;
ReadyClient=false;
ngpc_linkkit_init();
link_state=ngpc_linkkit_state();
//PrintString(SCR_FORE_PLANE,PAL_TEXT,0,0,"PLAYER VS LINK");
//PrintString(SCR_FORE_PLANE,PAL_TEXT,8,0,"...");
ConnectionTime=0;
retval=ROLE_CLIENT;
while((!ReadyHost)||(!ReadyClient))
{
// Send and receive the joystick state between the two machines
u8 iLoop;
if(!ReadyHost) ConnectionTime++;
myBuffer[0]=LINK_HANDSHAKE;
myBuffer[1]=Char1;
myBuffer[2]=Char2;
myBuffer[3]=Char3;
myBuffer[4]=Char4;
myBuffer[5]=ConnectionTime;
myBuffer[6]=QRandom();
myBuffer[7]=ReadyHost;
ngpc_linkkit_stage(myBuffer); // stages the send
ngpc_linkkit_update(); // sends and receives data
if (ngpc_linkkit_fresh()) // Checks that the link is active
{
ReadyHost=true;
ngpc_linkkit_peek(theirBuffer); // Retrieves the data into gRx[]
//UnpackTheirs();
}
link_state=ngpc_linkkit_state();
switch (link_state) // Check link status
{
case LINKKIT_CONNECTING:
//PrintString(SCR_FORE_PLANE,PAL_TEXT,8,0,"...");
break;
case LINKKIT_READY:
//PrintString(SCR_FORE_PLANE,PAL_TEXT,8,0,"OK!");
break;
case LINKKIT_LOST:
//PrintString(SCR_FORE_PLANE,PAL_TEXT,8,0,"!!!");
break;
case LINKKIT_MISMATCH:
//PrintString(SCR_FORE_PLANE,PAL_TEXT,8,0,"???");
//PrintString(SCR_FORE_PLANE,PAL_TEXT, 0, 17, "OTHER CART DIFFERS ");
break;
default:
//PrintString(SCR_FORE_PLANE,PAL_TEXT,8,0,"***");
ngpc_linkkit_init();
break;
}
ReadyClient=true;
switch(theirBuffer[0])
{
case LINK_HANDSHAKE:
// other client in handshake mode (as they should be)
// Check that their buffer contains the magic letters
// for this game mode
// Probably need to complain/abort if these differ
ReadyClient=theirBuffer[7];
if(ReadyClient)
{
for(iLoop=0;iLoop<5;iLoop++)
{
if(theirBuffer[iLoop]!=myBuffer[iLoop])
{
ReadyClient=false;
}
}
if(ReadyClient)
{
if(ConnectionTime<theirBuffer[5])
{
retval=ROLE_HOST;
}
else if(ConnectionTime==theirBuffer[5]&&myBuffer[6]<theirBuffer[6])
{
retval=ROLE_HOST;
}
}
}
break;
case LINK_GAME:
case LINK_META:
// other client is already in game - shouldn't happen, but
// it can do
// Just let it fall through - they may join the game late
// but the sync routine should sort it out
// At this point, we're assuming that the other client
// is the same cartid. They shouldn't have got past the
// handshake on their end if not
// Players can force this by starting one game mode and then
// quitting one console and rejoining in another.
// That's on them though ;)
break;
default:
// Nonsense or empty data
ReadyClient=false;
break;
}
// if(myBuffer[0]!=theirBuffer[0])
// {
// ReadyClient=false;
// }
if(JOYPAD_OPTION)
{
return ROLE_ABORT;
}
}
return retval;
}

