Purple Martians
Technical Code Descriptions

Netgame - Server Setup
Program states for server PM_PROGRAM_STATE_SERVER_NEW_GAME Server initializes network Server adds new clients PM_PROGRAM_STATE_SERVER_EXIT
Program states for server The program states used by the server are: PM_PROGRAM_STATE_SERVER_NEW_GAME PM_PROGRAM_STATE_MAIN_GAME_LOOP PM_PROGRAM_STATE_SERVER_EXIT PM_PROGRAM_STATE_SERVER_NEW_GAME
   if (state[1] == PM_PROGRAM_STATE_SERVER_NEW_GAME)
   {
      mLog.add(LOG_OTH_program_state, 0, "[PM_PROGRAM_STATE_SERVER_NEW_GAME]\n");
      if (!mNetgame.ServerInitNetwork())
      {
         state[0] = PM_PROGRAM_STATE_SERVER_EXIT;
         return;
      }
      mEventQueue.reset_fps_timer();
      if (!load_and_setup_level(mLevel.play_level, 3)) state[0] = PM_PROGRAM_STATE_SERVER_EXIT;
   }
Server initializes network When a server game is started, the first thing that happens is the network initialization.
int mwNetgame::ServerInitNetwork(void)
{
   mLog.log_versions();
   mLog.add_fw (LOG_NET, 0, 76, 10, "+", "-", "");
   mLog.add_fw (LOG_NET, 0, 76, 10, "|", " ", "Server mode started");
   mLog.add_fwf(LOG_NET, 0, 76, 10, "|", " ", "Server hostname:    [%s]", mLoop.local_hostname);
   mLog.add_fwf(LOG_NET, 0, 76, 10, "|", " ", "Level:              [%d]", mLevel.play_level);

   char msg[256];
   if (NetworkInit())
   {
      sprintf(msg, "Error: failed to initialize network");
      mLog.add_fw(LOG_error, 0, 76, 10, "|", " ", msg);
      mInput.m_err(msg);
      return 0;
   }

   // open the server channel
   char port[256];
   sprintf(port, ":%d", server_port);
   if (!(Channel = net_openchannel(NetworkDriver, port)))
   {
      sprintf(msg, "Error opening server channel");
      mLog.add_fw(LOG_error, 0, 76, 10, "|", " ", msg);
      mInput.m_err(msg);
      return 0;
   }
   mLog.add_fw(LOG_NET, 0, 76, 10, "|", " ", "Server network initialized");
   mLog.add_fw(LOG_NET, 0, 76, 10, "+", "-", "");

   ima_server = 1;
   return 1;
}
Server adds new clients When the server receives a cjon packet from an unknown address, it sets up a channel and replies.

void mwPacketBuffer::add_to_rx_buffer(void)
{
   char data[1024] = {0};
   char address[256];
   if (mNetgame.ima_server)
      while (int len = net_receive(mNetgame.Channel, data, 1024, address))
      {
         int p = mNetgame.server_check_address(address);
         if (p == -1) // unknown address
         {
            if (PacketRead(data, "cjon")) mNetgame.server_proc_cjon_packet(data, address); // setup channel and reply with sjon
         }
....

void mwNetgame::server_proc_cjon_packet(char *data, char * address)
{
   int pos = 4;
   int color  = mPacketBuffer.PacketGetByte(data, pos);
   char hostname[16];
   mPacketBuffer.PacketReadString(data, pos, hostname);

   mLog.add_fwf(LOG_NET, 0, 76, 10, "+", "-", "");
   mLog.add_fwf(LOG_NET, 0, 76, 10, "|", " ", "Server received join request from %s requesting color:%d", hostname, color);

   int p = mPlayer.find_inactive_player();
   if (p == 99) // no inactive player found
   {
      mLog.add_fwf(LOG_NET, 0, 76, 10, "|", " ", "Reply sent: 'SERVER FULL'");
      mLog.add_fwf(LOG_NET, 0, 76, 10, "+", "-", "");
      server_send_sjon_packet(address, 0, 0, 99, 0);
      if (mLog.log_types[LOG_NET_session].action) session_add_entry(address, hostname, 99, 0, 1);
   }
   else // inactive player found, proceed with join
   {
      // set up channel, use the player number as the index to the channel
      mwChannels[p].active = 1;
      strcpy(mwChannels[p].address, address);

      // try to use requested color, unless already used by another player
      while (mPlayer.is_player_color_used(color)) if (++color > 15) color = 1;

      mPlayer.init_player(p, 1); // full player reset
      mStateHistory[p].initialize();
      mItem.set_player_start_pos(p);

      mPlayer.syn[p].active = 1;

      mPlayer.syn[p].control_method = PM_PLAYER_CONTROL_METHOD_NETGAME_REMOTE;
      mPlayer.loc[p].server_last_stak_rx_frame_num = mLoop.frame_num + 200;
      sprintf(mPlayer.loc[p].hostname, "%s", hostname);

      mGameMoves.add_game_move(mLoop.frame_num, PM_GAMEMOVE_TYPE_PLAYER_ACTIVE, p, color); // add game move to make client active

      server_send_sjon_packet(address, mLevel.play_level, mLoop.frame_num, p, color);

      if (mLog.log_types[LOG_NET_session].action) session_add_entry(address, hostname, p, 1, 0);

      mLog.add_fwf(LOG_NET,               0, 76, 10, "|", " ", "Server replied with join invitation:");
      mLog.add_fwf(LOG_NET_join_details,  0, 76, 10, "|", " ", "Level:[%d]", mLevel.play_level);
      mLog.add_fwf(LOG_NET_join_details,  0, 76, 10, "|", " ", "Player Number:[%d]", p);
      mLog.add_fwf(LOG_NET_join_details,  0, 76, 10, "|", " ", "Player Color:[%d]", color);
      mLog.add_fwf(LOG_NET_join_details,  0, 76, 10, "|", " ", "Server Frame:[%d]", mLoop.frame_num);
      mLog.add_fwf(LOG_NET_join_details,  0, 76, 10, "|", " ", "Server Level Sequence Num:[%d]", mPlayer.syn[0].server_lev_seq_num);
      mLog.add_fwf(LOG_NET,               0, 76, 10, "+", "-", "");
   }
}
PM_PROGRAM_STATE_SERVER_EXIT
   if (state[1] == PM_PROGRAM_STATE_SERVER_EXIT)
   {
      mLog.add(LOG_OTH_program_state, 0, "[PM_PROGRAM_STATE_SERVER_EXIT]\n");

      if (mGameMoves.autosave_game_on_level_quit) mGameMoves.save_gm_make_fn("autosave on level quit", 0);

      mNetgame.ServerExitNetwork();
      state[0] = PM_PROGRAM_STATE_MENU;
   }

void mwNetgame::ServerExitNetwork(void)
{
   mLog.add_header(LOG_NET, 0, 0, "Shutting down the server network");

   if (Channel) net_closechannel(Channel);
   Channel = NULL;

   ima_server = 0;

   // reset player data
   for (int p=0; p < NUM_PLAYERS; p++) mPlayer.init_player(p, 1);
   mPlayer.syn[0].active = 1;

   if (mLog.log_types[LOG_NET_session].action) session_flush_active_at_server_exit();
}