More work isolating the global high score code from the game so it can be made a generic library.

This commit is contained in:
Jeremy Rand 2022-05-25 22:58:53 -04:00
parent b8b9089aa2
commit 37faafb698
6 changed files with 137 additions and 36 deletions

View File

@ -9,6 +9,12 @@
#ifndef _GUARD_PROJECTBuGS_FILEgame_ #ifndef _GUARD_PROJECTBuGS_FILEgame_
#define _GUARD_PROJECTBuGS_FILEgame_ #define _GUARD_PROJECTBuGS_FILEgame_
#include "tileData.h"
// These are globals used from assembly.
extern char globalScoreInfo[GAME_NUM_TILES_WIDE + 1];
// These are assembly functions called from C. // These are assembly functions called from C.
extern void game(void); extern void game(void);
@ -18,6 +24,7 @@ extern void uploadSpin1(void);
extern void uploadSpin2(void); extern void uploadSpin2(void);
extern void uploadSpin3(void); extern void uploadSpin3(void);
extern void displayConnectionString(void); extern void displayConnectionString(void);
extern void displayScorePosition(void);
extern void swapStereoChannels(void); extern void swapStereoChannels(void);

View File

@ -223,7 +223,6 @@ updateScorpion_explosionDone anop
setScorpionSpeed entry setScorpionSpeed entry
; TODO - Call this code with each level to set the scorpion speed
cmp #SPRITE_SPEED_FAST cmp #SPRITE_SPEED_FAST
beq setScorpionSpeed_fast beq setScorpionSpeed_fast

View File

@ -23,12 +23,14 @@ _drawDirtyGameRow_wait&rowNum anop
; offset numbers. ; offset numbers.
; ;
; Also according to that technote, it looks like these numbers are different ; Also according to that technote, it looks like these numbers are different
; in PAL mode. TODO - Do I need something here to handle PAL correctly? I ; in PAL mode. Do I need something here to handle PAL correctly? I switched
; switched my GS to 50Hz mode (startup/reboot with option held down to get the ; my GS to 50Hz mode (startup/reboot with option held down to get the menu)
; menu) and I didnt't detect any graphics glitches at all. I did notice that ; and I didnt't detect any graphics glitches at all. I did notice that the
; the game is noticably easier because things run a bit slower. If I do an ; game is noticably easier because things run a bit slower. If I do an online
; online score system, I should record whether a score was gotten at 50Hz vs ; score system, I should record whether a score was gotten at 50Hz vs 60Hz.
; 60Hz. ; (and in fact, I have since implemented online high scores and record the
; frequency at which the game was played)
lda >VERTICAL_COUNTER ; load the counter value lda >VERTICAL_COUNTER ; load the counter value
and #$80ff ; mask out the VBL bits and #$80ff ; mask out the VBL bits
asl a ; shift the word around asl a ; shift the word around

View File

@ -140,6 +140,7 @@ typedef enum tGameNetworkState {
typedef struct tGameNetworkGlobals { typedef struct tGameNetworkGlobals {
Boolean networkStartedConnected; Boolean networkStartedConnected;
tHighScoreInitParams initParams;
tGameNetworkState gameNetworkState; tGameNetworkState gameNetworkState;
dnrBuffer domainNameResolution; dnrBuffer domainNameResolution;
srBuff tcpStatus; srBuff tcpStatus;
@ -152,7 +153,6 @@ typedef struct tGameNetworkGlobals {
tHighScoreRequestWithHash highScoreRequest; tHighScoreRequestWithHash highScoreRequest;
Boolean hasHighScoreToSend; Boolean hasHighScoreToSend;
tStatusResponse setHighScoreResponse; tStatusResponse setHighScoreResponse;
tHighScoreInitParams initParams;
uint16_t errorCode; uint16_t errorCode;
uint16_t timeout; uint16_t timeout;
} tGameNetworkGlobals; } tGameNetworkGlobals;
@ -167,11 +167,11 @@ static tGameNetworkGlobals * networkGlobals = NULL;
// The following globals are accessed by name from assembly so are not in the // The following globals are accessed by name from assembly so are not in the
// tGameNetworkGlobals structure. // tGameNetworkGlobals structure.
// TODO - Make real interfaces for these things.
Boolean hasGlobalHighScores = FALSE; Boolean hasGlobalHighScores = FALSE;
tScoresResponse highScoreResponse; tScoresResponse highScoreResponse;
Word globalScoreAge = 0; Word globalScoreAge = 0; // TODO - Replace this with a call to a MiscTool function?
tSetHighScoreRequestWithHash setHighScoreRequest; tSetHighScoreRequestWithHash setHighScoreRequest;
char globalScoreInfo[26];
// Implementation // Implementation
@ -383,7 +383,7 @@ void pollNetwork(void)
case GAME_NETWORK_UNCONNECTED: case GAME_NETWORK_UNCONNECTED:
if (networkGlobals->initParams.displayConnectionString != NULL) if (networkGlobals->initParams.displayConnectionString != NULL)
networkGlobals->initParams.displayConnectionString(); networkGlobals->initParams.displayConnectionString(TRUE);
TCPIPConnect(NULL); TCPIPConnect(NULL);
if ((!toolerror()) && if ((!toolerror()) &&
(TCPIPGetConnectStatus())) { (TCPIPGetConnectStatus())) {
@ -391,6 +391,8 @@ void pollNetwork(void)
} else { } else {
networkGlobals->gameNetworkState = GAME_NETWORK_CONNECT_FAILED; networkGlobals->gameNetworkState = GAME_NETWORK_CONNECT_FAILED;
} }
if (networkGlobals->initParams.displayConnectionString != NULL)
networkGlobals->initParams.displayConnectionString(FALSE);
break; break;
case GAME_NETWORK_CONNECTED: case GAME_NETWORK_CONNECTED:
@ -721,16 +723,9 @@ BOOLEAN sendHighScore(void)
if (networkGlobals->gameNetworkState != GAME_NETWORK_TCP_UNCONNECTED) if (networkGlobals->gameNetworkState != GAME_NETWORK_TCP_UNCONNECTED)
return FALSE; return FALSE;
sprintf(globalScoreInfo, " %u OF %u SCORES", networkGlobals->setHighScoreResponse.position, networkGlobals->setHighScoreResponse.numberOfScores); if (networkGlobals->initParams.scorePosition != NULL)
for (cycleCount = strlen(globalScoreInfo); cycleCount < sizeof(globalScoreInfo); cycleCount++) { networkGlobals->initParams.scorePosition(networkGlobals->setHighScoreResponse.position,
globalScoreInfo[cycleCount] = ' '; networkGlobals->setHighScoreResponse.numberOfScores);
}
globalScoreInfo[25] = '\0'; // TODO - Get rid of hard coded value...
displayScorePosition();
for (cycleCount = 4 * 60; cycleCount > 0; cycleCount--) {
networkGlobals->initParams.waitForVbl();
}
return TRUE; return TRUE;
} }

View File

@ -12,6 +12,7 @@
#include <types.h> #include <types.h>
typedef struct tHighScore typedef struct tHighScore
{ {
char scoreText[10]; char scoreText[10];
@ -19,31 +20,100 @@ typedef struct tHighScore
unsigned long score; unsigned long score;
} tHighScore; } tHighScore;
extern char globalScoreInfo[26]; /* TODO - Get rid of this global and the hard coded length */
typedef struct tHighScoreInitParams typedef struct tHighScoreInitParams
{ {
/* This structure is initialized by the game and passed by pointer to the high score code. It consists of
a series of data members and a series of callbacks which are provided by the game.
*/
/* This is the memory manager ID of the game. */
unsigned int userId; unsigned int userId;
const char * scoreServer; /* Pascal string of the hostname of the score server. */
/* This is a Pascal string (not a C string) of the hostname of the score server to connect to. */
const char * scoreServer;
/* This is the TCP port number of the score server to connect to. */
unsigned int scorePort; unsigned int scorePort;
/* These two 32-bit values are the shared secrets used by the connect between the game and the score server
which is used to try to reduce the amount of game score hacking.
*/
unsigned long secret1; unsigned long secret1;
unsigned long secret2; unsigned long secret2;
void (*displayConnectionString)(void); /* This function should display a message to the user that the network is being brought up. */
void (*waitForVbl)(void); /* This function should wait for the next VBL and is used to poll the network and limit upload time for a high score. */ /* This function should display a message to the user that the network is being brought up and they should
void (*uploadSpin)(int); /* This argument iterates over 0, 1, 2, 3 and then back to 0, 1, 2, etc and is intended to show some kind of spinner to the user be patient when the argument is TRUE and when the argument is FALSE, it should clear that message. This
while uploading a high score. */ function shouldn't block and just put something on the screen to say that the connection is being brought
up or clear that message. It is called sometimes (rarely) by pollNetwork(). It is called with argument
TRUE and will always be called with FALSE sometime after that.
*/
void (*displayConnectionString)(BOOLEAN display);
/* This function should wait for the next VBL and is used to poll the network and limit upload time for a
high score.
*/
void (*waitForVbl)(void);
/* This argument iterates over 0, 1, 2, 3 and then back to 0, 1, 2, etc and is intended to show some kind
of spinner to the user while uploading a high score to the server. This function shouldn't block for
any real amount of time and just cause something on the screen to change to make sure the player doesn't
think something has hung while the upload is in progress. It is called when sendHighScore() is called
by the game.
*/
void (*uploadSpin)(int);
/* When a score is successfully uploaded to the server, this function will be called with the position
of this player's score among the total number of scores recorded for this game. This information
should be displayed to the user. The function can block while this information is being displayed
and that message should be cleaned up before the function returns to the caller. This function is
called by sendHighScore().
*/
void (*scorePosition)(unsigned int position, unsigned int numberOfScores);
} tHighScoreInitParams; } tHighScoreInitParams;
extern void initNetwork(tHighScoreInitParams * params);
extern void disconnectNetwork(void);
extern void pollNetwork(void);
extern void shutdownNetwork(void);
extern BOOLEAN canSendHighScore(void);
extern BOOLEAN sendHighScore(void);
// These are actually assembly functions called from the C code. /* Call this function once at launch. The pointer to the parameters is copied so the structure does not need
extern void displayScorePosition(void); to remain valid after the call to this function.
*/
extern void initNetwork(tHighScoreInitParams * params);
/* Call this when a game is about to start. It will interrupt any network operation in progress and get ready for
a quiet period where polling will stop until the game is over. That way, all CPU time can be focused on the game.
This function may call the waitForVbl() callback.
*/
extern void disconnectNetwork(void);
/* Call this every frame refresh period when a game is _not_ in progress. This does any network operations required
to download high scores. During this function call, the displayConnectionString() callback may be called.
*/
extern void pollNetwork(void);
/* Call this function once when the game is quitting. */
extern void shutdownNetwork(void);
/* Call this function when the player has a high score that should be recorded online. This function will return
TRUE if the network is up and a high score can be sent and if so, the game should call sendHighScore() to send
the high score. If FALSE is returned, then the user is playing while offline and no attempt should be made to
send the high score.
*/
extern BOOLEAN canSendHighScore(void);
/* Assuming canSendHighScore() returned TRUE, the game can call this function to actually try to send the high score
to the server. If this function returns TRUE, then the score was successfully sent to the server. If FALSE
is returned, then an error has occurred. The game can offer the user the option to retry to the upload of the
score and if the user would like to retry, just call sendHighScore() again. During this function call, the
waitForVbl(), uploadSpin() and scorePosition() callbacks may be called.
TODO - Pass the score as an argument rather than through globals.
*/
extern BOOLEAN sendHighScore(void);
#endif /* define _GUARD_PROJECTBuGS_FILEglobalScores_ */ #endif /* define _GUARD_PROJECTBuGS_FILEglobalScores_ */

View File

@ -31,6 +31,9 @@
unsigned int myUserId; unsigned int myUserId;
unsigned int randomSeed; unsigned int randomSeed;
// This symbol is used also from assembly directly so be careful with it...
char globalScoreInfo[GAME_NUM_TILES_WIDE + 1];
/* Implementation */ /* Implementation */
@ -66,6 +69,30 @@ void uploadSpin(int val)
} }
void scorePosition(unsigned int position, unsigned int numberOfScores)
{
int i;
sprintf(globalScoreInfo, " %u OF %u SCORES", position, numberOfScores);
for (i = strlen(globalScoreInfo); i < sizeof(globalScoreInfo); i++) {
globalScoreInfo[i] = ' ';
}
globalScoreInfo[GAME_NUM_TILES_WIDE] = '\0';
displayScorePosition();
for (i = 6 * 60; i > 0; i--) {
waitForVbl();
}
}
void showConnectionString(BOOLEAN display)
{
if (display)
displayConnectionString();
}
int main(void) int main(void)
{ {
static tHighScoreInitParams highScoreInitParams; static tHighScoreInitParams highScoreInitParams;
@ -102,9 +129,10 @@ int main(void)
highScoreInitParams.secret1 = NETWORK_SERVERSECRET1; highScoreInitParams.secret1 = NETWORK_SERVERSECRET1;
highScoreInitParams.secret2 = NETWORK_SERVERSECRET2; highScoreInitParams.secret2 = NETWORK_SERVERSECRET2;
highScoreInitParams.displayConnectionString = displayConnectionString; highScoreInitParams.displayConnectionString = showConnectionString;
highScoreInitParams.waitForVbl = waitForVbl; highScoreInitParams.waitForVbl = waitForVbl;
highScoreInitParams.uploadSpin = uploadSpin; highScoreInitParams.uploadSpin = uploadSpin;
highScoreInitParams.scorePosition = scorePosition;
initNetwork(&highScoreInitParams); initNetwork(&highScoreInitParams);