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_
#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.
extern void game(void);
@ -18,6 +24,7 @@ extern void uploadSpin1(void);
extern void uploadSpin2(void);
extern void uploadSpin3(void);
extern void displayConnectionString(void);
extern void displayScorePosition(void);
extern void swapStereoChannels(void);

View File

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

View File

@ -23,12 +23,14 @@ _drawDirtyGameRow_wait&rowNum anop
; offset numbers.
;
; 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
; switched my GS to 50Hz mode (startup/reboot with option held down to get the
; menu) and I didnt't detect any graphics glitches at all. I did notice that
; the game is noticably easier because things run a bit slower. If I do an
; online score system, I should record whether a score was gotten at 50Hz vs
; 60Hz.
; in PAL mode. Do I need something here to handle PAL correctly? I switched
; my GS to 50Hz mode (startup/reboot with option held down to get the menu)
; and I didnt't detect any graphics glitches at all. I did notice that the
; game is noticably easier because things run a bit slower. If I do an online
; score system, I should record whether a score was gotten at 50Hz vs 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
and #$80ff ; mask out the VBL bits
asl a ; shift the word around

View File

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

View File

@ -12,6 +12,7 @@
#include <types.h>
typedef struct tHighScore
{
char scoreText[10];
@ -19,31 +20,100 @@ typedef struct tHighScore
unsigned long score;
} tHighScore;
extern char globalScoreInfo[26]; /* TODO - Get rid of this global and the hard coded length */
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;
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;
/* 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 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. */
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
while uploading a high score. */
/* This function should display a message to the user that the network is being brought up and they should
be patient when the argument is TRUE and when the argument is FALSE, it should clear that message. This
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;
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.
extern void displayScorePosition(void);
/* Call this function once at launch. The pointer to the parameters is copied so the structure does not need
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_ */

View File

@ -31,6 +31,9 @@
unsigned int myUserId;
unsigned int randomSeed;
// This symbol is used also from assembly directly so be careful with it...
char globalScoreInfo[GAME_NUM_TILES_WIDE + 1];
/* 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)
{
static tHighScoreInitParams highScoreInitParams;
@ -102,9 +129,10 @@ int main(void)
highScoreInitParams.secret1 = NETWORK_SERVERSECRET1;
highScoreInitParams.secret2 = NETWORK_SERVERSECRET2;
highScoreInitParams.displayConnectionString = displayConnectionString;
highScoreInitParams.displayConnectionString = showConnectionString;
highScoreInitParams.waitForVbl = waitForVbl;
highScoreInitParams.uploadSpin = uploadSpin;
highScoreInitParams.scorePosition = scorePosition;
initNetwork(&highScoreInitParams);