MuckyFoot-UrbanChaos/thrust/server.cpp
2017-05-20 11:14:17 +10:00

869 lines
18 KiB
C++

//
// The server.
//
#include "always.h"
#include "font.h"
#include "game.h"
#include "land.h"
#include "log.h"
#include "net.h"
#include "os.h"
#include "player.h"
#include "server.h"
//
// The info we store for each player.
//
#define SERVER_PLAYER_FLAG_USED (1 << 0)
#define SERVER_PLAYER_FLAG_OUT_OF_SYNC (1 << 1)
#define SERVER_PLAYER_MODE_CONNECTED 0 // Just joined.
#define SERVER_PLAYER_MODE_REQUESTED 1 // Requested joining- awaiting gamestate.
#define SERVER_PLAYER_MODE_SENT_GAMESTATE 2 // Has been sent a copy of gamestate.
#define SERVER_PLAYER_MODE_PLAYING 3 // Received gamestate- awaiting activation.
typedef struct
{
UBYTE flag;
UBYTE mode;
UBYTE red;
UBYTE green;
UBYTE blue;
UBYTE ship_index; // The index into the SHIP_ship array.
UBYTE padding[2];
NET_Player player_id;
float mass;
float power;
CBYTE name[32];
//
// The keypresses we know about for sure.
//
#define SERVER_MAX_KEYS 256
UBYTE key[SERVER_MAX_KEYS];
SLONG gameturn; // The gameturn we know all the keys for.
SLONG active; // The gameturn when this ship became active.
float hash; // The hash value at 'gameturn'
} SERVER_Player;
#define SERVER_MAX_PLAYERS PLAYER_MAX_PLAYERS
SERVER_Player SERVER_player[SERVER_MAX_PLAYERS];
//
// The gameturn we've processed the world upto.
//
SLONG SERVER_turn;
//
// Who we are connecting or -1 if we are not connecting anybody.
//
SLONG SERVER_connecting;
SLONG SERVER_session_create(
CBYTE *name,
SLONG max_players,
SLONG connection_type,
CBYTE *internet_address)
{
ASSERT(max_players <= SERVER_MAX_PLAYERS);
switch(connection_type)
{
case SERVER_CONNECT_TYPE_LAN:
if (!NET_connection_lan())
{
return FALSE;
}
break;
case SERVER_CONNECT_TYPE_INTERNET:
return FALSE;
default:
ASSERT(0);
return FALSE;
}
if (NET_session_create(name, max_players))
{
//
// Initialise a new game.
//
//
// No players to start with...
//
memset(SERVER_player, 0, sizeof(SERVER_player));
//
// Start off not connecting anyone.
//
SERVER_connecting = -1;
SERVER_turn = 0;
return TRUE;
}
else
{
return FALSE;
}
}
void SERVER_process()
{
SLONG i;
SLONG j;
SLONG num_bytes;
SERVER_Player *sp;
SERVER_Player *sp_2;
SHIP_Ship *ss;
NET_Player player;
void *data;
switch(NET_server_message_receive(&player, &num_bytes, &data))
{
case NET_SERVER_MESSAGE_NONE:
break;
case NET_SERVER_MESSAGE_LOST_CONNECTION:
LOG_message(0xffffff, "Lost connection");
break;
case NET_SERVER_MESSAGE_PLAYER_LEFT:
LOG_message(0xffffff, "Player %d disconnected", player);
break;
case NET_SERVER_MESSAGE_PLAYER_JOINED:
{
SLONG i;
//
// Look for a free player.
//
for (i = 0; i < SERVER_MAX_PLAYERS; i++)
{
if (SERVER_player[i].flag & SERVER_PLAYER_FLAG_USED)
{
continue;
}
memset(&SERVER_player[i], 0, sizeof(SERVER_Player));
SERVER_player[i].flag = SERVER_PLAYER_FLAG_USED;
SERVER_player[i].player_id = player;
SERVER_player[i].mode = SERVER_PLAYER_MODE_CONNECTED;
LOG_message(0xffffff, "Player %d connected", player);
return;
}
//
// WHAT?!
//
ASSERT(0);
}
break;
case NET_SERVER_MESSAGE_FROM_PLAYER:
// LOG_message(0xffffff, "Message from player %d (%d bytes long)", player, num_bytes);
//
// Process the 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_PING:
//
// Send a ping message straight back!
//
{
SERVER_Block_ping *sbp = (SERVER_Block_ping *) upto;
LOG_message(0xffffff, "Ping %d", sbp->id);
struct
{
SLONG gameturn;
SERVER_Block_ping ping;
} ping_message;
ping_message.gameturn = GAME_turn;
ping_message.ping.type = SERVER_BLOCK_TYPE_PING;
ping_message.ping.id = sbp->id;
ping_message.ping.game_process = GAME_process;
NET_server_message_to_player(
player,
sizeof(ping_message),
&ping_message);
upto += sizeof(SERVER_Block_ping);
}
break;
case SERVER_BLOCK_TYPE_REQUEST_JOIN:
{
SERVER_Block_request_join *sbj = (SERVER_Block_request_join *) upto;
//
// The player is requesting to join the game. Look for the player
// in our player structure.
//
SLONG i;
SERVER_Player *sp;
for (i = 0; i < SERVER_MAX_PLAYERS; i++)
{
sp = &SERVER_player[i];
if ((sp->flag & SERVER_PLAYER_FLAG_USED) && sp->player_id == player)
{
goto found_player;
}
}
ASSERT(0);
found_player:;
LOG_message(0xffffff, "Request Join from player %d", i);
//
// Fill in the player details.
//
sp->red = sbj->red;
sp->blue = sbj->blue;
sp->green = sbj->green;
sp->mass = sbj->mass;
sp->power = sbj->power;
strncpy(sp->name, sbj->name, 31);
//
// The player id is the same as the server_player index.
//
sp->ship_index = sp - SERVER_player;
//
// Set the mode of the player to REQUESTED. When no
// other players are joining, this player will be
// sent a copy of gamestate.
//
sp->mode = SERVER_PLAYER_MODE_REQUESTED;
upto += sizeof(SERVER_Block_request_join);
}
break;
case SERVER_BLOCK_TYPE_RECEIVED_GAMESTATE:
{
SLONG i;
SLONG me;
SERVER_Player *sp;
SERVER_Player *sp_me;
//
// Which SERVER_Player has sent us this message?
//
for (i = 0; i < SERVER_MAX_PLAYERS; i++)
{
sp = &SERVER_player[i];
if (sp->flag & SERVER_PLAYER_FLAG_USED)
{
if (sp->player_id == player)
{
//
// Found the person who sent us the message.
//
me = i;
sp_me = sp;
goto found_him;
}
}
}
ASSERT(0);
found_him:;
LOG_message(0xffffff, "Received acknoledgement of receipt of gamestate from player %d", me);
//
// This is the gameturn he wlil become active.
//
sp_me->mode = SERVER_PLAYER_MODE_PLAYING;
sp_me->active = GAME_turn + 16 * 2;
sp_me->gameturn = sp_me->active - 2;
memset(&sp_me->key, 0, sizeof(sp_me->key));
//
// Send out a message to all the players, telling them about the
// new ship joining.
//
for (i = 0; i < SERVER_MAX_PLAYERS; i++)
{
sp = &SERVER_player[i];
if (sp->flag & SERVER_PLAYER_FLAG_USED)
{
switch(sp->mode)
{
case SERVER_PLAYER_MODE_CONNECTED:
//
// Hasn't been sent gamestate yet, so he doesn't
// need to know about the new player.
//
break;
case SERVER_PLAYER_MODE_SENT_GAMESTATE:
case SERVER_PLAYER_MODE_PLAYING:
{
//
// Send this player a new_player message.
//
struct
{
SLONG gameturn;
SERVER_Block_new_player sbn;
} new_player;
new_player.gameturn = GAME_turn;
new_player.sbn.type = SERVER_BLOCK_TYPE_NEW_PLAYER;
new_player.sbn.red = sp_me->red;
new_player.sbn.green = sp_me->green;
new_player.sbn.blue = sp_me->blue;
new_player.sbn.mass = sp_me->mass;
new_player.sbn.power = sp_me->power;
new_player.sbn.x = 0.0F;
new_player.sbn.y = -2.0F;
new_player.sbn.ship_index = sp_me->ship_index;
new_player.sbn.local = (i == me);
new_player.sbn.active = sp_me->active;
strncpy(new_player.sbn.name, sp_me->name, 31);
new_player.sbn.name[31] = '\000';
NET_server_message_to_player(sp->player_id, sizeof(new_player), &new_player, TRUE);
//
// Create the new ship locally.
//
memset(&SHIP_ship[sp_me->ship_index], 0, sizeof(SHIP_Ship));
SHIP_ship[sp_me->ship_index].red = new_player.sbn.red;
SHIP_ship[sp_me->ship_index].green = new_player.sbn.green;
SHIP_ship[sp_me->ship_index].blue = new_player.sbn.blue;
SHIP_ship[sp_me->ship_index].active = new_player.sbn.active;
SHIP_ship[sp_me->ship_index].x = new_player.sbn.x;
SHIP_ship[sp_me->ship_index].y = new_player.sbn.y;
SHIP_ship[sp_me->ship_index].flag = SHIP_FLAG_USED;
SHIP_ship[sp_me->ship_index].mass = new_player.sbn.mass;
SHIP_ship[sp_me->ship_index].power = new_player.sbn.power;
LOG_message(0xffff00, "New player active %d", new_player.sbn.active);
}
break;
default:
ASSERT(0);
break;
}
}
}
upto += sizeof(SERVER_Block_received_gamestate);
}
break;
case SERVER_BLOCK_TYPE_MY_KEYPRESS_LIST:
{
SLONG i;
SERVER_Block_my_keypress_list *sbk = (SERVER_Block_my_keypress_list *) upto;
//LOG_message(0xffffff, "Keypresses from player %d turn %d", sbk->ship_index, gameturn);
ASSERT(WITHIN(sbk->ship_index, 0, SERVER_MAX_PLAYERS - 1));
ASSERT((SERVER_player[sbk->ship_index].flag & SERVER_PLAYER_FLAG_USED) && SERVER_player[sbk->ship_index].mode == SERVER_PLAYER_MODE_PLAYING);
SERVER_Player *sp = &SERVER_player[sbk->ship_index];
//
// Update the player structure.
//
sp->gameturn = gameturn; // We know the keys upto this gameturn now.
sp->hash = sp->hash;
for (i = 0; i < sbk->num_keys; i++)
{
sp->key[(gameturn - i) & (SERVER_MAX_KEYS - 1)] = sbk->key[i];
}
upto += sizeof(SERVER_Block_my_keypress_list);
upto += sbk->num_keys;
}
break;
default:
LOG_message(0xffffff, "Unknown block type.");
exit_loop = TRUE;
break;
}
}
}
}
break;
default:
ASSERT(0);
break;
}
//
// Connecting players...
//
{
SLONG i;
SERVER_Player *sp;
if (SERVER_connecting == -1)
{
//
// We aren't in the process of connecting. Look for a player
// who has requested joining.
//
for (i = 0; i < SERVER_MAX_PLAYERS; i++)
{
sp = &SERVER_player[i];
if ((sp->flag & SERVER_PLAYER_FLAG_USED) && sp->mode == SERVER_PLAYER_MODE_REQUESTED)
{
SERVER_connecting = i;
LOG_message(0xffffff, "Connecting player %d", SERVER_connecting);
break;
}
}
}
if (SERVER_connecting != -1)
{
//
// Process this person joining.
//
ASSERT(WITHIN(SERVER_connecting, 0, SERVER_MAX_PLAYERS - 1));
sp = &SERVER_player[SERVER_connecting];
switch(sp->mode)
{
case SERVER_PLAYER_MODE_CONNECTED:
ASSERT(0);
break;
case SERVER_PLAYER_MODE_REQUESTED:
{
LOG_message(0xffffff, "Sending gamestate to player %d", SERVER_connecting);
struct
{
SLONG gameturn;
SERVER_Block_gamestate sbg;
} send_gamestate;
//
// Send the player a copy of gamestate.
//
send_gamestate.gameturn = GAME_turn;
send_gamestate.sbg.type = SERVER_BLOCK_TYPE_GAMESTATE;
send_gamestate.sbg.gameturn = SERVER_turn + 1;
GAMESTATE_store(&send_gamestate.sbg.gs);
NET_server_message_to_player(sp->player_id, sizeof(send_gamestate), &send_gamestate, TRUE);
//
// Change the mode.
//
sp->mode = SERVER_PLAYER_MODE_SENT_GAMESTATE;
}
break;
case SERVER_PLAYER_MODE_SENT_GAMESTATE:
//
// We are waiting for the player to send us a RECEIVED_GAMESTATE message.
//
break;
case SERVER_PLAYER_MODE_PLAYING:
//
// Finished connecting this player.
//
LOG_message(0xffffff, "Finished connecting player %d", SERVER_connecting);
SERVER_connecting = -1;
break;
default:
ASSERT(0);
break;
}
}
}
//
// Send out the keypresses to all the players.
//
{
UBYTE *upto;
UBYTE buffer[1024];
memset(buffer, 0, sizeof(buffer));
SLONG *slong = (SLONG *) buffer;
*slong = GAME_turn;
for (i = 0; i < SERVER_MAX_PLAYERS; i++)
{
sp = &SERVER_player[i];
if ((sp->flag & SERVER_PLAYER_FLAG_USED) && sp->mode == SERVER_PLAYER_MODE_PLAYING)
{
upto = buffer + 4;
for (j = 0; j < SERVER_MAX_PLAYERS; j++)
{
if (j == i)
{
//
// Don't send a players messages to himself.
//
continue;
}
sp_2 = &SERVER_player[j];
//
// Start building the remote keypress message.
//
if ((sp_2->flag & SERVER_PLAYER_FLAG_USED) && sp->mode == SERVER_PLAYER_MODE_PLAYING)
{
SLONG k;
SLONG num_turns;
SERVER_Block_remote_keypress_list *sbr = (SERVER_Block_remote_keypress_list *) upto;
num_turns = sp_2->gameturn - sp_2->active;
if (num_turns > 32)
{
num_turns = 32;
}
if (num_turns > 0)
{
sbr->type = SERVER_BLOCK_TYPE_REMOTE_KEYPRESS_LIST;
sbr->num_keys = num_turns;
sbr->ship_index = sp_2->ship_index;
sbr->gameturn = sp_2->gameturn;
for (k = 0; k < num_turns; k++)
{
sbr->key[k] = sp_2->key[(sp_2->gameturn - k) & (SERVER_MAX_KEYS - 1)];
}
upto += sizeof(SERVER_Block_remote_keypress_list);
upto += num_turns;
}
}
}
if (upto != buffer + 4)
{
NET_server_message_to_player(sp->player_id, upto - buffer, buffer);
}
}
}
}
//
// What gameturn do we know all the keypresses upto?
//
SLONG upto = GAME_turn;
for (i = 0; i < SERVER_MAX_PLAYERS; i++)
{
sp = &SERVER_player[i];
if ((sp->flag & SERVER_PLAYER_FLAG_USED) && sp->mode == SERVER_PLAYER_MODE_PLAYING)
{
if (sp->gameturn < upto)
{
upto = sp->gameturn;
}
}
}
//
// Process the game upto this gameturn.
//
while(SERVER_turn < upto)
{
SERVER_turn += 1;
//
// Check for out-of-sync.
//
for (i = 0; i < SERVER_MAX_PLAYERS; i++)
{
sp = &SERVER_player[i];
if ((sp->flag & SERVER_PLAYER_FLAG_USED) && sp->mode == SERVER_PLAYER_MODE_PLAYING)
{
if ((SERVER_turn & 0x3f) == 0)
{
SHIP_Ship *ss = &SHIP_ship[sp->ship_index];
sp->hash = ss->x + ss->y + ss->angle;
}
}
}
//
// Mark all ships that go active this gameturn.
//
SHIP_flag_active(SERVER_turn);
//
// Update the keypresses in the players.
//
for (i = 0; i < SERVER_MAX_PLAYERS; i++)
{
sp = &SERVER_player[i];
if ((sp->flag & SERVER_PLAYER_FLAG_USED) && sp->mode == SERVER_PLAYER_MODE_PLAYING)
{
ASSERT(SERVER_turn <= sp->gameturn);
ASSERT(WITHIN(sp->ship_index, 0, SHIP_MAX_SHIPS - 1));
ss = &SHIP_ship[sp->ship_index];
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 (sp->key[SERVER_turn & (SERVER_MAX_KEYS - 1)] & PLAYER_KEY_LEFT ) {ss->flag |= SHIP_FLAG_KEY_LEFT ;}
if (sp->key[SERVER_turn & (SERVER_MAX_KEYS - 1)] & PLAYER_KEY_RIGHT ) {ss->flag |= SHIP_FLAG_KEY_RIGHT ;}
if (sp->key[SERVER_turn & (SERVER_MAX_KEYS - 1)] & PLAYER_KEY_THRUST ) {ss->flag |= SHIP_FLAG_KEY_THRUST ;}
if (sp->key[SERVER_turn & (SERVER_MAX_KEYS - 1)] & PLAYER_KEY_TRACTOR_BEAM) {ss->flag |= SHIP_FLAG_KEY_TRACTOR_BEAM;}
if (ss->flag & SHIP_FLAG_KEY_THRUST)
{
LOG_message(0x00ff00, "Thrust %d", SERVER_turn);
}
}
}
if (SHIP_ship[0].flag & SHIP_FLAG_ACTIVE)
{
LOG_file("SHIP_process_one: SERVER_turn %3d key %3d (%f,%f) %f\n", SERVER_turn, SHIP_ship[0].flag, SHIP_ship[0].x, SHIP_ship[0].y, SHIP_ship[0].angle);
}
//
// Sixteen processes per gameturn!
//
for (i = 0; i < 16; i++)
{
SHIP_process_all();
ORB_process_all();
TB_process_all();
}
}
}
void SERVER_draw()
{
SLONG i;
static SLONG last;
static SLONG now;
now = OS_ticks();
if (now < last + (1000 / 20))
{
//
// Don't draw more than 20 times a second.
//
return;
}
last = now;
if ((GAME_turn & 0x1f) == 0)
{
OS_clear_screen(0x22, 0x33, 0xff);
}
else
{
OS_clear_screen();
}
OS_scene_begin();
{
float mid_x = 0.0F;
float mid_y = 0.0F;
float zoom = 0.014F;
SHIP_draw_all(mid_x, mid_y, zoom);
ORB_draw_all (mid_x, mid_y, zoom);
TB_draw_all (mid_x, mid_y, zoom);
LAND_draw_all(mid_x, mid_y, zoom);
}
FONT_draw(0.052F, 0.052F, 0x000000, 0.7F, -1, "Gameturn %4d: Server turn: %4d Real turn: %d", GAME_turn >> 5, SERVER_turn >> 5, GAME_turn);
FONT_draw(0.050F, 0.050F, 0xff0000, 0.7F, -1, "Gameturn %4d: Server turn: %4d Real turn: %d", GAME_turn >> 5, SERVER_turn >> 5, GAME_turn);
FONT_draw(0.600F, 0.700F, 0xffff00, 1.7F, -1, "%f", SERVER_player[0].hash);
FONT_draw(0.600F, 0.800F, 0xffff00, 1.7F, -1, "%f", SERVER_player[1].hash);
FONT_draw(0.600F, 0.900F, 0xffff00, 1.7F, -1, "%f", SERVER_player[2].hash);
/*
for (i = 0; i < SERVER_MAX_PLAYERS; i++)
{
SERVER_Player *sp = &SERVER_player[i];
if (sp->flag & SERVER_PLAYER_FLAG_USED)
{
if (sp->flag & SERVER_PLAYER_FLAG_OUT_OF_SYNC)
{
FONT_draw(0.6F, 0.2F + (i * 0.05F), 0x3344ff, 0.4F, -1, "Player %d out of sync", sp->ship_index);
}
}
}
*/
LOG_draw();
OS_scene_end();
OS_show();
}