From 6afbf4eb853bc8edbb0d69f487960b057b37fad4 Mon Sep 17 00:00:00 2001 From: Jeremy Rand Date: Sun, 11 Jun 2023 22:51:21 -0400 Subject: [PATCH] Finish reworking the network high score code to be independent of BuGS itself so it can be put into its own library. --- BuGS/global.macros | 28 +++---- BuGS/globals.s | 1 - BuGS/main.c | 117 +++++++++++++++++++++++++++- BuGS/netScores.c | 188 ++++++++++++++++----------------------------- BuGS/netScores.h | 41 +++++++++- BuGS/score.s | 8 +- BuGS/settings.c | 8 ++ BuGS/settings.h | 6 +- 8 files changed, 247 insertions(+), 150 deletions(-) diff --git a/BuGS/global.macros b/BuGS/global.macros index 8ea05ba..1eda80b 100644 --- a/BuGS/global.macros +++ b/BuGS/global.macros @@ -224,53 +224,53 @@ _dirtyNonGameTile_skip&SYSCNT anop macro _globalHighScoreRow &nthScore - ldy #2+&nthScore*SETTINGS_HIGH_SCORE_SIZE - lda highScoreResponse,y + ldy #&nthScore*SETTINGS_HIGH_SCORE_SIZE + lda globalHighScores,y jsl asciiToTileType jsl setGameTile iny - lda highScoreResponse,y + lda globalHighScores,y jsl asciiToTileType jsl setGameTile iny - lda highScoreResponse,y + lda globalHighScores,y jsl asciiToTileType jsl setGameTile iny - lda highScoreResponse,y + lda globalHighScores,y jsl asciiToTileType jsl setGameTile iny - lda highScoreResponse,y + lda globalHighScores,y jsl asciiToTileType jsl setGameTile iny - lda highScoreResponse,y + lda globalHighScores,y jsl asciiToTileType jsl setGameTile iny - lda highScoreResponse,y + lda globalHighScores,y jsl asciiToTileType jsl setGameTile iny - lda highScoreResponse,y + lda globalHighScores,y jsl asciiToTileType jsl setGameTile iny - lda highScoreResponse,y + lda globalHighScores,y jsl asciiToTileType jsl setGameTile iny - lda highScoreResponse,y + lda globalHighScores,y jsl asciiToTileType jsl setGameTile @@ -278,17 +278,17 @@ _dirtyNonGameTile_skip&SYSCNT anop jsl setGameTile iny - lda highScoreResponse,y + lda globalHighScores,y jsl asciiToTileType jsl setGameTile iny - lda highScoreResponse,y + lda globalHighScores,y jsl asciiToTileType jsl setGameTile iny - lda highScoreResponse,y + lda globalHighScores,y jsl asciiToTileType jsl setGameTile mend diff --git a/BuGS/globals.s b/BuGS/globals.s index 6dfddce..f7b8804 100644 --- a/BuGS/globals.s +++ b/BuGS/globals.s @@ -195,7 +195,6 @@ collisionAddr dc i2'0' numSegments dc i2'0' displayGlobalHighScores dc i2'0' shouldQuit dc i2'1' -scoreIndex dc i2'0' tileJumpTable dc a4'solid0' diff --git a/BuGS/main.c b/BuGS/main.c index 62f778a..4d4628f 100644 --- a/BuGS/main.c +++ b/BuGS/main.c @@ -26,6 +26,8 @@ /* Defines and macros */ +#define GLOBAL_SCORE_REFRESH_TIME (15 * 60 * 60) + #define TOOLFAIL(string) \ if (toolerror()) SysFailMgr(toolerror(), (Pointer)"\p" string "\n\r Error Code -> $"); @@ -38,6 +40,11 @@ unsigned int randomSeed; // This symbol is used also from assembly directly so be careful with it... char globalScoreInfo[GAME_NUM_TILES_WIDE + 1]; +tNSGSHighScores globalHighScores; +Boolean hasGlobalHighScores = FALSE; +Word globalScoreAge = 0; +unsigned int scoreIndex = 0; + /* Implementation */ @@ -50,7 +57,7 @@ word randomMushroomOffset(void) } -void uploadSpin(int val) +static void uploadSpin(int val) { switch (val) { @@ -73,7 +80,7 @@ void uploadSpin(int val) } -void scorePosition(unsigned int position, unsigned int numberOfScores) +static void scorePosition(unsigned int position, unsigned int numberOfScores) { int i; @@ -87,16 +94,118 @@ void scorePosition(unsigned int position, unsigned int numberOfScores) for (i = 6 * 60; i > 0; i--) { waitForVbl(); } + + globalScoreAge = 0; } -void showConnectionString(BOOLEAN display) +static void showConnectionString(BOOLEAN display) { if (display) displayConnectionString(); } +static char hexDigitToAscii(Word digit) +{ + digit &= 0xf; + if (digit < 10) + return '0' + digit; + + if (digit > 15) + return 'X'; + + return 'A' + digit - 10; +} + + +static void displayString(Word row, char * string) +{ + strcpy(&(globalHighScores.score[row].scoreText[2]), string); +} + +static void displayNetworkError(char * line1, char * line2, unsigned int errorCode) +{ + Word row; + Word column; + + for (row = 0; row < sizeof(globalHighScores.score) / sizeof(globalHighScores.score[0]); row++) { + for (column = 0; + column < sizeof(globalHighScores.score[0].scoreText) / sizeof(globalHighScores.score[0].scoreText[0]); + column++) { + globalHighScores.score[row].scoreText[column] = ' '; + } + for (column = 0; + column < sizeof(globalHighScores.score[0].who) / sizeof(globalHighScores.score[0].who[0]); + column++) { + globalHighScores.score[row].who[column] = ' '; + } + } + + displayString(1, "NETWORK"); + displayString(2, "PROBLEM"); + + displayString(4, line1); + displayString(5, line2); + + globalHighScores.score[7].scoreText[0] = 'C'; + globalHighScores.score[7].scoreText[1] = 'O'; + globalHighScores.score[7].scoreText[2] = 'D'; + globalHighScores.score[7].scoreText[3] = 'E'; + globalHighScores.score[7].scoreText[4] = ':'; + globalHighScores.score[7].scoreText[5] = ' '; + globalHighScores.score[7].scoreText[6] = hexDigitToAscii(errorCode >> 12); + globalHighScores.score[7].scoreText[7] = hexDigitToAscii(errorCode >> 8); + globalHighScores.score[7].scoreText[8] = hexDigitToAscii(errorCode >> 4); + globalHighScores.score[7].scoreText[9] = hexDigitToAscii(errorCode); + + hasGlobalHighScores = TRUE; + globalScoreAge = 0; +} + +static void displayError(tNSGSErrorType errorType, unsigned int errorCode) +{ + switch (errorType) { + case NSGS_CONNECT_ERROR: + displayNetworkError("CONNECT", "FAILED", errorCode); + break; + + case NSGS_LOOKUP_ERROR: + displayNetworkError("LOOKUP", "FAILED", errorCode); + break; + + case NSGS_SOCKET_ERROR: + displayNetworkError("SOCKET", "ERROR", errorCode); + break; + + case NSGS_PROTOCOL_ERROR: + displayNetworkError("PROTOCOL", "FAILED", errorCode); + break; + } +} + + +static void setHighScores(const tNSGSHighScores * highScores) +{ + memcpy(&globalHighScores, highScores, sizeof(globalHighScores)); + hasGlobalHighScores = TRUE; + globalScoreAge = GLOBAL_SCORE_REFRESH_TIME; +} + + +static BOOLEAN shouldRefreshHighScores(void) +{ + return globalScoreAge == 0; +} + + +BOOLEAN sendHighScore(void) +{ + const tNSGSHighScore * highScore = getHighScore(scoreIndex / sizeof(tNSGSHighScore)); + return NSGS_SendHighScore(highScore->who, highScore->score); +} + + int main(void) { static tNSGSHighScoreInitParams highScoreInitParams; @@ -137,6 +246,8 @@ int main(void) highScoreInitParams.waitForVbl = waitForVbl; highScoreInitParams.uploadSpin = uploadSpin; highScoreInitParams.scorePosition = scorePosition; + highScoreInitParams.displayError = displayError; + highScoreInitParams.setHighScores = setHighScores; NSGS_InitNetwork(&highScoreInitParams); diff --git a/BuGS/netScores.c b/BuGS/netScores.c index 0a19ee6..e608951 100644 --- a/BuGS/netScores.c +++ b/BuGS/netScores.c @@ -29,7 +29,6 @@ #define RESPONSE_TYPE_SCORES 1 #define RESPONSE_TYPE_STATUS 2 -#define GLOBAL_SCORE_REFRESH_TIME (15 * 60 * 60) #define SHUTDOWN_NETWORK_TIMEOUT (2 * 60) #define TCP_CONNECT_TIMEOUT (8 * 60) #define READ_NETWORK_TIMEOUT (5 * 60) @@ -89,21 +88,6 @@ typedef struct tStatusResponse { } tStatusResponse; -typedef enum tProtocolErrors { - TCP_CONNECT_TIMEOUT_ERROR = 1, - HELLO_TIMEOUT_ERROR, - HELLO_TOO_BIG_ERROR, - HELLO_UNEXPECTED_RESPONSE_ERROR, - HIGH_SCORE_TIMEOUT_ERROR, - HIGH_SCORE_TOO_BIG_ERROR, - HIGH_SCORE_UNEXPECTED_RESPONSE_ERROR, - SET_SCORE_TIMEOUT_ERROR, - SET_SCORE_TOO_BIG_ERROR, - SET_SCORE_UNEXPECTED_RESPONSE_ERROR, - SET_SCORE_FAILED_ERROR, -} tProtocolErrors; - - typedef enum tGameNetworkState { GAME_NETWORK_CONNECT_FAILED = 0, GAME_NETWORK_UNCONNECTED, @@ -155,10 +139,13 @@ typedef struct tGameNetworkGlobals { md5WorkBlock hashWorkBlock; uint32_t secrets[3]; tHighScoreRequestWithHash highScoreRequest; + tScoresResponse highScoreResponse; Boolean hasHighScoreToSend; + tSetHighScoreRequestWithHash setHighScoreRequest; tStatusResponse setHighScoreResponse; uint16_t errorCode; uint16_t timeout; + Boolean hasGlobalHighScores; } tGameNetworkGlobals; // Forward declarations @@ -216,14 +203,6 @@ static tGameNetworkStateHandler handlers[NUM_GAME_NETWORK_STATES] = { // detected. static tGameNetworkGlobals * networkGlobals = NULL; -// The following globals are accessed by name from assembly so are not in the -// tGameNetworkGlobals structure. -// TODO - Make real interfaces for these things. -Boolean hasGlobalHighScores = FALSE; -tScoresResponse highScoreResponse; -Word globalScoreAge = 0; // TODO - Replace this with a call to a MiscTool function? -tSetHighScoreRequestWithHash setHighScoreRequest; - // Implementation @@ -284,8 +263,9 @@ void NSGS_InitNetwork(tNSGSHighScoreInitParams * params) networkGlobals->secrets[1] = params->secret2; networkGlobals->hasHighScoreToSend = FALSE; + networkGlobals->hasGlobalHighScores = FALSE; - setHighScoreRequest.setHighScoreRequest.is60Hz = !ReadBParam(hrtz50or60); + networkGlobals->setHighScoreRequest.setHighScoreRequest.is60Hz = !ReadBParam(hrtz50or60); } @@ -324,76 +304,21 @@ void NSGS_DisconnectNetwork(void) networkGlobals->timeout = SHUTDOWN_NETWORK_TIMEOUT; } - while (networkGlobals->gameNetworkState > GAME_NETWORK_TCP_UNCONNECTED) { - networkGlobals->initParams.waitForVbl(); - NSGS_PollNetwork(); - } + } + + while ((networkGlobals->gameNetworkState != GAME_NETWORK_TCP_UNCONNECTED) && + (networkGlobals->gameNetworkState != GAME_NETWORK_FAILURE)) { + networkGlobals->initParams.waitForVbl(); + NSGS_PollNetwork(); } } -static char hexDigitToAscii(Word digit) -{ - digit &= 0xf; - if (digit < 10) - return '0' + digit; - - if (digit > 15) - return 'X'; - - return 'A' + digit - 10; -} - - -static void displayString(Word row, char * string) -{ - strcpy(&(highScoreResponse.highScores.score[row].scoreText[2]), string); -} - -static void displayNetworkError(char * line1, char * line2) -{ - Word row; - Word column; - - networkGlobals->gameNetworkState = GAME_NETWORK_FAILURE; - - for (row = 0; row < sizeof(highScoreResponse.highScores) / sizeof(highScoreResponse.highScores.score[0]); row++) { - for (column = 0; - column < sizeof(highScoreResponse.highScores.score[0].scoreText) / sizeof(highScoreResponse.highScores.score[0].scoreText[0]); - column++) { - highScoreResponse.highScores.score[row].scoreText[column] = ' '; - } - for (column = 0; - column < sizeof(highScoreResponse.highScores.score[0].who) / sizeof(highScoreResponse.highScores.score[0].who[0]); - column++) { - highScoreResponse.highScores.score[row].who[column] = ' '; - } - } - - displayString(1, "NETWORK"); - displayString(2, "PROBLEM"); - - displayString(4, line1); - displayString(5, line2); - - highScoreResponse.highScores.score[7].scoreText[0] = 'C'; - highScoreResponse.highScores.score[7].scoreText[1] = 'O'; - highScoreResponse.highScores.score[7].scoreText[2] = 'D'; - highScoreResponse.highScores.score[7].scoreText[3] = 'E'; - highScoreResponse.highScores.score[7].scoreText[4] = ':'; - highScoreResponse.highScores.score[7].scoreText[5] = ' '; - highScoreResponse.highScores.score[7].scoreText[6] = hexDigitToAscii(networkGlobals->errorCode >> 12); - highScoreResponse.highScores.score[7].scoreText[7] = hexDigitToAscii(networkGlobals->errorCode >> 8); - highScoreResponse.highScores.score[7].scoreText[8] = hexDigitToAscii(networkGlobals->errorCode >> 4); - highScoreResponse.highScores.score[7].scoreText[9] = hexDigitToAscii(networkGlobals->errorCode); - - globalScoreAge = 0; - hasGlobalHighScores = TRUE; -} - static void handleConnectFailed(void) { - displayNetworkError("CONNECT", "FAILED"); + if (networkGlobals->initParams.displayError != NULL) + networkGlobals->initParams.displayError(NSGS_CONNECT_ERROR, networkGlobals->errorCode); + networkGlobals->gameNetworkState = GAME_NETWORK_FAILURE; } static void handleUnconnected(void) @@ -406,6 +331,7 @@ static void handleUnconnected(void) networkGlobals->gameNetworkState = GAME_NETWORK_CONNECTED; } else { networkGlobals->gameNetworkState = GAME_NETWORK_CONNECT_FAILED; + networkGlobals->errorCode = toolerror(); } if (networkGlobals->initParams.displayConnectionString != NULL) networkGlobals->initParams.displayConnectionString(FALSE); @@ -437,19 +363,25 @@ static void handleResolvingName(void) static void handleLookupFailed(void) { - displayNetworkError("LOOKUP", "FAILED"); + if (networkGlobals->initParams.displayError != NULL) + networkGlobals->initParams.displayError(NSGS_LOOKUP_ERROR, networkGlobals->errorCode); + networkGlobals->gameNetworkState = GAME_NETWORK_FAILURE; } static void handleSocketError(void) { - displayNetworkError("SOCKET", "ERROR"); + if (networkGlobals->initParams.displayError != NULL) + networkGlobals->initParams.displayError(NSGS_SOCKET_ERROR, networkGlobals->errorCode); + networkGlobals->gameNetworkState = GAME_NETWORK_FAILURE; networkGlobals->timeout = NETWORK_RETRY_TIMEOUT; } static void handleProtocolFailed(void) { abortConnection(); - displayNetworkError("PROTOCOL", "FAILED"); + if (networkGlobals->initParams.displayError != NULL) + networkGlobals->initParams.displayError(NSGS_PROTOCOL_ERROR, networkGlobals->errorCode); + networkGlobals->gameNetworkState = GAME_NETWORK_FAILURE; networkGlobals->timeout = NETWORK_RETRY_TIMEOUT; } @@ -466,10 +398,14 @@ static void handleFailure(void) static void handleTcpUnconnected(void) { - if ((hasGlobalHighScores) && - (globalScoreAge > 0) && - (!networkGlobals->hasHighScoreToSend)) - return; + if ((networkGlobals->hasGlobalHighScores) && + (!networkGlobals->hasHighScoreToSend)) { + if (networkGlobals->initParams.shouldRefreshHighScores == NULL) + return; + + if (!networkGlobals->initParams.shouldRefreshHighScores()) + return; + } networkGlobals->ipid = TCPIPLogin(networkGlobals->initParams.userId, networkGlobals->domainNameResolution.DNRIPaddress, networkGlobals->initParams.scorePort, 0, 64); if (toolerror()) { @@ -502,7 +438,7 @@ static void handleWaitingForTcp(void) networkGlobals->timeout--; } else { networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; - networkGlobals->errorCode = TCP_CONNECT_TIMEOUT_ERROR; + networkGlobals->errorCode = NSGS_TCP_CONNECT_TIMEOUT_ERROR; } return; } @@ -537,28 +473,29 @@ static void handleWaitingForHello(void) networkGlobals->timeout--; } else { networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; - networkGlobals->errorCode = HELLO_TIMEOUT_ERROR; + networkGlobals->errorCode = NSGS_HELLO_TIMEOUT_ERROR; } return; } if (networkGlobals->bytesRead > sizeof(networkGlobals->helloResponse)) { networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; - networkGlobals->errorCode = HELLO_TOO_BIG_ERROR; + networkGlobals->errorCode = NSGS_HELLO_TOO_BIG_ERROR; return; } if (networkGlobals->helloResponse.responseType != RESPONSE_TYPE_HELLO) { networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; - networkGlobals->errorCode = HELLO_UNEXPECTED_RESPONSE_ERROR; + networkGlobals->errorCode = NSGS_HELLO_UNEXPECTED_RESPONSE_ERROR; return; } networkGlobals->secrets[2] = networkGlobals->helloResponse.nonce; if (networkGlobals->hasHighScoreToSend) { networkGlobals->gameNetworkState = GAME_NETWORK_SET_HIGH_SCORE; - } else if ((!hasGlobalHighScores) || - (globalScoreAge == 0)) { + } else if ((!networkGlobals->hasGlobalHighScores) || + ((networkGlobals->initParams.shouldRefreshHighScores != NULL) && + (networkGlobals->initParams.shouldRefreshHighScores()))) { networkGlobals->gameNetworkState = GAME_NETWORK_REQUEST_SCORES; } else { TCPIPCloseTCP(networkGlobals->ipid); @@ -591,8 +528,8 @@ static void handleRequestScores(void) static void handleWaitingForScores(void) { networkGlobals->errorCode = TCPIPReadTCP(networkGlobals->ipid, 0, - ((uint32_t)(&highScoreResponse)) + networkGlobals->bytesRead, - sizeof(highScoreResponse) - networkGlobals->bytesRead, + ((uint32_t)(&networkGlobals->highScoreResponse)) + networkGlobals->bytesRead, + sizeof(networkGlobals->highScoreResponse) - networkGlobals->bytesRead, &(networkGlobals->readResponseBuf)); if (networkGlobals->errorCode != tcperrOK) { abortConnection(); @@ -601,30 +538,31 @@ static void handleWaitingForScores(void) } networkGlobals->bytesRead += networkGlobals->readResponseBuf.rrBuffCount; - if (networkGlobals->bytesRead < sizeof(highScoreResponse)) { + if (networkGlobals->bytesRead < sizeof(networkGlobals->highScoreResponse)) { if (networkGlobals->timeout > 0) { networkGlobals->timeout--; } else { networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; - networkGlobals->errorCode = HIGH_SCORE_TIMEOUT_ERROR; + networkGlobals->errorCode = NSGS_HIGH_SCORE_TIMEOUT_ERROR; } return; } - if (networkGlobals->bytesRead > sizeof(highScoreResponse)) { + if (networkGlobals->bytesRead > sizeof(networkGlobals->highScoreResponse)) { networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; - networkGlobals->errorCode = HIGH_SCORE_TOO_BIG_ERROR; + networkGlobals->errorCode = NSGS_HIGH_SCORE_TOO_BIG_ERROR; return; } - if (highScoreResponse.responseType != RESPONSE_TYPE_SCORES) { + if (networkGlobals->highScoreResponse.responseType != RESPONSE_TYPE_SCORES) { networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; - networkGlobals->errorCode = HIGH_SCORE_UNEXPECTED_RESPONSE_ERROR; + networkGlobals->errorCode = NSGS_HIGH_SCORE_UNEXPECTED_RESPONSE_ERROR; return; } - globalScoreAge = GLOBAL_SCORE_REFRESH_TIME; - hasGlobalHighScores = TRUE; + networkGlobals->hasGlobalHighScores = TRUE; + if (networkGlobals->initParams.setHighScores != NULL) + networkGlobals->initParams.setHighScores(&(networkGlobals->highScoreResponse.highScores)); TCPIPCloseTCP(networkGlobals->ipid); networkGlobals->gameNetworkState = GAME_NETWORK_CLOSING_TCP; @@ -633,15 +571,15 @@ static void handleWaitingForScores(void) static void handleSetHighScore(void) { - setHighScoreRequest.setHighScoreRequest.requestType = REQUEST_TYPE_SET_SCORE; - setHighScoreRequest.setHighScoreRequest.who[3] = '\0'; + networkGlobals->setHighScoreRequest.setHighScoreRequest.requestType = REQUEST_TYPE_SET_SCORE; + networkGlobals->setHighScoreRequest.setHighScoreRequest.who[3] = '\0'; md5Init(&(networkGlobals->hashWorkBlock)); md5Append(&(networkGlobals->hashWorkBlock), (Pointer)&(networkGlobals->secrets), sizeof(networkGlobals->secrets)); - md5Append(&(networkGlobals->hashWorkBlock), (Pointer)&(setHighScoreRequest.setHighScoreRequest), sizeof(setHighScoreRequest.setHighScoreRequest)); - md5Finish(&(networkGlobals->hashWorkBlock), (Pointer)&(setHighScoreRequest.md5Digest[0])); + md5Append(&(networkGlobals->hashWorkBlock), (Pointer)&(networkGlobals->setHighScoreRequest.setHighScoreRequest), sizeof(networkGlobals->setHighScoreRequest.setHighScoreRequest)); + md5Finish(&(networkGlobals->hashWorkBlock), (Pointer)&(networkGlobals->setHighScoreRequest.md5Digest[0])); - networkGlobals->errorCode = TCPIPWriteTCP(networkGlobals->ipid, (Pointer)&setHighScoreRequest, sizeof(setHighScoreRequest), FALSE, FALSE); + networkGlobals->errorCode = TCPIPWriteTCP(networkGlobals->ipid, (Pointer)&(networkGlobals->setHighScoreRequest), sizeof(networkGlobals->setHighScoreRequest), FALSE, FALSE); if (networkGlobals->errorCode != tcperrOK) { abortConnection(); networkGlobals->gameNetworkState = GAME_NETWORK_SOCKET_ERROR; @@ -671,31 +609,30 @@ static void handleWaitingForScoreAck(void) networkGlobals->timeout--; } else { networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; - networkGlobals->errorCode = SET_SCORE_TIMEOUT_ERROR; + networkGlobals->errorCode = NSGS_SET_SCORE_TIMEOUT_ERROR; } return; } if (networkGlobals->bytesRead > sizeof(networkGlobals->setHighScoreResponse)) { networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; - networkGlobals->errorCode = SET_SCORE_TOO_BIG_ERROR; + networkGlobals->errorCode = NSGS_SET_SCORE_TOO_BIG_ERROR; return; } if (networkGlobals->setHighScoreResponse.responseType != RESPONSE_TYPE_STATUS) { networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; - networkGlobals->errorCode = SET_SCORE_UNEXPECTED_RESPONSE_ERROR; + networkGlobals->errorCode = NSGS_SET_SCORE_UNEXPECTED_RESPONSE_ERROR; return; } if (!networkGlobals->setHighScoreResponse.success) { networkGlobals->gameNetworkState = GAME_NETWORK_PROTOCOL_FAILED; - networkGlobals->errorCode = SET_SCORE_FAILED_ERROR; + networkGlobals->errorCode = NSGS_SET_SCORE_FAILED_ERROR; return; } networkGlobals->hasHighScoreToSend = FALSE; - globalScoreAge = 0; networkGlobals->gameNetworkState = GAME_NETWORK_REQUEST_SCORES; } @@ -744,11 +681,16 @@ BOOLEAN NSGS_CanSendHighScore(void) return TRUE; } -BOOLEAN NSGS_SendHighScore(void) +BOOLEAN NSGS_SendHighScore(const char * who, unsigned long score) { uint16_t cycleCount = 0; + if (strlen(who) != 3) + return FALSE; + networkGlobals->hasHighScoreToSend = TRUE; + memcpy(networkGlobals->setHighScoreRequest.setHighScoreRequest.who, who, sizeof(networkGlobals->setHighScoreRequest.setHighScoreRequest.who)); + networkGlobals->setHighScoreRequest.setHighScoreRequest.score = score; if (networkGlobals->gameNetworkState < GAME_NETWORK_TCP_UNCONNECTED) networkGlobals->gameNetworkState = GAME_NETWORK_TCP_UNCONNECTED; diff --git a/BuGS/netScores.h b/BuGS/netScores.h index 71c481f..229ee49 100644 --- a/BuGS/netScores.h +++ b/BuGS/netScores.h @@ -12,6 +12,29 @@ #include +typedef enum tNSGSErrorType +{ + NSGS_CONNECT_ERROR, + NSGS_LOOKUP_ERROR, + NSGS_SOCKET_ERROR, + NSGS_PROTOCOL_ERROR +} tNSGSErrorType; + + +typedef enum ttNSGSProtocolErrors { + NSGS_TCP_CONNECT_TIMEOUT_ERROR = 1, + NSGS_HELLO_TIMEOUT_ERROR, + NSGS_HELLO_TOO_BIG_ERROR, + NSGS_HELLO_UNEXPECTED_RESPONSE_ERROR, + NSGS_HIGH_SCORE_TIMEOUT_ERROR, + NSGS_HIGH_SCORE_TOO_BIG_ERROR, + NSGS_HIGH_SCORE_UNEXPECTED_RESPONSE_ERROR, + NSGS_SET_SCORE_TIMEOUT_ERROR, + NSGS_SET_SCORE_TOO_BIG_ERROR, + NSGS_SET_SCORE_UNEXPECTED_RESPONSE_ERROR, + NSGS_SET_SCORE_FAILED_ERROR, +} ttNSGSProtocolErrors; + typedef struct tNSGSHighScore { @@ -75,6 +98,22 @@ typedef struct tNSGSHighScoreInitParams called by NSGS_SendHighScore(). */ void (*scorePosition)(unsigned int position, unsigned int numberOfScores); + + /* This function is only called from NSGS_PollNetwork() when something unexpected has occurred. + The meaning of the error code depends on the error type. For a protocol error, the error code + is one of tNSGSProtocolErrors. For other error codes, they come from Marinetti error codes. + if the error code > $8000, then the error code is the socket state or-ed with $8000. */ + void (*displayError)(tNSGSErrorType errorType, unsigned int errorCode); + + /* This function is only called from NSGS_PollNetwork() when new scores have been downloaded. + The scores passed should be copied because the pointer is not guaranteed to be valid after + the callback returns. */ + void (*setHighScores)(const tNSGSHighScores * scores); + + /* This function is called to ask if it is time to refresh the global high score list. This should + be based on watching the elapsed time and it should return true if say 5 minutes has elapsed + since high scores have been updated. */ + BOOLEAN (*shouldRefreshHighScores)(void); } tNSGSHighScoreInitParams; @@ -117,7 +156,7 @@ extern BOOLEAN NSGS_CanSendHighScore(void); TODO - Pass the score as an argument rather than through globals. */ -extern BOOLEAN NSGS_SendHighScore(void); +extern BOOLEAN NSGS_SendHighScore(const char * who, unsigned long score); #endif /* define _GUARD_PROJECTNetScoresGS_FILEnetScores_ */ diff --git a/BuGS/score.s b/BuGS/score.s index 91cdcb0..2db6365 100644 --- a/BuGS/score.s +++ b/BuGS/score.s @@ -438,10 +438,8 @@ checkHighScore_doneCopy anop tax lda gameScore,x sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_OFFSET,y - sta setHighScoreRequest+6 lda gameScore+2,x sta settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_SCORE_OFFSET+2,y - sta setHighScoreRequest+8 lda playerNum cmp #PLAYER_ONE @@ -782,10 +780,6 @@ checkHighScore_isInvalid anop checkHighScore_doneInitials anop _overwriteGameTile TILE_EMPTY - lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_WHO_OFFSET-3,y - sta setHighScoreRequest+2 - lda settings+SETTINGS_HIGH_SCORE_OFFSET+HIGH_SCORE_WHO_OFFSET-1,y - sta setHighScoreRequest+4 jsl saveSettings jsl NSGS_CanSendHighScore bne checkHighScore_retry @@ -814,7 +808,7 @@ checkHighScore_retry anop _overwriteGameTile TILE_EMPTY _overwriteGameTile TILE_EMPTY _overwriteGameTile TILE_EMPTY - jsl NSGS_SendHighScore + jsl sendHighScore beq checkHighScore_retryPrompt brl checkHighScore_doneNetwork diff --git a/BuGS/settings.c b/BuGS/settings.c index d9f2ae7..f283c4b 100644 --- a/BuGS/settings.c +++ b/BuGS/settings.c @@ -69,6 +69,14 @@ static Handle filenameHandle = NULL; segment "settings"; #endif +const tNSGSHighScore * getHighScore(unsigned int index) +{ + if (index >= NUM_HIGH_SCORES) + return NULL; + + return &(settings.highScores[index]); +} + static void allocateFilenameHandle(void) { if (filenameHandle == NULL) diff --git a/BuGS/settings.h b/BuGS/settings.h index dd40ae5..6ee2ebc 100644 --- a/BuGS/settings.h +++ b/BuGS/settings.h @@ -12,11 +12,15 @@ #include +typedef struct tNSGSHighScore tNSGSHighScore; + extern unsigned int myUserId; extern void saveSettings(void); -BOOLEAN loadSettings(void); +extern BOOLEAN loadSettings(void); extern void swapStereoSettings(void); +extern const tNSGSHighScore * getHighScore(unsigned int index); + #endif /* define _GUARD_PROJECTBuGS_FILEsettings_ */