922 lines
19 KiB
C++
922 lines
19 KiB
C++
|
//
|
||
|
// A player.
|
||
|
//
|
||
|
|
||
|
#include "always.h"
|
||
|
#include "key.h"
|
||
|
#include "font.h"
|
||
|
#include "game.h"
|
||
|
#include "gamestate.h"
|
||
|
#include "log.h"
|
||
|
#include "net.h"
|
||
|
#include "os.h"
|
||
|
#include "player.h"
|
||
|
#include "server.h"
|
||
|
#include "ship.h"
|
||
|
|
||
|
|
||
|
//
|
||
|
// The keypresses of all the players.
|
||
|
//
|
||
|
|
||
|
PLAYER_Key PLAYER_key[PLAYER_MAX_PLAYERS];
|
||
|
SLONG PLAYER_key_start;
|
||
|
SLONG PLAYER_key_gameturn;
|
||
|
|
||
|
//
|
||
|
// The messages relating to players joining and leaving the game.
|
||
|
//
|
||
|
|
||
|
PLAYER_Message *PLAYER_message[PLAYER_NUM_MESSAGES];
|
||
|
SLONG PLAYER_message_start;
|
||
|
SLONG PLAYER_message_gameturn;
|
||
|
|
||
|
//
|
||
|
// The gamestate into the past.
|
||
|
//
|
||
|
|
||
|
PLAYER_Gamestate PLAYER_gamestate[PLAYER_NUM_GAMESTATES];
|
||
|
SLONG PLAYER_gamestate_start;
|
||
|
SLONG PLAYER_gamestate_gameturn;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Our local SHIP_ship \ PLAYER_key index.
|
||
|
//
|
||
|
|
||
|
SLONG PLAYER_local_index;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
void PLAYER_init()
|
||
|
{
|
||
|
memset(PLAYER_key, 0, sizeof(PLAYER_key ));
|
||
|
memset(PLAYER_message, 0, sizeof(PLAYER_message ));
|
||
|
memset(PLAYER_gamestate, 0, sizeof(PLAYER_gamestate));
|
||
|
|
||
|
PLAYER_key_start = -1;
|
||
|
PLAYER_key_gameturn = -1;
|
||
|
|
||
|
PLAYER_message_start = -1;
|
||
|
PLAYER_message_gameturn = -1;
|
||
|
|
||
|
PLAYER_gamestate_start = -1;
|
||
|
PLAYER_gamestate_gameturn = -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void PLAYER_new_gameturn(SLONG gameturn)
|
||
|
{
|
||
|
if (PLAYER_gamestate_start == -1)
|
||
|
{
|
||
|
PLAYER_gamestate_start = gameturn;
|
||
|
PLAYER_gamestate_gameturn = gameturn;
|
||
|
}
|
||
|
|
||
|
if (WITHIN(gameturn, PLAYER_gamestate_start, PLAYER_gamestate_gameturn))
|
||
|
{
|
||
|
//
|
||
|
// Overwrite an existing gamestate.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// We should never have to remember so long in the past!
|
||
|
//
|
||
|
|
||
|
ASSERT(gameturn >= PLAYER_gamestate_start);
|
||
|
|
||
|
PLAYER_gamestate_gameturn = gameturn;
|
||
|
|
||
|
if (PLAYER_gamestate_start < PLAYER_gamestate_gameturn - (PLAYER_NUM_GAMESTATES - 1))
|
||
|
{
|
||
|
PLAYER_gamestate_start = PLAYER_gamestate_gameturn - (PLAYER_NUM_GAMESTATES - 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Store gamestate.
|
||
|
//
|
||
|
|
||
|
GAMESTATE_store(&PLAYER_gamestate[gameturn & (PLAYER_NUM_GAMESTATES - 1)].gs);
|
||
|
|
||
|
PLAYER_gamestate[gameturn & (PLAYER_NUM_GAMESTATES - 1)].gameturn = gameturn;
|
||
|
}
|
||
|
|
||
|
void PLAYER_restore_gamestate(SLONG gameturn)
|
||
|
{
|
||
|
ASSERT(WITHIN(gameturn, PLAYER_gamestate_start, PLAYER_gamestate_gameturn));
|
||
|
ASSERT(PLAYER_gamestate[gameturn & (PLAYER_NUM_GAMESTATES - 1)].gameturn == gameturn);
|
||
|
|
||
|
GAMESTATE_restore(&PLAYER_gamestate[gameturn & (PLAYER_NUM_GAMESTATES - 1)].gs);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
SLONG PLAYER_create_local(
|
||
|
CBYTE *name,
|
||
|
UBYTE red,
|
||
|
UBYTE green,
|
||
|
UBYTE blue,
|
||
|
float ship_mass,
|
||
|
float ship_power)
|
||
|
{
|
||
|
SLONG num_bytes;
|
||
|
void *data;
|
||
|
|
||
|
//
|
||
|
// Say what we're doing...
|
||
|
//
|
||
|
|
||
|
OS_clear_screen();
|
||
|
OS_scene_begin();
|
||
|
FONT_draw(0.5F, 0.4F, 0xffffff, 1.0F, -1, "Awaiting gamestate...");
|
||
|
OS_scene_end();
|
||
|
OS_show();
|
||
|
|
||
|
//
|
||
|
// Send a message to the server requesting to join the game.
|
||
|
//
|
||
|
|
||
|
LOG_message(0xffffff, "Requesting to joing game");
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
SLONG gameturn;
|
||
|
SERVER_Block_request_join join;
|
||
|
|
||
|
} join_message;
|
||
|
|
||
|
join_message.gameturn = GAME_turn;
|
||
|
join_message.join.type = SERVER_BLOCK_TYPE_REQUEST_JOIN;
|
||
|
join_message.join.red = red;
|
||
|
join_message.join.green = green;
|
||
|
join_message.join.blue = blue;
|
||
|
join_message.join.mass = ship_mass;
|
||
|
join_message.join.power = ship_power;
|
||
|
|
||
|
//
|
||
|
// Send this message guaranteed.
|
||
|
//
|
||
|
|
||
|
NET_player_message_send(sizeof(join_message), &join_message, TRUE);
|
||
|
|
||
|
//
|
||
|
// Wait until the server responds with a copy of gamestate.
|
||
|
//
|
||
|
|
||
|
SLONG start = OS_ticks();
|
||
|
|
||
|
while(1)
|
||
|
{
|
||
|
//
|
||
|
// Keep our gameturns up-to-date.
|
||
|
//
|
||
|
|
||
|
while(GAME_tick < OS_ticks())
|
||
|
{
|
||
|
GAME_process += 1;
|
||
|
GAME_tick += GAME_TICKS_PER_PROCESS;
|
||
|
GAME_turn = GAME_process >> 4;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Poll for the gamestate received message.
|
||
|
//
|
||
|
|
||
|
switch(NET_player_message_receive(&num_bytes, &data))
|
||
|
{
|
||
|
case NET_PLAYER_MESSAGE_NONE:
|
||
|
|
||
|
if (OS_ticks() > start + 4 * 1000)
|
||
|
{
|
||
|
//
|
||
|
// More than 4 seconds!
|
||
|
//
|
||
|
|
||
|
return PLAYER_CREATE_TIMED_OUT;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case NET_PLAYER_MESSAGE_LOST_CONNECTION:
|
||
|
return PLAYER_CREATE_LOST_CONNECTION;
|
||
|
|
||
|
case NET_PLAYER_MESSAGE_FROM_SERVER:
|
||
|
|
||
|
//
|
||
|
// We should only ever get a GAMESTATE message.
|
||
|
//
|
||
|
|
||
|
{
|
||
|
SLONG exit_loop = FALSE;
|
||
|
|
||
|
UBYTE *upto = ((UBYTE *) data) + 4;
|
||
|
SLONG gameturn = ((SLONG *) data)[0];
|
||
|
|
||
|
while(!exit_loop)
|
||
|
{
|
||
|
if (upto - ((UBYTE *) data) >= num_bytes)
|
||
|
{
|
||
|
//
|
||
|
// Finished processing the message.
|
||
|
//
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch(*upto)
|
||
|
{
|
||
|
case SERVER_BLOCK_TYPE_GAMESTATE:
|
||
|
|
||
|
{
|
||
|
SERVER_Block_gamestate *sbg = (SERVER_Block_gamestate *) upto;
|
||
|
|
||
|
LOG_message(0xffffff, "Got gamestate");
|
||
|
|
||
|
//
|
||
|
// Initialise all the state.
|
||
|
//
|
||
|
|
||
|
memset(PLAYER_key, 0, sizeof(PLAYER_key ));
|
||
|
memset(PLAYER_message, 0, sizeof(PLAYER_message ));
|
||
|
memset(PLAYER_gamestate, 0, sizeof(PLAYER_gamestate));
|
||
|
|
||
|
PLAYER_key_start = -1;
|
||
|
PLAYER_key_gameturn = -1;
|
||
|
|
||
|
PLAYER_message_start = -1;
|
||
|
PLAYER_message_gameturn = -1;
|
||
|
|
||
|
PLAYER_gamestate_start = -1;
|
||
|
PLAYER_gamestate_gameturn = -1;
|
||
|
|
||
|
//
|
||
|
// We know one of the gamestates...
|
||
|
//
|
||
|
|
||
|
PLAYER_gamestate_start = sbg->gameturn;
|
||
|
PLAYER_gamestate_gameturn = sbg->gameturn;
|
||
|
|
||
|
PLAYER_gamestate[sbg->gameturn & (PLAYER_NUM_GAMESTATES - 1)].gameturn = sbg->gameturn;
|
||
|
PLAYER_gamestate[sbg->gameturn & (PLAYER_NUM_GAMESTATES - 1)].gs = sbg->gs;
|
||
|
|
||
|
//
|
||
|
// We don't know about any keypresses or messages.
|
||
|
//
|
||
|
|
||
|
PLAYER_key_start = sbg->gameturn;
|
||
|
PLAYER_key_gameturn = sbg->gameturn;
|
||
|
|
||
|
PLAYER_message_start = sbg->gameturn;
|
||
|
PLAYER_message_gameturn = sbg->gameturn;
|
||
|
|
||
|
//
|
||
|
// Restore gamestate.
|
||
|
//
|
||
|
|
||
|
GAMESTATE_restore(&sbg->gs);
|
||
|
|
||
|
//
|
||
|
// This is the gameturn we are upto...
|
||
|
//
|
||
|
|
||
|
{
|
||
|
SLONG dprocess = (sbg->gameturn << 4) - GAME_process;
|
||
|
|
||
|
GAME_turn = sbg->gameturn;
|
||
|
GAME_process = sbg->gameturn << 4;
|
||
|
GAME_tick += dprocess * GAME_TICKS_PER_PROCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This is the order of events...
|
||
|
//
|
||
|
// GAME_turn += 1;
|
||
|
// SHIP_set_active()
|
||
|
// Get keypresses.
|
||
|
// Process game...
|
||
|
//
|
||
|
// So subtract one from the process. Get it?
|
||
|
//
|
||
|
|
||
|
GAME_process -= 1;
|
||
|
GAME_turn -= 1;
|
||
|
GAME_tick -= GAME_TICKS_PER_PROCESS;
|
||
|
|
||
|
PLAYER_key_start -= 1;
|
||
|
PLAYER_key_gameturn -= 1;
|
||
|
|
||
|
PLAYER_message_start -= 1;
|
||
|
PLAYER_message_gameturn -= 1;
|
||
|
|
||
|
goto got_gamestate;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case SERVER_BLOCK_TYPE_PING:
|
||
|
|
||
|
//
|
||
|
// We can ignore these now...
|
||
|
//
|
||
|
|
||
|
upto += sizeof(SERVER_Block_ping);
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ASSERT(0);
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ASSERT(0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
got_gamestate:;
|
||
|
|
||
|
//
|
||
|
// Tell the server that we've got gamestate. Send the data guaranteed.
|
||
|
//
|
||
|
|
||
|
LOG_message(0xffffff, "Sending acknoledgement that I received gamestate.");
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
SLONG gameturn;
|
||
|
SERVER_Block_received_gamestate received;
|
||
|
|
||
|
} got_it;
|
||
|
|
||
|
got_it.gameturn = GAME_turn;
|
||
|
got_it.received.type = SERVER_BLOCK_TYPE_RECEIVED_GAMESTATE;
|
||
|
|
||
|
NET_player_message_send(sizeof(got_it), &got_it, TRUE);
|
||
|
|
||
|
return PLAYER_CREATE_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
SLONG PLAYER_process(SLONG *rollback, SLONG ignore_server_messages)
|
||
|
{
|
||
|
SLONG i;
|
||
|
SLONG j;
|
||
|
SLONG num_bytes;
|
||
|
SLONG rollback_happened;
|
||
|
|
||
|
void *data;
|
||
|
PLAYER_Key *pk;
|
||
|
SHIP_Ship *ss;
|
||
|
|
||
|
UBYTE local;
|
||
|
|
||
|
//
|
||
|
// Rollback only happens we get a different keypress before PLAYER_key_gameturn,
|
||
|
// not GAME_turn. They should only ever be one different!
|
||
|
//
|
||
|
|
||
|
ASSERT(PLAYER_key_gameturn == GAME_turn - 1);
|
||
|
|
||
|
*rollback = PLAYER_key_gameturn;
|
||
|
rollback_happened = FALSE;
|
||
|
|
||
|
//
|
||
|
// Clear out the new keypresses and update the start of the keypress history.
|
||
|
//
|
||
|
|
||
|
while (PLAYER_key_gameturn < GAME_turn)
|
||
|
{
|
||
|
PLAYER_key_gameturn += 1;
|
||
|
|
||
|
for (i = 0; i < PLAYER_MAX_PLAYERS; i++)
|
||
|
{
|
||
|
PLAYER_key[i].key[PLAYER_key_gameturn & (PLAYER_NUM_KEYS - 1)] = 0;
|
||
|
}
|
||
|
|
||
|
if (PLAYER_key_start <= PLAYER_key_gameturn - PLAYER_NUM_KEYS)
|
||
|
{
|
||
|
//
|
||
|
// The circular queue is going to overwrite an old keypress.
|
||
|
//
|
||
|
|
||
|
PLAYER_key_start = PLAYER_key_gameturn - (PLAYER_NUM_KEYS - 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Update the keypress history.
|
||
|
//
|
||
|
|
||
|
local = 0;
|
||
|
|
||
|
if (KEY_on[KEY_Z]) {local |= PLAYER_KEY_LEFT; }
|
||
|
if (KEY_on[KEY_X]) {local |= PLAYER_KEY_RIGHT;}
|
||
|
if (KEY_on[KEY_V]) {local |= PLAYER_KEY_THRUST;}
|
||
|
|
||
|
if (KEY_on[KEY_G])
|
||
|
{
|
||
|
KEY_on[KEY_G] = 0;
|
||
|
|
||
|
local |= PLAYER_KEY_TRACTOR_BEAM;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < PLAYER_MAX_PLAYERS; i++)
|
||
|
{
|
||
|
pk = &PLAYER_key[i];
|
||
|
ss = &SHIP_ship [i];
|
||
|
|
||
|
if (ss->flag & SHIP_FLAG_USED)
|
||
|
{
|
||
|
if (ss->flag & SHIP_FLAG_LOCAL)
|
||
|
{
|
||
|
//
|
||
|
// This is a local player.
|
||
|
//
|
||
|
|
||
|
if (ss->flag & SHIP_FLAG_ACTIVE)
|
||
|
{
|
||
|
pk->key[GAME_turn & (PLAYER_NUM_KEYS - 1)] = local;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// A non-active player can't press his keys.
|
||
|
//
|
||
|
|
||
|
pk->key[GAME_turn & (PLAYER_NUM_KEYS - 1)] = 0;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (ss->flag & SHIP_FLAG_ACTIVE)
|
||
|
{
|
||
|
UBYTE last;
|
||
|
|
||
|
if (ss->active == GAME_turn)
|
||
|
{
|
||
|
//
|
||
|
// The keypresses start off blank.
|
||
|
//
|
||
|
|
||
|
last = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Guess the keys that the remote player has pressed down.
|
||
|
// The last gameturns keypresses are a good starting point!
|
||
|
//
|
||
|
|
||
|
last = pk->key[(GAME_turn - 1) & (PLAYER_NUM_KEYS - 1)];
|
||
|
|
||
|
//
|
||
|
// Assume remote player's key stay the same- except for tractor
|
||
|
// beam, which we assume stays on for only one gameturn.
|
||
|
//
|
||
|
|
||
|
last &= ~PLAYER_KEY_TRACTOR_BEAM;
|
||
|
}
|
||
|
|
||
|
pk->key[GAME_turn & (PLAYER_NUM_KEYS - 1)] = last;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// A non-active player can't press his keys.
|
||
|
//
|
||
|
|
||
|
pk->key[GAME_turn & (PLAYER_NUM_KEYS - 1)] = 0;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Send the server our keypresses.
|
||
|
//
|
||
|
|
||
|
ASSERT(WITHIN(PLAYER_local_index, 0, SHIP_MAX_SHIPS - 1));
|
||
|
|
||
|
if (SHIP_ship[PLAYER_local_index].flag & SHIP_FLAG_ACTIVE)
|
||
|
{
|
||
|
SLONG num_turns;
|
||
|
|
||
|
num_turns = GAME_turn - SHIP_ship[PLAYER_local_index].active + 1;
|
||
|
|
||
|
if (num_turns > 32)
|
||
|
{
|
||
|
num_turns = 32;
|
||
|
}
|
||
|
|
||
|
if (num_turns > 0)
|
||
|
{
|
||
|
PLAYER_Key *pk = &PLAYER_key[PLAYER_local_index];
|
||
|
|
||
|
struct
|
||
|
{
|
||
|
#define PLAYER_MAX_NUM_KEYS 256
|
||
|
|
||
|
SLONG gameturn;
|
||
|
union
|
||
|
{
|
||
|
SERVER_Block_my_keypress_list sbm;
|
||
|
UBYTE room[sizeof(SERVER_Block_my_keypress_list) + 2 * PLAYER_MAX_NUM_KEYS];
|
||
|
};
|
||
|
|
||
|
} my_keys;
|
||
|
|
||
|
my_keys.gameturn = GAME_turn;
|
||
|
my_keys.sbm.type = SERVER_BLOCK_TYPE_MY_KEYPRESS_LIST;
|
||
|
my_keys.sbm.num_keys = num_turns;
|
||
|
my_keys.sbm.ship_index = PLAYER_local_index;
|
||
|
my_keys.sbm.hash = SHIP_ship[PLAYER_local_index].x * SHIP_ship[PLAYER_local_index].y + SHIP_ship[PLAYER_local_index].angle;
|
||
|
|
||
|
for (i = 0; i < num_turns; i++)
|
||
|
{
|
||
|
my_keys.sbm.key[i] = pk->key[(GAME_turn - i) & (PLAYER_NUM_KEYS - 1)];
|
||
|
}
|
||
|
|
||
|
NET_player_message_send(sizeof(SLONG) + sizeof(SERVER_Block_my_keypress_list) + my_keys.sbm.num_keys, &my_keys);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Free up the old wrapped-around messages.
|
||
|
//
|
||
|
|
||
|
while(PLAYER_message_gameturn < GAME_turn)
|
||
|
{
|
||
|
PLAYER_message_gameturn += 1;
|
||
|
|
||
|
if (PLAYER_message_start <= PLAYER_message_gameturn - PLAYER_NUM_MESSAGES)
|
||
|
{
|
||
|
//
|
||
|
// The circular queue is going to overwrite an old message.
|
||
|
//
|
||
|
|
||
|
PLAYER_message_start = PLAYER_message_gameturn - (PLAYER_NUM_MESSAGES - 1);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// free() up the old messages from the last time this
|
||
|
// gameturn was used.
|
||
|
//
|
||
|
|
||
|
while(PLAYER_message[PLAYER_message_gameturn & (PLAYER_NUM_MESSAGES - 1)])
|
||
|
{
|
||
|
void *free_me_up;
|
||
|
|
||
|
free_me_up = PLAYER_message[PLAYER_message_gameturn & (PLAYER_NUM_MESSAGES - 1)];
|
||
|
PLAYER_message[PLAYER_message_gameturn & (PLAYER_NUM_MESSAGES - 1)] = PLAYER_message[PLAYER_message_gameturn & (PLAYER_NUM_MESSAGES - 1)]->next;
|
||
|
|
||
|
free(free_me_up);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!ignore_server_messages)
|
||
|
{
|
||
|
//
|
||
|
// Get the server messages.
|
||
|
//
|
||
|
|
||
|
while(1)
|
||
|
{
|
||
|
//
|
||
|
// Now process network messages. We might have to change the
|
||
|
// keypresses of the remote players.
|
||
|
//
|
||
|
|
||
|
switch(NET_player_message_receive(&num_bytes, &data))
|
||
|
{
|
||
|
case NET_PLAYER_MESSAGE_NONE:
|
||
|
|
||
|
//
|
||
|
// No more messages.
|
||
|
//
|
||
|
|
||
|
goto no_more_messages;
|
||
|
|
||
|
case NET_PLAYER_MESSAGE_LOST_CONNECTION:
|
||
|
|
||
|
//
|
||
|
// Arggh! We can't find the server anymore.
|
||
|
//
|
||
|
|
||
|
*rollback = 0;
|
||
|
rollback_happened = FALSE;
|
||
|
|
||
|
return FALSE;
|
||
|
|
||
|
case NET_PLAYER_MESSAGE_FROM_SERVER:
|
||
|
|
||
|
{
|
||
|
SLONG exit_loop = FALSE;
|
||
|
|
||
|
UBYTE *upto = ((UBYTE *) data) + 4;
|
||
|
SLONG gameturn = ((SLONG *) data)[0];
|
||
|
|
||
|
while(!exit_loop)
|
||
|
{
|
||
|
if (upto - ((UBYTE *) data) >= num_bytes)
|
||
|
{
|
||
|
//
|
||
|
// Finished processing the message.
|
||
|
//
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch(*upto)
|
||
|
{
|
||
|
case SERVER_BLOCK_TYPE_REQUEST_JOIN:
|
||
|
ASSERT(0);
|
||
|
break;
|
||
|
|
||
|
case SERVER_BLOCK_TYPE_NEW_PLAYER:
|
||
|
|
||
|
{
|
||
|
SERVER_Block_new_player *sbnp = (SERVER_Block_new_player *) upto;
|
||
|
|
||
|
LOG_message(0xffffff, "Got new player %d packet", sbnp->ship_index);
|
||
|
|
||
|
//
|
||
|
// Create a new player message structure and put this message in
|
||
|
// the player message queue.
|
||
|
//
|
||
|
|
||
|
PLAYER_Message *pm = (PLAYER_Message *) malloc(sizeof(PLAYER_Message));
|
||
|
|
||
|
pm->new_player = *sbnp;
|
||
|
pm->next = PLAYER_message[GAME_turn & (PLAYER_NUM_MESSAGES - 1)];
|
||
|
PLAYER_message[GAME_turn & (PLAYER_NUM_MESSAGES - 1)] = pm;
|
||
|
|
||
|
LOG_message(0xffff00, "New player active %d", sbnp->active);
|
||
|
|
||
|
upto += sizeof(SERVER_Block_new_player);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case SERVER_BLOCK_TYPE_MY_KEYPRESS_LIST:
|
||
|
ASSERT(0);
|
||
|
break;
|
||
|
|
||
|
case SERVER_BLOCK_TYPE_REMOTE_KEYPRESS_LIST:
|
||
|
|
||
|
{
|
||
|
SLONG i;
|
||
|
SLONG j;
|
||
|
SLONG turn;
|
||
|
UBYTE key;
|
||
|
UBYTE repeat;
|
||
|
|
||
|
SHIP_Ship *ss;
|
||
|
PLAYER_Key *pk;
|
||
|
|
||
|
SERVER_Block_remote_keypress_list *sbk = (SERVER_Block_remote_keypress_list *) upto;
|
||
|
|
||
|
ASSERT(WITHIN(sbk->ship_index, 0, SHIP_MAX_SHIPS - 1));
|
||
|
|
||
|
ss = &SHIP_ship[sbk->ship_index];
|
||
|
|
||
|
if (!(ss->flag & SHIP_FLAG_USED))
|
||
|
{
|
||
|
//
|
||
|
// This is a keypress list for a ship that we haven't
|
||
|
// created yet!
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Overwrite the keypresses we guessed with the keypresses that
|
||
|
// actaully hapenned. If there is a discrepency, then we must
|
||
|
// roll back to that gameturn and reprocess everything.
|
||
|
//
|
||
|
|
||
|
pk = &PLAYER_key[sbk->ship_index];
|
||
|
|
||
|
for (i = 0; i < sbk->num_keys; i++)
|
||
|
{
|
||
|
turn = sbk->gameturn - i;
|
||
|
|
||
|
if (WITHIN(turn, PLAYER_key_start, PLAYER_key_gameturn))
|
||
|
{
|
||
|
if (pk->key[turn & (PLAYER_NUM_KEYS - 1)] != sbk->key[i])
|
||
|
{
|
||
|
if (turn <= *rollback)
|
||
|
{
|
||
|
//
|
||
|
// We guessed wrong! We must rollback.
|
||
|
//
|
||
|
|
||
|
*rollback = turn;
|
||
|
rollback_happened = TRUE;
|
||
|
|
||
|
ASSERT(*rollback < GAME_turn);
|
||
|
|
||
|
if (i == 0)
|
||
|
{
|
||
|
//
|
||
|
// Update the guessing...
|
||
|
//
|
||
|
|
||
|
for (j = sbk->gameturn + 1; j <= GAME_turn; j++)
|
||
|
{
|
||
|
//
|
||
|
// Assume the keys we don't know are the same as the
|
||
|
// last key press we got. Except the tractor beam.
|
||
|
//
|
||
|
|
||
|
pk->key[j & (PLAYER_NUM_KEYS - 1)] = sbk->key[0] & ~PLAYER_KEY_TRACTOR_BEAM;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pk->key[turn & (PLAYER_NUM_KEYS - 1)] = sbk->key[i];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
upto += sizeof(SERVER_Block_remote_keypress_list);
|
||
|
upto += sizeof(UBYTE) * sbk->num_keys;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case SERVER_BLOCK_TYPE_PING:
|
||
|
ASSERT(0);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ASSERT(0);
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ASSERT(0);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
no_more_messages:;
|
||
|
}
|
||
|
|
||
|
if (!rollback_happened)
|
||
|
{
|
||
|
//
|
||
|
// 0 means no rollback.
|
||
|
//
|
||
|
|
||
|
*rollback = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ASSERT(*rollback < GAME_turn);
|
||
|
|
||
|
//
|
||
|
// Rollback gamestate.
|
||
|
//
|
||
|
|
||
|
PLAYER_restore_gamestate(*rollback);
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void PLAYER_process_messages(SLONG gameturn)
|
||
|
{
|
||
|
ASSERT(WITHIN(gameturn, PLAYER_message_start, PLAYER_message_gameturn));
|
||
|
|
||
|
PLAYER_Message *pm = PLAYER_message[gameturn & (PLAYER_NUM_MESSAGES - 1)];
|
||
|
|
||
|
while(pm)
|
||
|
{
|
||
|
switch(pm->type)
|
||
|
{
|
||
|
case SERVER_BLOCK_TYPE_NEW_PLAYER:
|
||
|
|
||
|
{
|
||
|
SHIP_Ship *ss;
|
||
|
|
||
|
ASSERT(WITHIN(pm->new_player.ship_index, 0, SHIP_MAX_SHIPS - 1));
|
||
|
|
||
|
ss = &SHIP_ship[pm->new_player.ship_index];
|
||
|
|
||
|
memset(ss, 0, sizeof(SHIP_Ship));
|
||
|
|
||
|
ss->flag = SHIP_FLAG_USED;
|
||
|
ss->active = pm->new_player.active;
|
||
|
ss->red = pm->new_player.red;
|
||
|
ss->green = pm->new_player.green;
|
||
|
ss->blue = pm->new_player.blue;
|
||
|
ss->mass = pm->new_player.mass;
|
||
|
ss->power = pm->new_player.power;
|
||
|
ss->x = pm->new_player.x;
|
||
|
ss->y = pm->new_player.y;
|
||
|
ss->angle = 0.0F;
|
||
|
|
||
|
if (pm->new_player.local)
|
||
|
{
|
||
|
//
|
||
|
// This is the local player.
|
||
|
//
|
||
|
|
||
|
ss->flag |= SHIP_FLAG_LOCAL;
|
||
|
|
||
|
PLAYER_local_index = pm->new_player.ship_index;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Up until the gameturn before this ship goes active, all it's
|
||
|
// keypresses are zero.
|
||
|
//
|
||
|
|
||
|
memset(&PLAYER_key[pm->new_player.ship_index], 0, sizeof(PLAYER_Key));
|
||
|
|
||
|
PLAYER_key[pm->new_player.ship_index].known = ss->active - 1;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case SERVER_BLOCK_TYPE_PLAYER_LEFT:
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
ASSERT(0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pm = pm->next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void PLAYER_press_keys(SLONG gameturn)
|
||
|
{
|
||
|
SLONG i;
|
||
|
|
||
|
PLAYER_Key *pk;
|
||
|
SHIP_Ship *ss;
|
||
|
|
||
|
for (i = 0; i < PLAYER_MAX_PLAYERS; i++)
|
||
|
{
|
||
|
ss = &SHIP_ship[i];
|
||
|
|
||
|
if (ss->flag & SHIP_FLAG_USED)
|
||
|
{
|
||
|
ss->flag &= ~SHIP_FLAG_KEY_LEFT;
|
||
|
ss->flag &= ~SHIP_FLAG_KEY_RIGHT;
|
||
|
ss->flag &= ~SHIP_FLAG_KEY_THRUST;
|
||
|
ss->flag &= ~SHIP_FLAG_KEY_TRACTOR_BEAM;
|
||
|
|
||
|
if (gameturn < ss->active)
|
||
|
{
|
||
|
//
|
||
|
// No keys presses before is goes active.
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pk = &PLAYER_key[i];
|
||
|
|
||
|
if (pk->key[gameturn & (PLAYER_NUM_KEYS - 1)] & PLAYER_KEY_LEFT ) {ss->flag |= SHIP_FLAG_KEY_LEFT ;}
|
||
|
if (pk->key[gameturn & (PLAYER_NUM_KEYS - 1)] & PLAYER_KEY_RIGHT ) {ss->flag |= SHIP_FLAG_KEY_RIGHT ;}
|
||
|
if (pk->key[gameturn & (PLAYER_NUM_KEYS - 1)] & PLAYER_KEY_THRUST ) {ss->flag |= SHIP_FLAG_KEY_THRUST ;}
|
||
|
if (pk->key[gameturn & (PLAYER_NUM_KEYS - 1)] & PLAYER_KEY_TRACTOR_BEAM) {ss->flag |= SHIP_FLAG_KEY_TRACTOR_BEAM;}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|