Complete.Org: Mailing Lists: Archives: freeciv-dev: October 2003:
[Freeciv-Dev] (PR#977) Re: (PR#4638) Connect Dialog Patch
Home

[Freeciv-Dev] (PR#977) Re: (PR#4638) Connect Dialog Patch

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: dspeyer@xxxxxxxxxxx
Subject: [Freeciv-Dev] (PR#977) Re: (PR#4638) Connect Dialog Patch
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Sun, 12 Oct 2003 03:10:15 -0700
Reply-to: rt@xxxxxxxxxxxxxx

I wanted badly to test this patch, so I fixed the compile errors (missing
entries in Makefile.am, experimental skill level overflow, lots of
warnings), and while I was at it I cleaned up a lot of bad style stuff.
Patch attached.

(Patch authors really ought to compile with --with-debug=yes and read the
style guide!)

Let me first say: It is great! This really looks good.

Needs to be fixed before committing is to be considered:
  - GTK2 support is missing. This is now our official client, so it must
have support. Perhaps Vasco can lend a hand?
  - The win32 FIXMEs need to be fixed or well documented.
  - The "wants hack" code seems like it could be easily fooled by reading
the contents (a timestamp) of the challenge file and sending the contents
back... there is no check that the client actually wrote something in
there... isn't a test for value > 0 missing?
  - The rename function no longers says it is unimplemented, but it still
is... shouldn't we just remove this function?
  - In stdinhand.c: +  /*  send_rulesets(&(game.est_connections));*/ ??

General comments:
  - I liked the way server options are set. Clearly a hack, which one day
deserves a better implementation, but a nice one.
  - I do not like min_free_port(). This is a race-condition waiting to
happen. Instead, the user should specify which port to use. This way the
user knows the port number and can inform his friends. Failure should be
soft and allow retries.
  - I do not like NUMBER_OF_TRIES 500, a lower number but more delay
should be used instead
  - CMD_FLUSH is unnecessary, I think. Use delay on client end instead.
  - I think "Save like Autosave" is unnecessary
  - In the "join networked game" part, the "Welcome to Freeciv" message
and the frame around the tabs should be removed
  - Consider removing the big "Quit" button; it is too easily pressed by
mistake
  - The helppopup command is unnecessary. Instead, pass an extra parameter
to the help function, IMHO. Don't spam server with commands.
  - There are lots of style issues I did not touch in the new gtk code. It
is ok not to fix this for gtk1 client, but please fix them when porting it
over to gtk2.

I'm hoping to see this in CVS soon.

  - Per

/********************************************************************** 
Freeciv - Copyright (C) 2003 - The Freeciv Project
   This program is free software; you can redistribute it and / or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful, 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
***********************************************************************/ 
#ifndef FC__CONNECTDLG_COMMON_H
#define FC__CONNECTDLG_COMMON_H

#include "shared.h"

bool client_start_server(void);
void client_kill_server(void);
bool is_server_running(void);

int min_free_port(void);

void send_client_wants_hack(char *filename);
void handle_client_wants_hack(struct packet_generic_integer *packet);
void send_initial_server_commands(void);
void send_final_server_commands(void);
void send_save_game(char *filename);

extern char new_username[MAX_LEN_NAME];
extern char *current_filename;

enum skill_levels {
  NOVICE,
  EASY,
  NORMAL,
  HARD,
#ifdef DEBUG
  EXPERIMENTAL,
#endif
  NUM_SKILL_LEVELS
};

extern const char *skill_level_names[NUM_SKILL_LEVELS];
extern const char * skill_level_names_translated[NUM_SKILL_LEVELS];

extern bool client_has_hack;

#endif  /* FC__CONNECTDLG_COMMON_H */ 
/********************************************************************** 
Freeciv - Copyright (C) 2003 - The Freeciv Project
   This program is free software; you can redistribute it and / or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful, 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
***********************************************************************/ 
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>

#ifdef HAVE_SYS_SOCKET_H
  #include <sys/socket.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>

/* FIXME: need to use AC_HEADER_SYS_WAIT */ 
#include <sys/wait.h>

#include "fcintl.h"
#include "mem.h"
#include "netintf.h"
#include "rand.h"
#include "registry.h"
#include "support.h"
#include "civclient.h"
#include "climisc.h"
#include "clinet.h"

#include "chatline_common.h"
#include "connectdlg_g.h"
#include "connectdlg_common.h"

#define WAIT_BETWEEN_TRIES 100000 /* usecs */ 
#define NUMBER_OF_TRIES 500

/* FIXME: this will need to change for WIN32 */ 
pid_t server_pid = - 1;

char player_name[MAX_LEN_NAME];
char new_username[MAX_LEN_NAME];
char * current_filename = NULL;

/* TRANS: don't translate this array. */ 
const char *skill_level_names[NUM_SKILL_LEVELS] = 
{ 
  "novice",
  "easy", 
  "normal", 
  "hard"
#ifdef DEBUG
, "experimental"
#endif
};

const char *skill_level_names_translated[NUM_SKILL_LEVELS] = 
{ 
  N_("novice"),
  N_("easy"), 
  N_("normal"), 
  N_("hard")
#ifdef DEBUG
, N_("experimental")
#endif
};

bool client_has_hack = FALSE;

/************************************************************************** 
  The general chain of events:

  Two distinct paths are taken depending on the choice of mode: 

  If the user selects the multi-player mode, then a packet_req_join_game 
  packet is sent to the server. It is either successful or not. The end.

  If the user selects a single- player mode (either a new game or a save 
  game) then: 
   1. the packet_req_join_game is sent.
   2. on receipt, if we can join, then a challenge packet is sent to the
      server, so we can get hack level control.
   3. if we can't get hack, then we get dumped to multi- player mode. If
      we can, then:
      a. for a new game, we send a series of packet_generic_message packets
         with commands to start the game.
      b. for a saved game, we send the load command with a 
         packet_generic_message, then we send a PACKET_PLAYER_LIST_REQUEST.
         the response to this request will tell us if the game was loaded or
         not. if not, then we send another load command. if so, then we send
         a series of packet_generic_message packets with commands to start 
         the game.
**************************************************************************/ 

/************************************************************************** 
  Tests if the client has started the server.
**************************************************************************/ 
bool is_server_running()
{ 
  return (server_pid > 0);
} 

/************************************************************************** 
  Kills the server if the client has started it (FIXME: atexit handler?)
**************************************************************************/ 
void client_kill_server()
{
  if (is_server_running()) {
    kill(server_pid, SIGTERM);
    waitpid(server_pid, NULL, WUNTRACED);
    server_pid = - 1;
  }
}   

/************************************************************************** 
  Forks a server if it can. returns FALSE is we find we couldn't start
  the server. This is so system intensive that it is *nix only. VMS and 
  Windows code will come later.
**************************************************************************/ 
bool client_start_server(void)
{
#if !defined(__VMS) && !defined(WIN32_NATIVE)
  int i = 0;
  char **argv = NULL;
  char buf[512];

  /* only one server (forked from this client) shall be running at a time */ 
  client_kill_server();

  append_output_window(_("Starting server..."));

  /* find a free port */ 
  server_port = min_free_port();

  argv = fc_malloc(8 * sizeof(char*));
  argv[0] = fc_malloc(10);
  my_snprintf(argv[0], 10, "civserver");
  argv[1] = fc_malloc(3);
  my_snprintf(argv[1], 3, "-p");
  argv[2] = fc_malloc(7);
  my_snprintf(argv[2], 7, "%d", server_port);
  argv[3] = fc_malloc(8);
  my_snprintf(argv[3], 8, "--debug");
  argv[4] = fc_malloc(2);
  my_snprintf(argv[4], 2, "3");
  argv[5] = fc_malloc(6);
  my_snprintf(argv[5], 6, "--log");
  argv[6] = fc_malloc(2);
  my_snprintf(argv[6], 2, "-");
  argv[7] = NULL;

  server_pid = fork();
  
  if (server_pid == 0) {
    /* inside the fork */  
    /* avoid terminal spam, but still make server output available */ 
    char fn[32];
    int fd;

    fclose(stdout);
    fclose(stderr);
    sprintf(fn, ".civserverlog%d", server_port); /* include the port to avoid 
duplication */ 
    fd = open(fn, O_WRONLY | O_CREAT);
    if (fd!= 1) {
      dup2(fd, 1);
    }
    if (fd!= 2) {
      dup2(fd, 2);
    }
    fchmod(1,0644);

    /* If it's still attatched to our terminal, things get messed up, 
      but civserver needs *something* */ 
    fclose(stdin);
    fd = open("/dev/null", O_RDONLY);
    if (fd!= 0) {
      dup2(fd, 0);
    }

    /* These won't return on success */ 
    execvp("./ser", argv);
    execvp("./server/civserver", argv);
    execvp("civserver", argv);

    /* This line is only reached if civserver cannot be started, 
     * so we kill the forked process.
     * Calling exit here is dangerous due to X11 problems (async replies) */ 
    _exit(1);
  }

  /* Don't need these anymore */ 
  for (i = 0; i < 6; i++) {
    free(argv[i]);
  }
  free(argv);

  /* a reasonable number of tries */ 
  while(connect_to_server(user_username(), "localhost", server_port, 
                          buf, sizeof(buf)) == - 1) {
    myusleep(WAIT_BETWEEN_TRIES);

    if (i > NUMBER_OF_TRIES) {
      break;
    }
    i++;
  }

  /* Weird, but could happen, if server doesn't support new startup stuff
   * capabilities won't help us here... */ 
  if (!aconnection.used) {
    /* possible that server is still running. kill it */ 
    kill(server_pid, SIGTERM);
    server_pid = -1;

    append_output_window(_("Couldn't connect to the server. We probably "
                           "couldn't start it from here. You'll have to "
                           "start one manually. Sorry..."));
    return FALSE;
  }

  return TRUE;
#else /*VMS or Win32*/
  return FALSE;
#endif
}

/************************************************************************** 
  Finds the lowest port which can be used for the server (starting at 
  the default 5555).
**************************************************************************/ 
int min_free_port(void)
{
  int port, n, s;
  struct sockaddr_in tmp;
  
  s = socket(AF_INET, SOCK_STREAM, 0);
  n = INADDR_ANY;
  port = 5554; /* make looping convinient */ 
  do {
    port++;
    memset(&tmp, 0, sizeof(struct sockaddr_in));
    tmp.sin_family = AF_INET;
    tmp.sin_port = htons(port);
    memcpy(&tmp. sin_addr, &n, sizeof(long));
  } while(bind(s, (struct sockaddr*) &tmp, sizeof(struct sockaddr_in)));

  my_closesocket(s);
  
  return port;
}

/************************************************************************** 
  If the client is capable of 'wanting hack', then the server will 
  send the client a filename in the packet_join_game_reply packet.

  This function creates the file with a suitably random number in it 
  and then sends the filename and number to the server. If the server 
  can open and read the number, then the client is given hack access.
**************************************************************************/ 
void send_client_wants_hack(char * filename)
{
  struct packet_generic_integer packet;
  struct section_file file;

  /* find some entropy */ 
  packet.value = myrand(MAX_UINT32) - time(NULL);

  /* we don't want zero */ 
  if (packet.value == 0) {
    packet.value++;
  }

  section_file_init(&file);
  secfile_insert_int(&file, packet.value, "challenge.entropy");
  section_file_save(&file, filename, 0);      
  section_file_free(&file);

  /* tell the server what we put into the file */ 
  send_packet_generic_integer(&aconnection, PACKET_CLIENT_WANTS_HACK, 
                              &packet);
}

/************************************************************************** 
  Handle response (by the server) if the client has got hack or not.
**************************************************************************/ 
void handle_client_wants_hack(struct packet_generic_integer * packet)
{
  if (packet->value == 1) {
    append_output_window("We have control of the server "
                         "(command access level hack)");
    client_has_hack = TRUE;
  } else if (client_start_type != START_MULTI) {
    append_output_window("We can't take control of server, "
                         "killing server.");
    client_kill_server();
  }
}

/************************************************************************** 
  sends commands to the server to ready a loaded savegame.
**************************************************************************/ 
void send_initial_server_commands(void)
{
  int msglen = MAX_LEN_MSG - MAX_LEN_USERNAME + 1;
  struct packet_generic_message packet;

  if (client_start_type == START_SINGLE_SAVE) {
    my_snprintf(packet.message, msglen, "/load %s", current_filename);
    send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);

    send_packet_generic_empty(&aconnection, PACKET_PLAYER_LIST_REQUEST);
  }
}

/************************************************************************** 
  Sends a bunch of commands to the server to ready the game.
**************************************************************************/ 
void send_final_server_commands(void)
{
  int aifill = get_aifill_setting();
  const char *ruleset = get_ruleset_setting();
  int msglen = MAX_LEN_MSG - MAX_LEN_USERNAME + 1;
  struct packet_generic_message packet;

  if (client_start_type == START_SINGLE_NEW) {
    strcpy(player_name, user_username());
    send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);

    if (aifill > 0){
      my_snprintf(packet.message, msglen, "/%s", get_aiskill_setting());
      send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);

      my_snprintf(packet.message, msglen, "/set aifill %d", aifill);
      send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);
    }
    my_snprintf(packet.message, msglen, "/rulesetdir %s", ruleset);
    send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);

  } else if (client_start_type == START_SINGLE_SAVE) {
    my_snprintf(packet.message, msglen, "/set autotoggle 1");
    send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);

    my_snprintf(packet.message, msglen, "/take \"%s\" \"%s\"", 
                user_username(), new_username);
    sz_strlcpy(player_name, new_username);
    send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);
  } else { /* START_MULTI */ 
    /* shouldn't happen */ 
    assert(0);
  }

  sz_strlcpy(packet.message, "/start");
  send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);
}

/************************************************************************** 
 Send server command to save game.
**************************************************************************/ 
void send_save_game(char * filename)
{   
  int msglen = MAX_LEN_MSG - MAX_LEN_USERNAME + 1;
  struct packet_generic_message packet;

  if (filename) {
    my_snprintf(packet.message, msglen, "/save %s", filename);
  } else if (current_filename) {
    my_snprintf(packet.message, msglen, "/save %s", current_filename);
  } else {
    my_snprintf(packet.message, msglen, "/save");
  }

  send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);
}
Index: client/Makefile.am
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/Makefile.am,v
retrieving revision 1.50
diff -u -r1.50 Makefile.am
--- client/Makefile.am  13 May 2003 21:10:32 -0000      1.50
+++ client/Makefile.am  12 Oct 2003 10:06:43 -0000
@@ -175,7 +175,9 @@
        audio.c         \
        audio.h         \
        audio_none.c    \
-       audio_none.h
+       audio_none.h    \
+       connectdlg_common.c \
+       connectdlg_common.h
 
 civclient_LDFLAGS = @CLIENT_LDFLAGS@
 civclient_DEPENDENCIES = @gui_sources@/libguiclient.a \
Index: client/civclient.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/civclient.c,v
retrieving revision 1.178
diff -u -r1.178 civclient.c
--- client/civclient.c  9 Oct 2003 11:59:20 -0000       1.178
+++ client/civclient.c  12 Oct 2003 10:06:43 -0000
@@ -55,6 +55,7 @@
 #include "clinet.h"
 #include "cma_core.h"          /* kludge */
 #include "connectdlg_g.h"
+#include "connectdlg_common.h"
 #include "control.h" 
 #include "dialogs_g.h"
 #include "diplodlg_g.h"
@@ -91,6 +92,8 @@
 
 int seconds_to_turndone;
 
+enum client_start_types client_start_type = START_SINGLE_NEW;
+
 /* TRUE if an end turn request is blocked by busy agents */
 bool waiting_for_end_turn = FALSE;
 
@@ -454,6 +457,14 @@
                                  packet);
     break;
 
+  case PACKET_PLAYER_LIST_REPLY:
+    handle_player_list_reply((struct packet_player_list*)packet);
+    break;
+
+  case PACKET_CLIENT_WANTS_HACK:
+    handle_client_wants_hack(packet);
+    break;
+
   case PACKET_PROCESSING_STARTED:
     handle_processing_started();
     break;
@@ -675,6 +686,7 @@
     if (client_state==CLIENT_PRE_GAME_STATE
        && (newstate==CLIENT_SELECT_RACE_STATE
            || newstate==CLIENT_GAME_RUNNING_STATE)) {
+      popdown_connect_dialog();
       translate_data_names();
       audio_stop();            /* stop intro sound loop */
     }
@@ -691,7 +703,7 @@
     }
     else if (client_state == CLIENT_PRE_GAME_STATE) {
       popdown_all_city_dialogs();
-      popdown_all_game_dialogs();
+      popdown_all_game_dialogs(); 
       close_all_diplomacy_dialogs();
       set_unit_focus_no_center(NULL);
       clear_notify_window();
Index: client/civclient.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/civclient.h,v
retrieving revision 1.30
diff -u -r1.30 civclient.h
--- client/civclient.h  10 Jul 2003 03:34:29 -0000      1.30
+++ client/civclient.h  12 Oct 2003 10:06:43 -0000
@@ -54,6 +54,10 @@
 
 extern int seconds_to_turndone;
 
+enum client_start_types { START_SINGLE_NEW, START_SINGLE_SAVE, START_MULTI };
+
+extern enum client_start_types client_start_type;
+
 void wait_till_request_got_processed(int request_id);
 bool client_is_observer(void);
 void real_timer_callback(void);
Index: client/clinet.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/clinet.c,v
retrieving revision 1.89
diff -u -r1.89 clinet.c
--- client/clinet.c     12 Sep 2003 07:10:45 -0000      1.89
+++ client/clinet.c     12 Oct 2003 10:06:43 -0000
@@ -68,6 +68,7 @@
 #include "civclient.h"
 #include "climisc.h"
 #include "connectdlg_g.h"
+#include "connectdlg_common.h"
 #include "dialogs_g.h"         /* popdown_races_dialog() */
 #include "gui_main_g.h"                /* add_net_input(), remove_net_input() 
*/
 #include "mapview_common.h"    /* unqueue_mapview_update */
@@ -79,6 +80,8 @@
 
 #include "clinet.h"
 
+extern pid_t server_pid;
+
 struct connection aconnection;
 static struct sockaddr_in server_addr;
 
@@ -133,6 +136,7 @@
   if (try_to_connect(username, errbuf, errbufsize) != 0) {
     return -1;
   }
+  client_has_hack = FALSE;
   return 0;
 }
 
@@ -176,8 +180,6 @@
 **************************************************************************/
 int try_to_connect(char *username, char *errbuf, int errbufsize)
 {
-  struct packet_login_request req;
-
   /* connection in progress? wait. */
   if (aconnection.used) {
     (void) mystrlcpy(errbuf, _("Connection in progress."), errbufsize);
@@ -215,19 +217,30 @@
   /* call gui-dependent stuff in gui_main.c */
   add_net_input(aconnection.sock);
 
-  /* now send join_request package */
+  /* send the first packet to the server */ 
+  send_join_request(username);
+
+  return 0;
+}
 
-  sz_strlcpy(req.short_name, username);
+/************************************************************************** 
+...
+**************************************************************************/ 
+void send_join_request(char *user_name)
+{
+  struct packet_login_request req;
+
+  sz_strlcpy(req.short_name, user_name);
   req.major_version = MAJOR_VERSION;
   req.minor_version = MINOR_VERSION;
   req.patch_version = PATCH_VERSION;
   sz_strlcpy(req.version_label, VERSION_LABEL);
   sz_strlcpy(req.capability, our_capability);
-  sz_strlcpy(req.username, username);
+  sz_strlcpy(req.username, user_name);
   
   send_packet_login_request(&aconnection, &req);
 
-  return 0;
+  return;
 }
 
 /**************************************************************************
@@ -237,6 +250,7 @@
 {
   append_output_window(_("Disconnecting from server."));
   close_socket_nomessage(&aconnection);
+  server_pid = -1; /*Not our problem now*/
 }  
 
 /**************************************************************************
Index: client/clinet.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/clinet.h,v
retrieving revision 1.17
diff -u -r1.17 clinet.h
--- client/clinet.h     12 Apr 2003 18:24:41 -0000      1.17
+++ client/clinet.h     12 Oct 2003 10:06:43 -0000
@@ -58,4 +58,6 @@
 struct server_list *create_server_list(char *errbuf, int n_errbuf);
 void delete_server_list(struct server_list *server_list);
 
+void send_join_request(char *user_name);
+
 #endif  /* FC__CLINET_H */
Index: client/packhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/packhand.c,v
retrieving revision 1.334
diff -u -r1.334 packhand.c
--- client/packhand.c   7 Oct 2003 18:55:08 -0000       1.334
+++ client/packhand.c   12 Oct 2003 10:06:44 -0000
@@ -53,6 +53,8 @@
 #include "clinet.h"            /* aconnection */
 #include "connectdlg_g.h"
 #include "control.h"
+#include "connectdlg_common.h"
+#include "connectdlg_g.h"
 #include "dialogs_g.h"
 #include "goto.h"               /* client_goto_init() */
 #include "graphics_g.h"
@@ -158,9 +160,15 @@
     aconnection.established = TRUE;
     aconnection.id = packet->conn_id;
     agents_game_joined();
+    /* we could always use hack, verify we're local */ 
+    send_client_wants_hack(packet->challenge);
+    gui_handle_rulesets(packet->rulesets);
   } else {
     my_snprintf(msg, sizeof(msg),
                _("You were rejected from the game: %s"), packet->message);
+    if (client_start_type == START_SINGLE_SAVE){
+      return;
+    }
     append_output_window(msg);
     aconnection.id = 0;
     if (auto_connect) {
@@ -168,6 +176,7 @@
     }
     gui_server_connect();
   }
+  update_menus();
   if (strcmp(s_capability, our_capability) == 0) {
     return;
   }
@@ -832,6 +841,7 @@
                             packet->x, packet->y);
   play_sound_for_event(packet->event);
 }
+
  
 /**************************************************************************
 ...
@@ -855,6 +865,12 @@
   } else {
     headline = "";
     lines = "";
+  }
+
+  /*If we can change the server settings, and the gui supports it, then that's 
enough
+    We're taking advantage of short-circuit evaluation here*/
+  if (packet->x == 0 && change_server_options(lines)){
+    return;
   }
 
   if (!game.player_ptr->ai.control || ai_popup_windows ||
Index: client/gui-gtk/connectdlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/connectdlg.c,v
retrieving revision 1.43
diff -u -r1.43 connectdlg.c
--- client/gui-gtk/connectdlg.c 10 Jul 2003 03:34:29 -0000      1.43
+++ client/gui-gtk/connectdlg.c 12 Oct 2003 10:06:44 -0000
@@ -16,15 +16,18 @@
 #endif
 
 #include <errno.h>
+#include <hash.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include <gtk/gtk.h>
 
+#include "connection.h"
 #include "fcintl.h"
 #include "log.h"
-#include "packets.h"
+#include "mem.h"
+#include "netintf.h"
 #include "support.h"
 #include "version.h"
 
@@ -33,32 +36,56 @@
 
 #include "chatline.h"
 #include "colors.h"
-#include "connectdlg_g.h"
 #include "dialogs.h"
+#include "helpdlg.h"
+#include "graphics.h"
 #include "gui_main.h"
 #include "gui_stuff.h"
-
+#include "repodlgs.h"
+#include "tilespec.h"
+  
 #include "connectdlg.h"
+#include "connectdlg_common.h"
+ 
+enum { PAGE_START, PAGE_NEW, PAGE_SAVE, PAGE_MULTI, PAGE_CHOOSE };
 
-enum { 
-  LOGIN_PAGE, 
-  METASERVER_PAGE 
-};
-
-static enum { 
-  LOGIN_TYPE, 
-  NEW_PASSWORD_TYPE, 
-  VERIFY_PASSWORD_TYPE, 
-  ENTER_PASSWORD_TYPE 
-} dialog_config;
+GtkWidget *init_opts_frame;
+  
+static GtkWidget *book;
 
-static GtkWidget *imsg, *ilabel, *iinput, *ihost, *iport;
-static GtkWidget *connw, *quitw;
+static GtkWidget *iname, *ihost, *iport;
 static GtkWidget *server_clist;        /* sorted list of servers */
+static GtkWidget *rulesets_combo;
+
+extern GtkWidget *opening_dialog;
+extern GtkNotebook *sidebarbook;
+extern GtkWidget *serverInfo;
+extern GtkNotebook *uberNB;
 
-static GtkWidget *dialog, *book;
 static int sort_column;
 
+static GtkWidget *prevb, *nextb;
+
+static GtkWidget *starttype_radio[3];
+
+/* new game page */ 
+static GtkWidget *login_new_entry, *aifill_spin, *skill_combo;
+static GtkAdjustment * aifill_adj;
+
+/* save game page */ 
+static GtkWidget *load_dialog=NULL, *save_dialog=NULL;
+static GtkWidget *players_list, *choose_players_list, *game_label;
+
+static void connect_callback(void);
+static gint connect_deleted_callback(GtkWidget *w, GdkEvent *ev, gpointer 
data);
+static void select_player_callback(GtkWidget *w, gint player, gint col, 
+                                   GdkEventButton *ev, gpointer d);
+static void file_ok(GtkWidget *w, gpointer data);
+static void find_load_filename(GtkWidget *w, gpointer data);
+static void page_prev_callback(GtkNotebook *n);
+static void page_next_callback(GtkNotebook *n);
+static void starttype_callback(GtkWidget *w, gpointer data);
+
 /* meta Server */
 static bool update_meta_dialog(GtkWidget *meta_list);
 static void meta_list_callback(GtkWidget *w, gint row, gint column);
@@ -69,119 +96,58 @@
 #define DEFAULT_SORT_COLUMN 0  /* default sort column  (server)  */
 
 /**************************************************************************
- close and destroy the dialog.
+...
 **************************************************************************/
-void close_connection_dialog()
+void gui_handle_rulesets(char* rss)
 {
-  if (dialog) {
-    gtk_widget_destroy(dialog);
-    dialog = NULL;
-    gtk_widget_set_sensitive(top_vbox, TRUE);
+  GList *sets=NULL;
+  char *i,*o;
+  if (!rulesets_combo || !GTK_IS_COMBO(rulesets_combo)){
+    /*probably auto-connect*/
+    return;
+  }
+  o = rss;
+  i = strchr(rss,'/');
+  while(i){
+    *i = 0;
+    sets = g_list_append(sets,o);
+    o = i+1;
+    i = strchr(i+1, '/');
   }
+  sets=g_list_append(sets,o);
+  gtk_combo_set_popdown_strings(GTK_COMBO(rulesets_combo), sets);
+  gtk_entry_set_text(GTK_ENTRY( GTK_COMBO(rulesets_combo)->entry ), "default");
 }
 
 /**************************************************************************
- configure the dialog depending on what type of authentication request the
- server is making.
+...
 **************************************************************************/
-void handle_authentication_request(struct packet_authentication_request *
-                                   packet)
+void popdown_connect_dialog(void)
 {
-  gtk_widget_grab_focus(iinput);
-  gtk_entry_set_text(GTK_ENTRY(iinput), "");
-  gtk_set_label(GTK_BUTTON(connw)->child, _("Next"));
-  gtk_widget_set_sensitive(connw, TRUE);
-  gtk_set_label(imsg, packet->message);
-
-  switch (packet->type) {
-  case AUTH_NEWUSER_FIRST:
-    dialog_config = NEW_PASSWORD_TYPE;
-    break;
-  case AUTH_NEWUSER_RETRY:
-    dialog_config = NEW_PASSWORD_TYPE;
-    break;
-  case AUTH_LOGIN_FIRST:
-    /* if we magically have a password already present in 'password'
-     * then, use that and skip the password entry dialog */
-    if (password[0] != '\0') {
-      struct packet_authentication_reply reply;
-
-      sz_strlcpy(reply.password, password);
-      send_packet_authentication_reply(&aconnection, &reply);
-      return;
-    } else {
-      dialog_config = ENTER_PASSWORD_TYPE;
-    }
-    break;
-  case AUTH_LOGIN_RETRY:
-    dialog_config = ENTER_PASSWORD_TYPE;
-    break;
-  default:
-    assert(0);
-  }
-
-  gtk_widget_show(dialog);
-  gtk_entry_set_visibility(GTK_ENTRY(iinput), FALSE);
-  gtk_set_label(ilabel, _("Password:"));
+  gtk_notebook_set_page(uberNB,1);
+  gtk_notebook_set_page(sidebarbook,1);
 }
 
-/**************************************************************************
- if on the metaserver page, switch page to the login page (with new server
- and port). if on the login page, send connect and/or authentication 
- requests to the server.
-**************************************************************************/
-static void connect_callback(GtkWidget *w, gpointer data)
+/************************************************************************** 
+multiplayer connect- to- server callback
+**************************************************************************/ 
+static void connect_callback(void)
 {
-  char errbuf [512];
-  struct packet_authentication_reply reply;
-
-  if (gtk_notebook_get_current_page(GTK_NOTEBOOK(book)) == METASERVER_PAGE) {
-    gtk_notebook_set_page(GTK_NOTEBOOK(book), LOGIN_PAGE);
-    return;
-  }
+  char errbuf [512], buf[256];
 
-  switch (dialog_config) {
-  case LOGIN_TYPE:
-    sz_strlcpy(user_name, gtk_entry_get_text(GTK_ENTRY(iinput)));
-    sz_strlcpy(server_host, gtk_entry_get_text(GTK_ENTRY(ihost)));
-    sscanf(gtk_entry_get_text(GTK_ENTRY(iport)), "%d", &server_port);
-  
-    if (connect_to_server(user_name, server_host, server_port,
-                          errbuf, sizeof(errbuf)) != -1) {
-    } else {
-      append_output_window(errbuf);
-    }
-
-    break;
-  case NEW_PASSWORD_TYPE:
-    sz_strlcpy(password, gtk_entry_get_text(GTK_ENTRY(iinput)));
-    gtk_set_label(imsg, _("Verify Password"));
-    gtk_entry_set_text(GTK_ENTRY(iinput), "");
-    gtk_widget_grab_focus(iinput);
-    dialog_config = VERIFY_PASSWORD_TYPE;
-    break;
-  case VERIFY_PASSWORD_TYPE:
-    sz_strlcpy(reply.password, gtk_entry_get_text(GTK_ENTRY(iinput)));
-    if (strncmp(reply.password, password, MAX_LEN_NAME) == 0) {
-      gtk_widget_set_sensitive(connw, FALSE);
-      memset(password, 0, MAX_LEN_NAME);
-      password[0] = '\0';
-      send_packet_authentication_reply(&aconnection, &reply);
-    } else {
-      gtk_widget_grab_focus(iinput);
-      gtk_entry_set_text(GTK_ENTRY(iinput), "");
-      gtk_set_label(imsg, _("Passwords don't match, enter password."));
-      dialog_config = NEW_PASSWORD_TYPE;
-    }
-    break;
-  case ENTER_PASSWORD_TYPE:
-    gtk_widget_set_sensitive(connw, FALSE);
-    sz_strlcpy(reply.password, gtk_entry_get_text(GTK_ENTRY(iinput)));
-    send_packet_authentication_reply(&aconnection, &reply);
-    break;
-  default:
-    assert(0);
+  sz_strlcpy(user_name, gtk_entry_get_text(GTK_ENTRY(iname)));
+  sz_strlcpy(server_host, gtk_entry_get_text(GTK_ENTRY(ihost)));
+  sscanf(gtk_entry_get_text(GTK_ENTRY(iport)), "%d", &server_port);
+
+  if(connect_to_server(user_name, server_host, server_port,
+                      errbuf, sizeof(errbuf))!=-1) {
+    gtk_notebook_set_page(uberNB,1);
+    gtk_widget_set_sensitive(top_vbox, TRUE);
+    sprintf(buf, _("Server: %s\nPort: %d"), server_host, server_port);
+    gtk_set_label(serverInfo, buf);
   }
+  else
+    append_output_window(errbuf);
 }
 
 /**************************************************************************
@@ -223,7 +189,8 @@
 }
 
 /**************************************************************************
-...
+They clicked an entry in the metaserver list -- lets set the host and port
+to match it
 **************************************************************************/
 static void meta_list_callback(GtkWidget *w, gint row, gint column)
 {
@@ -236,111 +203,638 @@
 }
 
 /**************************************************************************
-...
+They double-clicked a meta-server game, let's connect to it
 ***************************************************************************/
 static void meta_click_callback(GtkWidget *w, GdkEventButton *event, gpointer 
data)
 {
-  if (event->type==GDK_2BUTTON_PRESS) connect_callback(w, data);
+  if (event->type==GDK_2BUTTON_PRESS) connect_callback();
 }
 
 /**************************************************************************
-...
+If the player *double* clicks on a name in players_list, start the game
+***************************************************************************/ 
+static void resume_game_callback(GtkWidget *w, GdkEventButton *event, 
+                                 gpointer data)
+{
+  if (event->type == GDK_2BUTTON_PRESS) {
+    send_final_server_commands();
+    popdown_connect_dialog();
+  }
+}
+
+/************************************************************************** 
+selects the player we'll play. call from single_save radio buttons
+**************************************************************************/ 
+static void select_player_callback(GtkWidget *w, gint player, gint col, 
+                                   GdkEventButton *ev, gpointer d)
+{
+  sz_strlcpy(new_username, game.players[player].name);
+}   
+
+/**************************************************************************
+Helper function for handle_player_list_reply
+Fills in a clist with the relevant information
 **************************************************************************/
-static gint connect_deleted_callback(GtkWidget *w, GdkEvent *ev, gpointer data)
+static void fill_player_list_clist(GtkWidget *players_list, 
+                                  struct packet_player_list *packet)
 {
-  gtk_main_quit();
-  return FALSE;
+  int i;
+  char userbuf[MAX_LEN_NAME], namebuf[MAX_LEN_NAME], nationbuf[MAX_LEN_NAME], 
+       deadbuf[32], aibuf[32], flagbuf[64];
+  char *list_labels[5];
+  int max_height = 0;
+  gtk_clist_freeze(GTK_CLIST(players_list));
+  gtk_clist_clear(GTK_CLIST(players_list));
+
+  for (i=0; i<game.nplayers; i++) {
+    struct Sprite * tmp;
+    /* our packet gets freed, so this is a nice place to store it. */ 
+    sz_strlcpy(game.players[i].name, packet->name[i]);
+    sz_strlcpy(game.players[i].username, packet->username[i]);
+    game.players[i].ai.control = packet->ai[i];
+
+    my_snprintf(userbuf, sizeof(userbuf), packet->username[i]);
+    my_snprintf(namebuf, sizeof(namebuf), packet->name[i]);
+    my_snprintf(nationbuf, sizeof(nationbuf), packet->nation_name[i]);
+    my_snprintf(flagbuf, sizeof(flagbuf), packet->nation_flag[i]);
+    my_snprintf(deadbuf, sizeof(deadbuf), 
+                (packet->is_alive[i]) ? _("Alive") : _("Dead"));
+    my_snprintf(aibuf, sizeof(aibuf), 
+                (packet->ai[i]) ? _("AI") : _("Human"));
+
+    list_labels[0] = namebuf;
+    list_labels[1] = "";
+    list_labels[2] = nationbuf;
+    list_labels[3] = deadbuf;
+    list_labels[4] = aibuf;
+    
+    gtk_clist_append(GTK_CLIST(players_list), list_labels);
+
+    tmp = load_sprite(flagbuf);
+    if (tmp){
+      tmp = crop_blankspace(tmp);
+      if  (tmp->height > max_height){
+       max_height = tmp->height;
+      }
+    
+      gtk_clist_set_pixmap(GTK_CLIST(players_list), i, 1, tmp->pixmap, 
tmp->mask);
+       
+      unload_sprite(flagbuf);
+    }
+
+    if (!packet->is_alive[i]){
+      gtk_clist_set_foreground(GTK_CLIST(players_list), i, 
+                              colors_standard[COLOR_STD_BACKGROUND]);
+    }
+  }
+  for(i=0;i<6;i++){
+    gtk_clist_set_column_width(GTK_CLIST(players_list), i, 
+                              
gtk_clist_optimal_column_width(GTK_CLIST(players_list), i));
+  }
+  gtk_clist_set_row_height(GTK_CLIST(players_list), max_height + 2);
+  gtk_clist_thaw(GTK_CLIST(players_list));
+
+  gtk_clist_select_row(GTK_CLIST(players_list), 0, 0);
 }
 
-/****************************************************************
- change the connect button label on switching.
-*****************************************************************/
-static void switch_page_callback(GtkNotebook * notebook,
-                                 GtkNotebookPage * page, gint page_num,
-                                 gpointer data)
+/**************************************************************** 
+for single_save type, this is the handler for the player list packet.
+*****************************************************************/ 
+void handle_player_list_reply(struct packet_player_list *packet)
 {
-  if (page_num == LOGIN_PAGE) {
-    gtk_set_label(GTK_BUTTON(connw)->child, 
-                  dialog_config == LOGIN_TYPE ? _("Connect") : _("Next"));
+  if (packet->nplayers != 0) {
+    game.nplayers = packet->nplayers;
+  }
+  if (gtk_notebook_get_current_page(uberNB)!=0 || 
+      gtk_notebook_get_current_page(GTK_NOTEBOOK(book))!=PAGE_SAVE){
+    fill_player_list_clist(choose_players_list,packet);
+    gtk_notebook_set_page(GTK_NOTEBOOK(book), PAGE_CHOOSE);
+    gtk_notebook_set_page(uberNB, 0);
+    gtk_notebook_set_page(sidebarbook,0);
+    gtk_set_label(GTK_BUTTON(nextb)->child, _("Take Player"));
+    return;
+  }
+  /* we couldn't load the savegame, we could have gotten the name wrong, etc 
*/ 
+  if (packet->nplayers == 0
+      || strcmp(current_filename, packet->load_filename) != 0) {
+    gtk_set_label(game_label, _("Couldn't load the savegame"));
+    gtk_widget_set_sensitive(nextb, FALSE);
+    return;
   } else {
-    gtk_set_label(GTK_BUTTON(connw)->child, _("Select")); 
+    char *buf = current_filename;
+    
+    buf = strrchr(current_filename, '/');
+    gtk_set_label(game_label, ++buf);
+  }
+
+  sz_strlcpy(new_username, game.players[0].username);
+  fill_player_list_clist(players_list, packet);
+  gtk_widget_set_sensitive(nextb, TRUE);
+
+}
+
+/**************************************************************** 
+gui- specific get function
+*****************************************************************/ 
+int get_aifill_setting(void)
+{
+  return gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(aifill_spin)) + 
+         conn_list_size(&game.est_connections);
+}
+
+/**************************************************************** 
+gui- specific get function
+*****************************************************************/ 
+const char* get_ruleset_setting(void)
+{
+  return gtk_entry_get_text(GTK_ENTRY( GTK_COMBO(rulesets_combo)->entry ));
+}
+
+/**************************************************************** 
+gui- specific get function
+*****************************************************************/ 
+const char * get_aiskill_setting(void)
+{
+  int i;
+  char buf[32];
+
+  sz_strlcpy(buf, 
gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(skill_combo)->entry)));
+
+  for (i=0; i<NUM_SKILL_LEVELS; i++) {
+    if (strcmp(buf, skill_level_names_translated[i]) == 0) {
+      return skill_level_names[i];
+    }
+  }
+
+  /* an error of some kind, should never get here. */ 
+  assert(0);
+  return "easy"; /* TRANS: don't translate */ 
+}
+
+/**************************************************************** 
+gui- specific get function
+*****************************************************************/ 
+const char * get_new_username(void)
+{
+  return gtk_entry_get_text(GTK_ENTRY(login_new_entry));
+}
+
+/**************************************************************** 
+ok callback for find_load / save_filename() 
+*****************************************************************/ 
+static void file_ok(GtkWidget * w, gpointer data)
+{
+  GtkWidget *fs = GTK_WIDGET(data);
+
+  if (current_filename == NULL) {
+    current_filename = fc_malloc(1024*sizeof(char));
+  }
+
+  mystrlcpy(current_filename, 
+            gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)), 1024);
+    
+  if (fs == load_dialog) {
+    gtk_set_label(game_label, _("Loading..."));
+    send_initial_server_commands();
+    load_dialog = NULL;
+  }
+
+  if (fs == save_dialog) {
+    send_save_game(current_filename);
+    save_dialog = NULL;
+  }
+
+  /* we also need a packet challenge to the server to see if the game load
+   * was successful */ 
+
+  gtk_widget_destroy(fs);
+}   
+
+/**************************************************************** 
+cancel callback for find_load_filename()
+*****************************************************************/ 
+static void file_cancel(GtkWidget *w, gpointer data)
+{
+  GtkWidget *fs = GTK_WIDGET(data);
+
+  if (fs == load_dialog) {
+    /*    gtk_widget_set_sensitive(nextb, TRUE);*/ 
+    gtk_widget_destroy(fs);
+    load_dialog = NULL;
+  } else {
+    gtk_widget_destroy(fs);
+    save_dialog = NULL;
+  }
+}
+
+/**************************************************************** 
+finds a game to load.
+*****************************************************************/ 
+static void find_load_filename(GtkWidget *w, gpointer data)
+{
+  if (load_dialog) return;
+
+  load_dialog = gtk_file_selection_new(_("Load savegame..."));
+  
+  /*  gtk_widget_set_sensitive(nextb, FALSE);*/ 
+
+  if (current_filename != NULL) {
+    gtk_file_selection_set_filename(GTK_FILE_SELECTION(load_dialog), 
+                                    current_filename);
+  }
+
+  gtk_signal_connect(GTK_OBJECT(load_dialog), "destroy", 
+                     GTK_SIGNAL_FUNC(file_cancel), load_dialog);
+
+  
gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(load_dialog)->cancel_button), 
+                     "clicked", GTK_SIGNAL_FUNC(file_cancel), load_dialog);
+
+  gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(load_dialog)->ok_button), 
+                     "clicked", GTK_SIGNAL_FUNC(file_ok), load_dialog);
+  
+  gtk_widget_show(load_dialog);
+}
+
+/**************************************************************** 
+pop up a file dialog so the user can pick a name to save as
+*****************************************************************/ 
+void send_save_game_with_filename(void)
+{
+  if (save_dialog) return;
+  
+  save_dialog = gtk_file_selection_new(_("Save savegame..."));
+  
+  if (current_filename != NULL) {
+    gtk_file_selection_set_filename(GTK_FILE_SELECTION(save_dialog), 
+                                    current_filename);
+  }
+  
+  gtk_signal_connect(GTK_OBJECT(save_dialog), "destroy", 
+                     GTK_SIGNAL_FUNC(file_cancel), save_dialog);
+
+  
gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(save_dialog)->cancel_button), 
+                     "clicked", GTK_SIGNAL_FUNC(file_cancel), save_dialog);
+
+  gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(save_dialog)->ok_button), 
+                     "clicked", GTK_SIGNAL_FUNC(file_ok), save_dialog);
+
+  gtk_widget_show(save_dialog);
+}
+
+/************************************************************************** 
+callback for the Previous button
+**************************************************************************/ 
+static void page_prev_callback(GtkNotebook *n)
+{ 
+  switch (gtk_notebook_get_current_page(n)) {
+  case PAGE_NEW: 
+  case PAGE_SAVE:
+    gtk_notebook_set_page(n, PAGE_START);
+    if (load_dialog) {
+      file_cancel(NULL, load_dialog);
+    }
+    disconnect_from_server();
+    client_kill_server();
+  case PAGE_MULTI:
+    gtk_notebook_set_page(n, PAGE_START);
+    gtk_widget_set_sensitive(prevb, FALSE);
+    gtk_set_label(GTK_BUTTON(nextb)->child, _("Next >"));
+    gtk_widget_set_sensitive(nextb, TRUE);
+    gtk_widget_grab_default(nextb);
+    break;
+  case PAGE_CHOOSE:
+    disconnect_from_server();
+    gtk_notebook_set_page(n, PAGE_MULTI);
+    gtk_set_label(GTK_BUTTON(nextb)->child, _("Connect"));
+    gtk_widget_set_sensitive(nextb, TRUE);
+    gtk_widget_set_sensitive(prevb, TRUE);
+    gtk_widget_grab_default(nextb);
+    break;
+  default:
+    break;
+  }
+}
+
+/************************************************************************** 
+callback for the Next button
+**************************************************************************/ 
+static void page_next_callback(GtkNotebook *n)
+{
+  char buf[256], host[128];
+  struct packet_generic_message packet;
+  switch (gtk_notebook_get_current_page(n)) {
+  case PAGE_START:
+    gtk_widget_set_sensitive(prevb, TRUE);
+    switch(client_start_type) {
+    case START_SINGLE_NEW:
+      if (!client_start_server()) {
+        /* switch to multiplayer mode */ 
+        client_start_type = START_MULTI;
+        gtk_widget_set_sensitive(prevb, FALSE);
+        goto multi;
+      } else {
+       gethostname(host, 128);
+       sprintf(buf, _("Server: %s\nPort: %d"), host, server_port);
+       gtk_set_label(serverInfo, buf);
+        send_initial_server_commands();
+        gtk_set_label(GTK_BUTTON(nextb)->child, _("(Set and) Start Game"));
+        gtk_notebook_set_page(n, PAGE_NEW);
+       client_has_hack = TRUE; /*Not pretty, but it makes the initial options 
dialog work,
+                                 and if we don't have hack, we're not sticking 
around
+                                 for it to matter*/
+       send_report_request(REPORT_SERVER_OPTIONS1);
+       my_snprintf(packet.message, MAX_LEN_MSG, "/set autotoggle 1");
+       send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);
+      }
+      break;
+    case START_SINGLE_SAVE:
+      if (!client_start_server()) {
+        /* switch to multiplayer mode */ 
+        client_start_type = START_MULTI;
+        gtk_widget_set_sensitive(prevb, FALSE);
+        goto multi;
+      } else {
+       gethostname(host, 128);
+       sprintf(buf, _("Server: %s\nPort: %d"), host, server_port);
+       gtk_set_label(serverInfo, buf);
+        find_load_filename(NULL, NULL);
+        gtk_widget_set_sensitive(nextb, FALSE);
+        gtk_set_label(GTK_BUTTON(nextb)->child, _("Resume Game"));
+        gtk_notebook_set_page(n, PAGE_SAVE); 
+      }
+      break;
+    case START_MULTI:
+      multi:;
+      gtk_set_label(GTK_BUTTON(nextb)->child, _("Connect"));
+      gtk_notebook_set_page(n, PAGE_MULTI); 
+      break;
+    }
+    break;
+  case PAGE_NEW:
+  case PAGE_SAVE:
+    gtk_signal_emit_by_name(GTK_OBJECT(init_opts_frame), "show");
+    send_final_server_commands();
+    popdown_connect_dialog();
+    break;
+  case PAGE_MULTI:
+    connect_callback();
+    break;
+  case PAGE_CHOOSE:
+    my_snprintf(packet.message, MAX_LEN_MSG, "/take \"%s\" \"%s\"", 
+               gtk_entry_get_text(GTK_ENTRY(iname)), new_username);
+    send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);
+    popdown_connect_dialog();
+  default:
+    break;
   }
 }
 
+/************************************************************************** 
+callback for start page radio buttons. selects client_start_type.
+**************************************************************************/ 
+static void starttype_callback(GtkWidget *w, gpointer data)
+{
+  if (GTK_TOGGLE_BUTTON(starttype_radio[START_SINGLE_NEW])->active) {
+    client_start_type = START_SINGLE_NEW;
+  } else if (GTK_TOGGLE_BUTTON(starttype_radio[START_SINGLE_SAVE])->active) {
+    client_start_type = START_SINGLE_SAVE;
+  } else { /* START_MULTI */ 
+    client_start_type = START_MULTI;
+  }
+}
+
+/************************************************************************** 
+create and popup main connect dialog FIXME: rename.
+**************************************************************************/
+static gint connect_deleted_callback(GtkWidget *w, GdkEvent *ev, gpointer data)
+{
+  client_kill_server();
+  gtk_main_quit();
+  return FALSE;
+}
+
 /**************************************************************************
 ...
 **************************************************************************/
 void gui_server_connect(void)
 {
-  GtkWidget *label, *table, *scrolled, *vbox, *update;
-  static const char *titles_[6]= {N_("Server Name"), N_("Port"), N_("Version"),
-                                 N_("Status"), N_("Players"), N_("Comment")};
-  static char **titles;
-  char buf [256];
   int i;
 
-  if (dialog) {
+  GtkWidget *multibook, *table, *label, *button,
+            *scrolled, *update, *hbox, * vbox;
+  GSList *group = NULL;
+  GList *popdown_list = NULL;
+  
+  static const char *titles_[6] = { N_("Server Name"), N_("Port"), 
+                                    N_("Version"),     N_("Status"), 
+                                    N_("Players"), N_("Comment") };
+  static const char *ptitles_[5] = { N_("Name"), N_("Flag"), 
+                                    N_("Nation"), N_("Status"), N_("Type") };
+  static char **titles, **ptitles;
+  char buf[256];
+
+  if (GTK_IS_NOTEBOOK(book) && GTK_WIDGET_VISIBLE(book)) {
+    gtk_notebook_set_page(GTK_NOTEBOOK(book), PAGE_START);
+    gtk_widget_set_sensitive(prevb, FALSE);
+    gtk_set_label(GTK_BUTTON(nextb)->child, _("Next >"));
+    gtk_notebook_set_page(sidebarbook, 0);
+    gtk_notebook_set_page(uberNB, 0);
+    gtk_set_label(serverInfo, _("Not connected\n"));
     return;
   }
 
-  dialog_config = LOGIN_TYPE;
-
   if (!titles) titles = intl_slist(6, titles_);
+  if (!ptitles) ptitles = intl_slist(5, ptitles_);
+  
+  book = gtk_notebook_new();
+  gtk_notebook_set_show_tabs(GTK_NOTEBOOK(book), FALSE);
+  gtk_notebook_set_show_border(GTK_NOTEBOOK(book), FALSE);
+  
+
+  gtk_box_pack_start(GTK_BOX(opening_dialog), book, TRUE, TRUE, 0);
+
+
+
+  /* startup page */ 
+
+  vbox = gtk_vbox_new(TRUE, 2);
+
+  starttype_radio[START_SINGLE_NEW] = gtk_radio_button_new_with_label(group, 
+                                            _("Start new game"));
+  group = gtk_radio_button_group(GTK_RADIO_BUTTON(
+                                           starttype_radio[START_SINGLE_NEW]));
+  gtk_box_pack_start(GTK_BOX(vbox), starttype_radio[START_SINGLE_NEW], 
+                     TRUE, TRUE, 0);
+  gtk_signal_connect(GTK_OBJECT(starttype_radio[START_SINGLE_NEW]), 
+                     "toggled", GTK_SIGNAL_FUNC(starttype_callback), NULL);
+
+  starttype_radio[START_SINGLE_SAVE] = gtk_radio_button_new_with_label(group, 
+                                       _("Load saved game"));
+  group = gtk_radio_button_group(GTK_RADIO_BUTTON(
+                                          starttype_radio[START_SINGLE_SAVE]));
+  gtk_box_pack_start(GTK_BOX(vbox), starttype_radio[START_SINGLE_SAVE], 
+                     TRUE, TRUE, 0);
+  gtk_signal_connect(GTK_OBJECT(starttype_radio[START_SINGLE_SAVE]), 
+                     "toggled", GTK_SIGNAL_FUNC(starttype_callback), NULL);
+
+  starttype_radio[START_MULTI] = gtk_radio_button_new_with_label(group, 
+                                       _("Join networked game"));
+  group = gtk_radio_button_group(GTK_RADIO_BUTTON(
+                                                starttype_radio[START_MULTI]));
+  gtk_box_pack_start(GTK_BOX(vbox), starttype_radio[START_MULTI], 
+                     TRUE, TRUE, 0);
+  gtk_signal_connect(GTK_OBJECT(starttype_radio[START_MULTI]), 
+                     "toggled", GTK_SIGNAL_FUNC(starttype_callback), NULL);
+
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
+                               starttype_radio[client_start_type]), TRUE);
+
+  gtk_notebook_append_page(GTK_NOTEBOOK(book), vbox, NULL);
+
+  /* new game page */ 
+
+  vbox = gtk_vbox_new(FALSE, 2);
+  gtk_notebook_append_page(GTK_NOTEBOOK(book), vbox, NULL);
+
+  table = gtk_table_new(3, 3, FALSE);
+  gtk_table_set_row_spacings(GTK_TABLE(table), 2);
+  gtk_table_set_col_spacings(GTK_TABLE(table), 5);
+  gtk_container_border_width(GTK_CONTAINER(table), 5);
+  gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
+
+  label = gtk_label_new(_("Select number of AI players:"));
+  gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, 0, 0, 0, 0);
+  gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+
+  aifill_adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, MAX_NUM_PLAYERS - 1, 
+                                                 1, 1, 0));
+  aifill_spin = gtk_spin_button_new(aifill_adj, 0.3, 0);
+  gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(aifill_spin), TRUE);
+  gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(aifill_spin), FALSE);
+  gtk_spin_button_set_snap_to_ticks(GTK_SPIN_BUTTON(aifill_spin), TRUE);
+  gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(aifill_spin), 
+                                    GTK_UPDATE_IF_VALID);
+  gtk_signal_connect_object(GTK_OBJECT(aifill_spin), "activate", 
+                           GTK_SIGNAL_FUNC(page_next_callback), 
GTK_OBJECT(book));
+  gtk_table_attach_defaults(GTK_TABLE(table), aifill_spin, 1, 2, 1, 2);
+
+  label = gtk_label_new(_("Select AI skill level:"));
+  gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, 0, 0, 0, 0);
+  gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+
+  for (i = 0; i < NUM_SKILL_LEVELS; i++) {
+    popdown_list = g_list_append(popdown_list, 
+                                 (char*)skill_level_names_translated[i]);
+  }
 
-  gtk_widget_set_sensitive(turn_done_button, FALSE);
-  gtk_widget_set_sensitive(top_vbox, FALSE);
+  skill_combo = gtk_combo_new();
+  gtk_combo_set_popdown_strings(GTK_COMBO(skill_combo), popdown_list);
+  gtk_table_attach_defaults(GTK_TABLE(table), skill_combo, 1, 2, 2, 3);
+  gtk_entry_set_editable(GTK_ENTRY( GTK_COMBO(skill_combo)->entry ), 0);
+
+  label = gtk_label_new(_("Select Ruleset:"));
+  gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4, 0, 0, 0, 0);
+  rulesets_combo=gtk_combo_new();
+  gtk_table_attach_defaults(GTK_TABLE(table), rulesets_combo, 1, 2, 3, 4);
 
-  dialog=gtk_dialog_new();
-  gtk_signal_connect(GTK_OBJECT(dialog),"delete_event",
-       GTK_SIGNAL_FUNC(connect_deleted_callback), NULL);
   
-  gtk_window_set_title(GTK_WINDOW(dialog), _(" Connect to Freeciv Server"));
 
-  book = gtk_notebook_new ();
-  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), book, TRUE, TRUE, 0);
+  init_opts_frame = gtk_frame_new(_("Additional Options (feel free to ignore 
these)"));
+  gtk_container_set_border_width(GTK_CONTAINER(init_opts_frame), 5);
+  gtk_box_pack_start(GTK_BOX(vbox), init_opts_frame, TRUE, TRUE, 0);
+
+  button = gtk_button_new_with_label(_("Configure Metaserver"));
+  gtk_signal_connect(GTK_OBJECT(button), "clicked", 
+                     GTK_SIGNAL_FUNC(control_metaserver), NULL);
+  gtk_box_pack_end(GTK_BOX(vbox), button, FALSE, FALSE, 2);
+
+  /* save game page */ 
+
+  vbox = gtk_vbox_new(FALSE, 2);
+  gtk_notebook_append_page(GTK_NOTEBOOK(book), vbox, NULL);
+
+  hbox = gtk_hbox_new(FALSE, 10);
+  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+  game_label = gtk_label_new("");
+  gtk_box_pack_start(GTK_BOX(hbox), game_label, FALSE, FALSE, 6);
+
+  button = gtk_button_new_with_label(_("Load other game..."));
+  GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+  gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
+  gtk_signal_connect(GTK_OBJECT(button), 
+                     "clicked", GTK_SIGNAL_FUNC(find_load_filename), NULL);
+  label = gtk_label_new(_("These nations are in the savegame, choose which to 
play as:"));
+  gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
+  hbox = gtk_hbox_new(0, 0); /* Use this hbox to get left- alignment */ 
+  gtk_box_pack_start(GTK_BOX(hbox), label, 0, 0, 0);
+  gtk_box_pack_start(GTK_BOX(vbox), hbox, 0, 0, 0);
+
+  scrolled = gtk_scrolled_window_new(NULL, NULL);
+  gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), 
+                                 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
 
+  players_list = gtk_clist_new_with_titles(5, ptitles);
+  gtk_container_add(GTK_CONTAINER(scrolled), players_list);
+  gtk_clist_column_titles_passive(GTK_CLIST(players_list));
+  gtk_clist_set_shadow_type(GTK_CLIST(players_list), GTK_SHADOW_NONE);
 
-  label=gtk_label_new(_("Freeciv Server Selection"));
 
-  vbox=gtk_vbox_new(FALSE, 2);
-  gtk_notebook_append_page (GTK_NOTEBOOK (book), vbox, label);
+  gtk_signal_connect(GTK_OBJECT(players_list), "select_row", 
+                GTK_SIGNAL_FUNC(select_player_callback), NULL);
+
+  gtk_signal_connect(GTK_OBJECT(players_list), "button_press_event", 
+                     GTK_SIGNAL_FUNC(resume_game_callback), NULL);
+
+  /* multiplayer page */ 
+
+  multibook = gtk_notebook_new();
+  gtk_notebook_append_page(GTK_NOTEBOOK(book), multibook, NULL);
 
-  table = gtk_table_new (5, 2, FALSE);
+  vbox = gtk_vbox_new(FALSE, 2);
+  label = gtk_label_new(_("Freeciv Server Selection"));
+  gtk_notebook_append_page(GTK_NOTEBOOK(multibook), vbox, label);
+
+
+  table = gtk_table_new (4, 2, FALSE);
   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
   gtk_table_set_col_spacings (GTK_TABLE (table), 5);
   gtk_container_border_width (GTK_CONTAINER (table), 5);
   gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, TRUE, 0);
 
-  imsg = gtk_label_new(NULL);
-  gtk_table_attach_defaults(GTK_TABLE (table), imsg, 1, 2, 0, 1);
-  gtk_label_set_line_wrap(GTK_LABEL(imsg), TRUE);
-  gtk_misc_set_alignment(GTK_MISC(imsg), 0.0, 0.5);
-
-  ilabel = gtk_label_new(_("Login:"));
-  gtk_table_attach (GTK_TABLE (table), ilabel, 0, 1, 1, 2, 0, 0, 0, 0);
-  gtk_misc_set_alignment (GTK_MISC (ilabel), 0.0, 0.5);
-
-  iinput=gtk_entry_new();
-  gtk_entry_set_text(GTK_ENTRY(iinput), user_name);
-  gtk_table_attach_defaults (GTK_TABLE (table), iinput, 1, 2, 1, 2);
+  label=gtk_label_new(_("Name:"));
+  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, 0, 0, 0, 0);
+  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+  iname=gtk_entry_new();
+  gtk_entry_set_text(GTK_ENTRY(iname), user_name);
+  gtk_table_attach_defaults (GTK_TABLE (table), iname, 1, 2, 0, 1);
+  gtk_signal_connect_object(GTK_OBJECT(iname), "activate", 
+                           GTK_SIGNAL_FUNC(page_next_callback), 
GTK_OBJECT(book));
 
   label=gtk_label_new(_("Host:"));
-  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, 0, 0, 0, 0);
+  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, 0, 0, 0, 0);
   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
 
   ihost=gtk_entry_new();
   gtk_entry_set_text(GTK_ENTRY(ihost), server_host);
-  gtk_table_attach_defaults (GTK_TABLE (table), ihost, 1, 2, 2, 3);
+  gtk_table_attach_defaults (GTK_TABLE (table), ihost, 1, 2, 1, 2);
+  gtk_signal_connect_object(GTK_OBJECT(ihost), "activate", 
+                           GTK_SIGNAL_FUNC(page_next_callback), 
GTK_OBJECT(book));
 
   label=gtk_label_new(_("Port:"));
-  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4, 0, 0, 0, 0);
+  gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, 0, 0, 0, 0);
   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
 
   my_snprintf(buf, sizeof(buf), "%d", server_port);
 
   iport=gtk_entry_new();
   gtk_entry_set_text(GTK_ENTRY(iport), buf);
-  gtk_table_attach_defaults (GTK_TABLE (table), iport, 1, 2, 3, 4);
+  gtk_table_attach_defaults (GTK_TABLE (table), iport, 1, 2, 2, 3);
+  gtk_signal_connect_object(GTK_OBJECT(iport), "activate", 
+                           GTK_SIGNAL_FUNC(page_next_callback), 
GTK_OBJECT(book));
+
 
 #if IS_BETA_VERSION
   {
@@ -356,14 +850,13 @@
 
     style->fg[GTK_STATE_NORMAL] = *colors_standard[COLOR_STD_RED];
     gtk_widget_set_style(label2, style);
-    gtk_table_attach_defaults(GTK_TABLE (table), label2, 0, 2, 4, 5);
+    gtk_table_attach_defaults(GTK_TABLE (table), label2, 0, 2, 3, 4);
   }
 #endif
 
-  label=gtk_label_new(_("Metaserver"));
-
-  vbox=gtk_vbox_new(FALSE, 2);
-  gtk_notebook_append_page (GTK_NOTEBOOK (book), vbox, label);
+  vbox = gtk_vbox_new(FALSE, 2);
+  label = gtk_label_new(_("Metaserver"));
+  gtk_notebook_append_page(GTK_NOTEBOOK(multibook), vbox, label);
 
   server_clist = gtk_clist_new_with_titles(6, titles);
 
@@ -380,52 +873,6 @@
   update=gtk_button_new_with_label(_("Update"));
   gtk_box_pack_start(GTK_BOX(vbox), update, FALSE, FALSE, 2);
 
-  connw = gtk_button_new_with_label(_("Connect"));
-  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), connw,
-       TRUE, TRUE, 0);
-  GTK_WIDGET_SET_FLAGS(connw, GTK_CAN_DEFAULT);
-  gtk_widget_grab_default(connw);
-
-  quitw=gtk_button_new_with_label(_("Quit"));
-  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), quitw,
-       TRUE, TRUE, 0);
-  GTK_WIDGET_SET_FLAGS(quitw, GTK_CAN_DEFAULT);
-
-  gtk_widget_grab_focus (iinput);
-
-  /*  default sort column  */
-  gtk_clist_set_sort_column(GTK_CLIST(server_clist), DEFAULT_SORT_COLUMN);
-
-
-  gtk_widget_show_all(GTK_DIALOG(dialog)->vbox);
-  gtk_widget_show_all(GTK_DIALOG(dialog)->action_area);
-
-  gtk_widget_set_usize(dialog, 450, 250);
-  gtk_set_relative_position(toplevel, dialog, 50, 50);
-
-  if (auto_connect) {
-     gtk_widget_hide(dialog);
-  } else {
-     gtk_widget_show(dialog);
-  }
-
-  /* connect all the signals here, so that we can't send 
-   * packets to the server until the dialog is up (which 
-   * it may not be on very slow machines) */
-
-  gtk_signal_connect(GTK_OBJECT(book), "switch-page",
-                     GTK_SIGNAL_FUNC(switch_page_callback), NULL);
-  gtk_signal_connect(GTK_OBJECT(iinput), "activate",
-                     GTK_SIGNAL_FUNC(connect_callback), NULL);
-  gtk_signal_connect(GTK_OBJECT(ihost), "activate",
-                     GTK_SIGNAL_FUNC(connect_callback), NULL);
-  gtk_signal_connect(GTK_OBJECT(iport), "activate",
-                     GTK_SIGNAL_FUNC(connect_callback), NULL);
-  gtk_signal_connect(GTK_OBJECT(connw), "clicked",
-                     GTK_SIGNAL_FUNC(connect_callback), NULL);
-  gtk_signal_connect(GTK_OBJECT(quitw), "clicked",
-                     GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
-
   gtk_signal_connect(GTK_OBJECT(server_clist), "select_row",
                     GTK_SIGNAL_FUNC(meta_list_callback), NULL);
   gtk_signal_connect(GTK_OBJECT(server_clist), "button_press_event",
@@ -434,12 +881,67 @@
                     GTK_SIGNAL_FUNC(meta_update_callback),
                     (gpointer) server_clist);
 
+  /* Non-savegame Select Nation page */
+
+  vbox=gtk_vbox_new(FALSE, 2);
+  gtk_notebook_append_page(GTK_NOTEBOOK(book), vbox, NULL);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    gtk_label_new(_("These nations are present in the game you 
have "
+                                    "joined.  Choose one to play as:")),
+                    0, 0, 0);
+  choose_players_list = gtk_clist_new_with_titles(5, ptitles);
+  gtk_clist_column_titles_passive(GTK_CLIST(choose_players_list));
+  gtk_clist_set_shadow_type(GTK_CLIST(choose_players_list), GTK_SHADOW_NONE);
+  scrolled=gtk_scrolled_window_new(NULL,NULL);
+  gtk_container_add(GTK_CONTAINER(scrolled), choose_players_list);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
+                                GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+  gtk_box_pack_end(GTK_BOX(vbox), scrolled, 1, 1, 1);
+  gtk_signal_connect(GTK_OBJECT(choose_players_list), "select_row", 
+                GTK_SIGNAL_FUNC(select_player_callback), NULL);
+
+  /* Quit and prev/next screen buttons */ 
+  
+  hbox = gtk_hbox_new(FALSE, 2);
+  gtk_box_pack_end(GTK_BOX(opening_dialog), hbox, FALSE, FALSE, 0);
+
+  button = gtk_button_new_with_label(_("Quit"));
+  gtk_box_pack_start(GTK_BOX(hbox), button, 
+                      TRUE, TRUE, 0);
+  GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+  
+  gtk_signal_connect(GTK_OBJECT(button), "clicked", 
+                    GTK_SIGNAL_FUNC(connect_deleted_callback), NULL);
+  
+  prevb = gtk_button_new_with_label(_("< Back"));
+  GTK_WIDGET_SET_FLAGS(prevb, GTK_CAN_DEFAULT);
+  gtk_box_pack_start(GTK_BOX(hbox), prevb, 
+                      TRUE, TRUE, 0);
+  gtk_widget_set_sensitive(prevb, FALSE);
+  
+  gtk_signal_connect_object(GTK_OBJECT(prevb), "clicked", 
+                           GTK_SIGNAL_FUNC(page_prev_callback), 
GTK_OBJECT(book));
+  
+  nextb = gtk_button_new_with_label(_("Next >"));
+  GTK_WIDGET_SET_FLAGS(nextb, GTK_CAN_DEFAULT);
+  gtk_box_pack_start(GTK_BOX(hbox), nextb, 
+                    TRUE, TRUE, 0);
+  gtk_widget_grab_default(nextb);
+  
+  gtk_signal_connect_object(GTK_OBJECT(nextb), "clicked", 
+                           GTK_SIGNAL_FUNC(page_next_callback), 
GTK_OBJECT(book));
+  
+  /*  default sort column  */
+  gtk_clist_set_sort_column(GTK_CLIST(server_clist), DEFAULT_SORT_COLUMN);
+
   /*  all columns are clickable  */
   for (i = 0; i <6 ; i++) {
     gtk_signal_connect(GTK_OBJECT(GTK_CLIST(server_clist)->column[i].button),
                       "clicked", GTK_SIGNAL_FUNC(sort_servers_callback),
                       GINT_TO_POINTER(i));
   }
+    
+  gtk_widget_show_all(opening_dialog);
 }
 
 /**************************************************************************
@@ -540,6 +1042,8 @@
 {
   char buf[512];
 
+  gtk_notebook_set_page(uberNB, 1);
+
   my_snprintf(buf, sizeof(buf),
              _("Auto-connecting to server \"%s\" at port %d "
                "as \"%s\" every %d.%d second(s) for %d times"),
@@ -555,7 +1059,46 @@
            server_host, server_port, user_name, buf);
     gtk_exit(EXIT_FAILURE);
   }
+
   if (try_to_autoconnect(NULL)) {
     gtk_timeout_add(AUTOCONNECT_INTERVAL, try_to_autoconnect, NULL);
   }
+
+}
+
+
+
+void close_connection_dialog(void){
+}
+
+
+static void passwd_ok(GtkWidget *w, gpointer *ent)
+{
+  struct packet_authentication_reply reply;
+  sz_strlcpy(reply.password, gtk_entry_get_text(GTK_ENTRY(ent)));
+  send_packet_authentication_reply(&aconnection, &reply);
+  gtk_widget_destroy(gtk_widget_get_toplevel(w));
+}
+   
+
+void handle_authentication_request(struct packet_authentication_request *
+                                   packet)
+{
+  GtkWidget *win,*but,*vbox, *ent;
+  win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_modal(GTK_WINDOW(win), TRUE);
+  vbox = gtk_vbox_new(0, 0);
+  gtk_container_add(GTK_CONTAINER(win), vbox);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    gtk_label_new(packet->message),
+                    0, 0, 0);
+  ent = gtk_entry_new();
+  gtk_entry_set_visibility(GTK_ENTRY(ent), FALSE);
+  gtk_box_pack_start(GTK_BOX(vbox), ent, 0, 0, 0);
+  but = gtk_button_new_with_label(_("OK"));
+  gtk_box_pack_end(GTK_BOX(vbox), but, 0, 0, 0);
+  gtk_signal_connect(GTK_OBJECT(but), "clicked",
+                    GTK_SIGNAL_FUNC(passwd_ok), ent);
+  gtk_widget_show_all(win);
 }
+                    
Index: client/gui-gtk/dialogs.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/dialogs.c,v
retrieving revision 1.131
diff -u -r1.131 dialogs.c
--- client/gui-gtk/dialogs.c    3 Oct 2003 05:55:15 -0000       1.131
+++ client/gui-gtk/dialogs.c    12 Oct 2003 10:06:44 -0000
@@ -37,6 +37,7 @@
 
 #include "civclient.h"
 #include "clinet.h"
+#include "connectdlg_common.h"
 #include "control.h"
 #include "options.h"
 #include "packhand.h"
@@ -167,13 +168,18 @@
   int dest_x, dest_y;
   enum unit_activity what;
 };
-                    
+
 /****************************************************************
 ...
 *****************************************************************/
 static void notify_command_callback(GtkWidget *w, GtkWidget *t)
 {
-  popdown_notify_dialog();
+  if (gtk_widget_get_toplevel(w)== notify_dialog_shell){
+    popdown_notify_dialog();
+  }else{
+    /* Probably multiple dialogs -- just take care of ours */ 
+    gtk_widget_destroy(gtk_widget_get_toplevel(w));
+  }
 }
 
 /****************************************************************
@@ -2438,8 +2444,10 @@
   struct packet_alloc_nation packet;
 
   if(w == races_quit_command) {
+    client_kill_server();
     exit(EXIT_SUCCESS);
   } else if(w == races_disc_command) {
+    client_kill_server();
     popdown_races_dialog();
     disconnect_from_server();
     return;
Index: client/gui-gtk/gui_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/gui_main.c,v
retrieving revision 1.139
diff -u -r1.139 gui_main.c
--- client/gui-gtk/gui_main.c   6 Oct 2003 14:27:59 -0000       1.139
+++ client/gui-gtk/gui_main.c   12 Oct 2003 10:06:44 -0000
@@ -45,6 +45,7 @@
 #include "climisc.h"
 #include "clinet.h"
 #include "control.h"
+#include "connectdlg_common.h"
 #include "freeciv.ico"
 #include "helpdata.h"                   /* boot_help_texts() */
 #include "options.h"
@@ -68,6 +69,9 @@
 
 const char *client_string = "gui-gtk";
 
+GtkWidget *opening_dialog;
+GtkNotebook *uberNB;
+
 GtkWidget *map_canvas;                  /* GtkDrawingArea */
 GtkWidget *map_horizontal_scrollbar;
 GtkWidget *map_vertical_scrollbar;
@@ -122,6 +126,10 @@
 static GtkWidget *unit_below_pixmap_button[MAX_NUM_UNITS_BELOW];
 static GtkWidget *more_arrow_pixmap;
 
+static GtkWidget *conn_clist;
+GtkNotebook *sidebarbook;
+GtkWidget *serverInfo;
+
 static int unit_ids[MAX_NUM_UNITS_BELOW];  /* ids of the units icons in 
                                             * information display: (or 0) */
 GtkText   *main_message_area;
@@ -186,6 +194,10 @@
 **************************************************************************/
 static gint keyboard_handler(GtkWidget *w, GdkEventKey *ev)
 {
+  if (gtk_notebook_get_current_page(uberNB)!=1){
+    /*Game isn't playing!*/
+    return 0;
+  }
   /* inputline history code */
   if (GTK_WIDGET_HAS_FOCUS(inputline) || !GTK_WIDGET_IS_SENSITIVE(top_vbox)) {
     const char *data = NULL;
@@ -523,9 +535,11 @@
 {
   GtkWidget *box, *ebox, *hbox, *vbox;
   GtkWidget *avbox, *ahbox;
+  GtkWidget *ovbox;
   GtkWidget *frame, *table, *paned, *menubar, *text;
   GtkStyle  *text_style, *style;
   int i;
+  char *titles[2]={_("Name"), _("From")};
 
   /* the window is divided into two panes. "top" and "message window" */ 
   paned = gtk_vpaned_new();
@@ -575,10 +589,22 @@
   ahbox = detached_widget_new();
   gtk_container_add(GTK_CONTAINER(vbox), ahbox);
   avbox = detached_widget_fill(ahbox);
+  sidebarbook=(GtkNotebook*)gtk_notebook_new();
+  gtk_notebook_set_show_tabs(sidebarbook, FALSE);
+  gtk_notebook_set_show_border(sidebarbook, FALSE);
+  gtk_box_pack_start(GTK_BOX(avbox), GTK_WIDGET(sidebarbook), TRUE, TRUE, 0);
+  vbox = gtk_vbox_new(FALSE, 0);
+  gtk_notebook_append_page(sidebarbook, vbox, NULL);
+  serverInfo = gtk_label_new(_("Not connected\n"));
+  gtk_box_pack_start(GTK_BOX(vbox), serverInfo, FALSE, FALSE, 0);
+  conn_clist = gtk_clist_new_with_titles(2, titles);
+  gtk_box_pack_start(GTK_BOX(vbox), conn_clist, FALSE, FALSE, 0);
+  ovbox = gtk_vbox_new(FALSE, 0);
+  gtk_notebook_append_page(sidebarbook, ovbox, NULL);
 
   /* Info on player's civilization */
   frame = gtk_frame_new(NULL);
-  gtk_box_pack_start(GTK_BOX(avbox), frame, FALSE, FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(ovbox), frame, FALSE, FALSE, 0);
 
   main_frame_civ_name = frame;
 
@@ -599,7 +625,7 @@
 
   /* make a box so the table will be centered */
   box = gtk_hbox_new(FALSE, 0);
-  gtk_box_pack_start(GTK_BOX(avbox), box, FALSE, FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(ovbox), box, FALSE, FALSE, 0);
 
   table = gtk_table_new(3, 10, FALSE);
   gtk_table_set_row_spacing(GTK_TABLE(table), 0, 0);
@@ -675,13 +701,13 @@
   /* Selected unit status */
 
   unit_info_frame = gtk_frame_new(NULL);
-  gtk_box_pack_start(GTK_BOX(avbox), unit_info_frame, FALSE, FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(ovbox), unit_info_frame, FALSE, FALSE, 0);
     
   unit_info_label = gtk_label_new("\n\n\n");
   gtk_container_add(GTK_CONTAINER(unit_info_frame), unit_info_label);
 
   box = gtk_hbox_new(FALSE,0);
-  gtk_box_pack_start(GTK_BOX(avbox), box, FALSE, FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(ovbox), box, FALSE, FALSE, 0);
 
   table = gtk_table_new(0, 0, FALSE);
   gtk_box_pack_start(GTK_BOX(box), table, FALSE, FALSE, 5);
@@ -694,8 +720,18 @@
 
   /* Map canvas and scrollbars */
 
+  uberNB = (GtkNotebook*)gtk_notebook_new();
+  gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(uberNB), TRUE, TRUE, 0);
+  gtk_notebook_set_show_tabs(uberNB, FALSE);
+  gtk_notebook_set_show_border(uberNB, FALSE);
+  frame = gtk_frame_new(_("Welcome to Freeciv"));
+  gtk_container_set_border_width(GTK_CONTAINER(frame),10);
+  gtk_notebook_append_page(uberNB, frame, NULL);
+  opening_dialog = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(frame), opening_dialog);
+
   table = gtk_table_new(2, 2, FALSE);
-  gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 0);
+  gtk_notebook_append_page(uberNB, table, NULL);
 
   frame = gtk_frame_new(NULL);
   gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 0, 1,
@@ -840,6 +876,8 @@
   gtk_window_set_title(GTK_WINDOW (toplevel), _("Freeciv"));
 
   gtk_signal_connect(GTK_OBJECT(toplevel), "delete_event",
+                     GTK_SIGNAL_FUNC(client_kill_server), NULL);
+  gtk_signal_connect_after(GTK_OBJECT(toplevel), "delete_event",
                      GTK_SIGNAL_FUNC(gtk_main_quit), NULL);
 
   display_color_type = get_visual();
@@ -971,7 +1009,15 @@
 ***************************************************************************/
 void update_conn_list_dialog(void)
 {
-  /* PORTME */
+  char *row[2];
+  gtk_clist_clear(GTK_CLIST(conn_clist));
+  conn_list_iterate(game.est_connections, pconn) {
+    row[0]=pconn->username;
+    row[1]=pconn->addr;
+    gtk_clist_append(GTK_CLIST(conn_clist), row);
+  } conn_list_iterate_end;
+  gtk_clist_set_column_width(GTK_CLIST(conn_clist), 0,
+                 gtk_clist_optimal_column_width(GTK_CLIST(conn_clist),0));
 }
 
 /**************************************************************************
Index: client/gui-gtk/menu.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/menu.c,v
retrieving revision 1.79
diff -u -r1.79 menu.c
--- client/gui-gtk/menu.c       15 Sep 2003 16:05:35 -0000      1.79
+++ client/gui-gtk/menu.c       12 Oct 2003 10:06:45 -0000
@@ -32,6 +32,8 @@
 
 #include "civclient.h"
 #include "clinet.h"
+#include "connectdlg_g.h"
+#include "connectdlg_common.h"
 #include "control.h"
 #include "options.h"
 #include "packhand.h"
@@ -71,9 +73,14 @@
   MENU_GAME_SAVE_SETTINGS,
   MENU_GAME_SERVER_OPTIONS1,
   MENU_GAME_SERVER_OPTIONS2,
+  MENU_GAME_META_SERVER, 
+  MENU_GAME_SAVE_GAME, 
+  MENU_GAME_AUTOSAVE,
+  MENU_GAME_SAVE_GAME_NAME, 
   MENU_GAME_OUTPUT_LOG,
   MENU_GAME_CLEAR_OUTPUT,
   MENU_GAME_DISCONNECT,
+  MENU_GAME_END, 
   MENU_GAME_QUIT,
   
   MENU_KINGDOM_TAX_RATE,
@@ -137,6 +144,7 @@
   MENU_REPORT_MESSAGES,
   MENU_REPORT_DEMOGRAPHIC,
   MENU_REPORT_SPACESHIP,
+  MENU_REPORT_SCORE, 
 
   MENU_HELP_LANGUAGES,
   MENU_HELP_CONNECTING,
@@ -163,6 +171,33 @@
 /****************************************************************
 ...
 *****************************************************************/
+static void send_score_request(){
+  struct packet_generic_message pack;
+  strcpy(pack.message, "/ score\n");
+  send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &pack);
+}
+
+/**************************************************************** 
+Quits any server we have hack on (even if we didn't start it)
+*****************************************************************/ 
+static void end_game(){
+  struct packet_generic_message pack;
+  strcpy(pack.message, "/quit\n");
+  send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &pack);
+}
+
+/**************************************************************** 
+Saves like auto-save
+*****************************************************************/ 
+static void send_autosave_game(){
+  struct packet_generic_message pack;
+  strcpy(pack.message, "/save\n");
+  send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &pack);
+}
+
+/**************************************************************** 
+...
+*****************************************************************/ 
 static void game_menu_callback(gpointer callback_data,
                               guint callback_action, GtkWidget *widget)
 {
@@ -182,6 +217,18 @@
   case MENU_GAME_SERVER_OPTIONS2:
     send_report_request(REPORT_SERVER_OPTIONS2);
     break;
+  case MENU_GAME_META_SERVER:
+    control_metaserver(NULL, NULL);
+    break;
+  case MENU_GAME_AUTOSAVE:
+    send_autosave_game();
+    break;
+  case MENU_GAME_SAVE_GAME:
+    send_save_game(NULL);
+    break;
+  case MENU_GAME_SAVE_GAME_NAME:
+    send_save_game_with_filename();
+    break;
   case MENU_GAME_OUTPUT_LOG:
     log_output_window();
     break;
@@ -189,10 +236,22 @@
     clear_output_window();
     break;
   case MENU_GAME_DISCONNECT:
+    if (is_server_running()){
+      char buffer[1024];
+      my_snprintf(buffer, 1024, _("You have just disconnected from a running 
game.  The "
+                                "game will continue to run without you.  You 
can rejoin "
+                                "later by selecting \"Join Game\" on host 
\"localhost\" "
+                                 "and port %d with username \"FIXME\"."), 
server_port);
+      append_output_window(buffer);
+    }
     disconnect_from_server();
     break;
+  case MENU_GAME_END:
+    end_game();
+    break;
   case MENU_GAME_QUIT:
-    exit(EXIT_SUCCESS);
+    client_kill_server();
+    gtk_main_quit();
   }
 }
 
@@ -464,6 +523,9 @@
    case MENU_REPORT_SPACESHIP:
     popup_spaceship_dialog(game.player_ptr);
     break;
+   case MENU_REPORT_SCORE:
+    send_score_request();
+    break;
   }
 }
 
@@ -583,18 +645,30 @@
        game_menu_callback,     MENU_GAME_SERVER_OPTIONS1                       
        },
   { "/" N_("Game") "/" N_("Server Opt _ongoing"),      NULL,
        game_menu_callback,     MENU_GAME_SERVER_OPTIONS2                       
        },
+  { "/" N_("Game") "/" N_("_Metaserver"),              NULL, 
+        game_menu_callback,    MENU_GAME_META_SERVER                           
        }, 
   { "/" N_("Game") "/sep3",                            NULL,
        NULL,                   0,                                      
"<Separator>"   },
-  { "/" N_("Game") "/" N_("_Export Log"),              NULL,
+  { "/" N_("Game") "/" N_("Save Game"),       NULL, 
+        game_menu_callback,     MENU_GAME_SAVE_GAME                            
   }, 
+  { "/" N_("Game") "/" N_("Save like _Autosave"),       NULL, 
+        game_menu_callback,     MENU_GAME_AUTOSAVE                             
   }, 
+  { "/" N_("Game") "/" N_("Save Game as..."),    NULL, 
+        game_menu_callback,     MENU_GAME_SAVE_GAME_NAME                       
   }, 
+  { "/" N_("Game") "/sep4",                             NULL, 
+        NULL,                   0,                                      
"<Separator>"   }, 
+  { "/" N_("Game") "/" N_("E_xport Log"),              NULL,
        game_menu_callback,     MENU_GAME_OUTPUT_LOG                            
        },
   { "/" N_("Game") "/" N_("_Clear Log"),               NULL,
        game_menu_callback,     MENU_GAME_CLEAR_OUTPUT                          
        },
-  { "/" N_("Game") "/sep4",                            NULL,
+  { "/" N_("Game") "/sep5",                            NULL, 
        NULL,                   0,                                      
"<Separator>"   },
-  { "/" N_("Game") "/" N_("_Disconnect"),              NULL,
+  { "/" N_("Game") "/" N_("_Disconnect from Game"),    NULL, 
        game_menu_callback,     MENU_GAME_DISCONNECT                            
        },
-  { "/" N_("Game") "/" N_("_Quit"),                    "<control>q",
-       gtk_main_quit,          0                                               
        },
+  { "/" N_("Game") "/" N_("_End Game"),                NULL, 
+       game_menu_callback,     MENU_GAME_END                                   
        }, 
+  { "/" N_("Game") "/" N_("_Quit Freeciv"),            "<control>q", 
+       game_menu_callback,     MENU_GAME_QUIT                                  
        }, 
   /* Kingdom menu ... */
   { "/" N_("_Kingdom"),                                        NULL,
        NULL,                   0,                                      
"<Branch>"      },
@@ -761,6 +835,10 @@
        reports_menu_callback,  MENU_REPORT_DEMOGRAPHIC                         
        },
   { "/" N_("Reports") "/" N_("S_paceship"),            "F12",
        reports_menu_callback,  MENU_REPORT_SPACESHIP                           
        },
+  { "/" N_("Reports") "/sep1",                                 NULL, 
+       NULL,                   0,                                      
"<Separator>"   }, 
+  { "/" N_("Reports") "/" N_("Sc_ore (visible to all)"),               "F9", 
+       reports_menu_callback,  MENU_REPORT_SCORE                               
        }, 
   /* Help menu ... */
   { "/" N_("_Help"),                                   NULL,
        NULL,                   0,                                      
"<LastBranch>"  },
@@ -1003,8 +1081,16 @@
 *****************************************************************/
 void update_menus(void)
 {
+  menus_set_sensitive("<main>/_Game/_Disconnect from Game", 
aconnection.established);
+  menus_set_sensitive("<main>/_Game/Server Opt _initial", 
aconnection.established);
+  menus_set_sensitive("<main>/_Game/Server Opt _ongoing", 
aconnection.established);
+  menus_set_sensitive("<main>/_Game/_Metaserver", aconnection.established);
   if (!can_client_change_view()) {
+    menus_set_sensitive("<main>/_Game/Save Game", FALSE);
+    menus_set_sensitive("<main>/_Game/Save Game as...", FALSE); 
+    menus_set_sensitive("<main>/_Game/_End Game", FALSE);
     menus_set_sensitive("<main>/_Reports", FALSE);
+    menus_set_sensitive("<main>/_Reports/Sc_ore (visible to all)", FALSE);
     menus_set_sensitive("<main>/_Kingdom", FALSE);
     menus_set_sensitive("<main>/_View", FALSE);
     menus_set_sensitive("<main>/_Orders", FALSE);
@@ -1053,6 +1139,10 @@
     menus_set_sensitive("<main>/_View", TRUE);
     menus_set_sensitive("<main>/_Orders", can_client_issue_orders());
 
+    menus_set_sensitive("<main>/_Game/Save Game", client_has_hack);
+    menus_set_sensitive("<main>/_Game/Save Game as...", client_has_hack);
+    menus_set_sensitive("<main>/_Game/_End Game", client_has_hack);
+
     menus_set_sensitive("<main>/_Kingdom/_Tax Rates",
                        can_client_issue_orders());
     menus_set_sensitive("<main>/_Kingdom/Work_lists",
@@ -1062,6 +1152,8 @@
 
     menus_set_sensitive("<main>/_Reports/S_paceship",
                        (game.player_ptr->spaceship.state!=SSHIP_NONE));
+
+    menus_set_sensitive("<main>/_Reports/Sc_ore (visible to all)", 
client_has_hack);
 
     menus_set_active("<main>/_View/Map _Grid", draw_map_grid);
     menus_set_sensitive("<main>/_View/National _Borders", game.borders > 0);
Index: client/gui-gtk/repodlgs.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/repodlgs.c,v
retrieving revision 1.73
diff -u -r1.73 repodlgs.c
--- client/gui-gtk/repodlgs.c   15 Sep 2003 19:40:52 -0000      1.73
+++ client/gui-gtk/repodlgs.c   12 Oct 2003 10:06:45 -0000
@@ -91,6 +91,9 @@
 static int activeunits_dialog_shell_is_modal;
 
 /******************************************************************/
+extern GtkWidget *init_opts_frame;
+
+/******************************************************************/
 static void create_endgame_report(struct packet_endgame_report *packet);
 static void endgame_destroy_callback(GtkObject *object, gpointer data);
 
@@ -1178,4 +1181,315 @@
     gtk_widget_destroy(endgame_report_shell);
     endgame_report_shell = NULL;
   }
+}
+
+static void setvars(GtkWidget *w, gpointer data){
+  GtkWidget **ent;
+  struct packet_generic_message packet;
+  for (ent=(GtkWidget**)data; *ent; ent++){
+    if (!gtk_object_get_data(GTK_OBJECT(*ent), "marked")){
+      continue;
+    }
+    if (!GTK_IS_COMBO(*ent)){
+      my_snprintf(packet.message, MAX_LEN_MSG, "/set %s %s\n", 
+                 gtk_widget_get_name(*ent), 
+                 GTK_IS_ENTRY(*ent)?gtk_entry_get_text(GTK_ENTRY(*ent)):
+                 
(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(*ent))?"1":"0"));
+    }else{ /* must be the modpack */ 
+      my_snprintf(packet.message, MAX_LEN_MSG, "/rulesetdir %s\n", 
+                 gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(*ent)->entry)));
+    }
+    send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);
+  } 
+}
+
+/***************************************************************** 
+Destroy whatever toplevel window our calling widget is in
+*****************************************************************/ 
+static void close_server_options(GtkWidget *w, GtkWidget ** data){
+  gtk_widget_destroy(gtk_widget_get_toplevel(w));
+  *data = NULL;
+}
+
+
+/*************************************************************************
+Hide whatever toplevel window our calling widget is in
+*************************************************************************/
+static void hide_toplevel(GtkWidget * w, gpointer data){
+  gtk_widget_hide(gtk_widget_get_toplevel(w));
+}
+
+/***************************************************************** 
+'Mark' the calling object -- usually used to mark it as changed 
+(so we know to send it to the server)
+*****************************************************************/ 
+static void mark_object(GtkWidget *w, gpointer data){
+  gtk_object_set_data(GTK_OBJECT(w), "marked", (gpointer)TRUE);
+}
+
+/***************************************************************** 
+Ask the server to send us help in a popup window
+*****************************************************************/ 
+static void server_help(GtkWidget *w, gpointer data){
+  struct packet_generic_message pack;
+  sprintf(pack.message, "/helppopup %s\n", gtk_widget_get_name(w));
+  send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &pack);
+}
+
+/***************************************************************** 
+Reset values in a server opts dialog to their originals
+*****************************************************************/ 
+static void reset_server_opts(GtkWidget *w, int ongoing){
+    send_report_request(REPORT_SERVER_OPTIONS1+ongoing);
+}
+
+/***************************************************************** 
+Turns the server's "options" reports into dialog boxes
+*****************************************************************/ 
+int change_server_options(char *opts){
+  char *next;
+  char optname[64], val[64];
+  int min, max;
+  GtkWidget *win, *hbox, *scr, *ent, *but, *outbox, *butbox, *vboxen[3];
+  static GtkWidget *ents[100];
+  int i=0, curvbox;
+  int sb_height, sb_width;
+  bool pregame_flag = FALSE;
+  static GtkWidget *permwin[2] = {0,0};
+
+  /* FIXME: this is hideous, but how else can we resize spinboxes in a 
+    theme-friendly manner? */ 
+  win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  ent = gtk_spin_button_new(GTK_ADJUSTMENT(
+                              gtk_adjustment_new(0, 0, 1, 1, 0, 0)),  1, 0);
+  gtk_container_add(GTK_CONTAINER(win), ent);
+  gtk_widget_show_all(win);
+  sb_height = ent->allocation.height;
+  sb_width = ent->allocation.width * 2;
+  gtk_widget_destroy(win);
+  /* OK, on with the real code */ 
+
+  outbox = gtk_vbox_new(0, 3);
+
+  gtk_box_pack_start(GTK_BOX(outbox), 
+                    gtk_label_new(_("Click on any option for more info:")), 
+                    FALSE, FALSE, 0);
+
+  scr = gtk_scrolled_window_new(NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scr), 
+                                GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+  gtk_box_pack_start(GTK_BOX(outbox), scr, TRUE, TRUE, 0);
+
+  hbox = gtk_hbox_new(FALSE, 2);
+  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scr), hbox);
+
+  vboxen[0]=gtk_vbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), vboxen[0], TRUE, TRUE, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), gtk_vseparator_new(), FALSE, FALSE, 0);
+  vboxen[1]=gtk_vbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), vboxen[1], TRUE, TRUE, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), gtk_vseparator_new(), FALSE, FALSE, 0);
+  vboxen[2]=gtk_vbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), vboxen[2], TRUE, TRUE, 0);
+
+  curvbox=0;
+  while((next = strchr(opts, '\n'))){
+    *next++ = 0;
+    min = max = -999942;
+    hbox = gtk_hbox_new(0, 0);
+    gtk_box_pack_start(GTK_BOX(vboxen[curvbox]), hbox, FALSE, FALSE, 1);
+    sscanf(opts, "%s %s (%d, %d)", optname, val, &min, &max);
+    if (!strcmp(optname, "aifill")){ 
+      /* This is covered by the startup dialog, 
+       * but it does tell us where we are */ 
+      pregame_flag = TRUE;
+      opts = next;
+      continue;
+    }
+    but = gtk_button_new_with_label(optname);
+    gtk_widget_set_name(but, optname);
+    gtk_signal_connect(GTK_OBJECT(but), "clicked", server_help, NULL);
+    gtk_box_pack_start(GTK_BOX(hbox), but, TRUE, TRUE, 0);
+    if (min == 0 && max == 1){
+      /* boolean option */
+      GSList * gsl;
+      ent = gtk_radio_button_new_with_label(NULL, _("No"));
+      gsl = gtk_radio_button_group(GTK_RADIO_BUTTON(ent));
+      gtk_box_pack_end(GTK_BOX(hbox), ent, FALSE, FALSE, 0);
+      ent = gtk_radio_button_new_with_label(gsl, _("Yes"));
+      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ent), 
+                                  (int)strchr(val, '1'));
+    }else if (min!= -999942 || max!= -999942){
+      /* Numerical option */
+      int nval, step;
+      if (val[0]!='-' && (val[0]>'9' || val[0]<'0'))
+       nval = atoi(val + 1);
+      else
+       nval = atoi(val);
+      step=(max-min)/101 + 1;
+      if (step > 100){
+       /* This is rediculous -- it's got to be a meaningless upper bound  */
+       step=5;
+      }
+      ent = gtk_spin_button_new(GTK_ADJUSTMENT(gtk_adjustment_new(nval, min,
+                                                                 max, step, 
+                                                                 0, 0)),
+                               1, 0);
+      gtk_widget_set_usize(ent, sb_width, sb_height);
+    }else{
+      ent = gtk_entry_new();
+      if (val[strlen(val)-1] == '"')
+       val[strlen(val)-1] = 0;
+      gtk_entry_set_text(GTK_ENTRY(ent), strchr(val, '"')+ 1);
+    }
+    gtk_widget_set_name(ent, optname);
+    gtk_signal_connect(GTK_OBJECT(ent), 
+                      GTK_IS_ENTRY(ent)?"changed":"toggled", 
+                      mark_object, NULL);
+    if (GTK_IS_ENTRY(ent))
+      gtk_signal_connect(GTK_OBJECT(ent), "activate", 
+                        setvars, ents);
+    gtk_box_pack_end(GTK_BOX(hbox), ent, FALSE, FALSE, 0);
+    ents[i] = ent;
+    i++;
+    opts = next;
+    curvbox=(curvbox+1)%3;
+  }
+  ents[i] = NULL;
+
+  butbox = gtk_hbox_new(0, 0);
+  gtk_box_pack_end(GTK_BOX(outbox), butbox, FALSE, FALSE, 0);
+
+  if (pregame_flag && client_start_type==START_SINGLE_NEW 
+                   && get_client_state()==CLIENT_PRE_GAME_STATE){
+    win=init_opts_frame;
+  }else if (permwin[pregame_flag] && 
+           GTK_IS_WINDOW(permwin[pregame_flag]) && 
+           GTK_WIDGET_VISIBLE(permwin[pregame_flag])){
+    win=permwin[pregame_flag];
+  }else{
+    permwin[pregame_flag] = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(win), _("Set Server Options"));
+  }
+  gtk_container_forall(GTK_CONTAINER(win), (GtkCallback)gtk_widget_destroy, 
NULL);
+  gtk_container_add(GTK_CONTAINER(win), outbox);
+
+  if (GTK_IS_WINDOW(win)){
+    but = gtk_button_new_with_label(_("Close"));
+    GTK_WIDGET_SET_FLAGS(but, GTK_CAN_DEFAULT);
+    gtk_signal_connect(GTK_OBJECT(but), "clicked", 
+                      close_server_options, &permwin[pregame_flag]);
+    gtk_box_pack_start(GTK_BOX(butbox), but, TRUE, TRUE, 0);
+  }
+
+  but = gtk_button_new_with_label(_("Reset"));
+  GTK_WIDGET_SET_FLAGS(but, GTK_CAN_DEFAULT);
+  gtk_signal_connect(GTK_OBJECT(but), "clicked", 
+                    reset_server_opts, (gpointer)!pregame_flag);
+  gtk_box_pack_start(GTK_BOX(butbox), but, TRUE, TRUE, 0);
+
+  but = gtk_button_new_with_label(_("Set now"));
+  GTK_WIDGET_SET_FLAGS(but, GTK_CAN_DEFAULT);
+  GTK_WIDGET_SET_FLAGS(but, GTK_HAS_DEFAULT);
+  gtk_widget_grab_default(but);
+  gtk_signal_connect(GTK_OBJECT(but), "clicked", setvars, (gpointer)ents);
+  gtk_signal_connect(GTK_OBJECT(win), "show", setvars, (gpointer)ents);
+  gtk_box_pack_end(GTK_BOX(butbox), but, TRUE, TRUE, 0);
+
+  gtk_widget_show_all(win);
+  if (GTK_IS_WINDOW(win)){
+    gtk_widget_set_usize(scr, win->allocation.width, 
MIN(450,vboxen[0]->allocation.height+5));
+  }
+  return 1;
+}
+
+/*************************************************************************
+The user entered metaserver advertisement data, so let's tell the server
+*************************************************************************/
+static void set_metadata(GtkWidget *w, gpointer data){
+  GtkEntry *desc = (GtkEntry*)gtk_object_get_data(GTK_OBJECT(w), "desc");
+  GtkEntry *addr = (GtkEntry*)gtk_object_get_data(GTK_OBJECT(w), "addr");
+  GtkToggleButton *checkbox = (GtkToggleButton*)data;
+  struct packet_generic_message packet;
+  if (gtk_toggle_button_get_active(checkbox)){
+    my_snprintf(packet.message, MAX_LEN_MSG, "/metaconnection up\n");
+    send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);
+  }else{
+    my_snprintf(packet.message, MAX_LEN_MSG, "/metaconnection down\n");
+    send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);
+  }
+   my_snprintf(packet.message, MAX_LEN_MSG, "/metainfo %s\n", 
+                                          gtk_entry_get_text(desc));
+  send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);
+  my_snprintf(packet.message, MAX_LEN_MSG, "/metaserv %s\n", 
+                                          gtk_entry_get_text(addr));
+  send_packet_generic_message(&aconnection, PACKET_CHAT_MSG, &packet);
+  gtk_widget_hide(gtk_widget_get_toplevel(w));
+}
+
+/*************************************************************************
+Pop up a dialog with options for metaserver advertisement
+*************************************************************************/   
+void control_metaserver(GtkWidget *w, gpointer data){
+  static GtkWidget *checkbox, *desc, *addr, *win = NULL;
+  if (!win){ /* first time */ 
+    GtkWidget *ok, *canc, *vbox, *hbox, *lab;
+    win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(win), _("Control Metaserver"));
+    vbox = gtk_vbox_new(0, 0);
+    gtk_container_add(GTK_CONTAINER(win), vbox);
+    /* The label is manually wrapped to force the dialog to be wide enough.
+       If we don't do this, there isn't enough room for the description, and 
+       set_usize isn't versatile enough (e.g. we support big fonts this way*/ 
+    lab = gtk_label_new(_("Metaserver is a centralized server that " 
+                       "tells players where they can find\n"
+                       "freeciv games.  If you choose to "
+                       "be listed on metaserver, players from all\n" 
+                       "over the world will have the option"
+                       " of joining you.  This can be a great way\n" 
+                       "to meet other freeciv- ers!  It should "
+                       "be reasonaly safe and secure, but\n" 
+                       "we can't promise.\n\n"
+                       "If you are behind a firewall, outside " 
+                       "players will not be able to join your\n"
+                       "game, so please do not advertise "
+                       "on metaserver!"));
+    gtk_label_set_justify(GTK_LABEL(lab), GTK_JUSTIFY_FILL);
+    gtk_box_pack_start(GTK_BOX(vbox), lab, FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), gtk_hseparator_new(), FALSE, FALSE, 5);
+    checkbox = gtk_check_button_new_with_label(_("Advertise on metaserver"));
+    gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+    hbox = gtk_hbox_new(0, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("Game description: ")),
+                      FALSE, FALSE, 0);
+    desc = gtk_entry_new();
+    gtk_entry_append_text(GTK_ENTRY(desc), _("I'm running "));
+    gtk_entry_append_text(GTK_ENTRY(desc), VERSION_STRING);
+    gtk_entry_append_text(GTK_ENTRY(desc), _(", come join me!"));
+    gtk_box_pack_end(GTK_BOX(hbox), desc, TRUE, TRUE, 1);
+    hbox = gtk_hbox_new(0, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), 
+                      gtk_label_new(_("Metaserver address: ")), 
+                      false, false, 0);
+    addr = gtk_entry_new();
+    gtk_entry_append_text(GTK_ENTRY(addr), _("meta.freeciv.org"));
+    gtk_box_pack_end(GTK_BOX(hbox), addr, TRUE, TRUE, 1);
+    gtk_box_pack_start(GTK_BOX(vbox), gtk_hseparator_new(), FALSE, FALSE, 5);
+    hbox = gtk_hbox_new(0, 0);
+    gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+    canc = gtk_button_new_with_label(_("Cancel"));
+    gtk_box_pack_start(GTK_BOX(hbox), canc, TRUE, TRUE, 5);
+    ok = gtk_button_new_with_label(_("OK"));
+    gtk_box_pack_end(GTK_BOX(hbox), ok, TRUE, TRUE, 5);
+    gtk_signal_connect(GTK_OBJECT(canc), "clicked", 
+                      hide_toplevel, NULL);
+    gtk_signal_connect(GTK_OBJECT(ok), "clicked", 
+                      set_metadata, checkbox);
+    /* simple way to pass a additional arguments */ 
+    gtk_object_set_data(GTK_OBJECT(ok), "desc", (gpointer)desc); 
+    gtk_object_set_data(GTK_OBJECT(ok), "addr", (gpointer)addr);
+  }
+  gtk_widget_show_all(win);
 }
Index: client/gui-gtk/repodlgs.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/repodlgs.h,v
retrieving revision 1.7
diff -u -r1.7 repodlgs.h
--- client/gui-gtk/repodlgs.h   1 Dec 2002 22:49:25 -0000       1.7
+++ client/gui-gtk/repodlgs.h   12 Oct 2003 10:06:45 -0000
@@ -19,4 +19,6 @@
 void popdown_economy_report_dialog(void);
 void popdown_activeunits_report_dialog(void);
 
+void control_metaserver(GtkWidget *w, gpointer data);
+
 #endif  /* FC__REPODLGS_H */
Index: client/gui-gtk-2.0/connectdlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk-2.0/connectdlg.c,v
retrieving revision 1.26
diff -u -r1.26 connectdlg.c
--- client/gui-gtk-2.0/connectdlg.c     12 Sep 2003 07:11:16 -0000      1.26
+++ client/gui-gtk-2.0/connectdlg.c     12 Oct 2003 10:06:45 -0000
@@ -28,6 +28,8 @@
 #include "support.h"
 #include "version.h"
 
+#include "repodlgs_g.h"
+
 #include "civclient.h"
 #include "chatline.h"
 #include "clinet.h"
@@ -569,4 +571,30 @@
   if (try_to_autoconnect(NULL)) {
     gtk_timeout_add(AUTOCONNECT_INTERVAL, try_to_autoconnect, NULL);
   }
+}
+
+
+
+/************************************************************
+These are stub functions, to satisfy the linker
+Implement them if you want server control in this client
+************************************************************/
+
+void handle_player_list_reply(struct packet_player_list *packet){
+}
+void popdown_connect_dialog(){
+}
+int get_aifill_setting(){
+  return -1;
+}
+const char*  get_aiskill_setting(){
+  return "foo";
+}
+const char* get_ruleset_setting(){
+  return "";
+}
+void gui_handle_rulesets(char* x){
+}
+int change_server_options(char* x){
+  return 0;
 }
Index: client/gui-sdl/connectdlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-sdl/connectdlg.c,v
retrieving revision 1.19
diff -u -r1.19 connectdlg.c
--- client/gui-sdl/connectdlg.c 2 Oct 2003 21:37:44 -0000       1.19
+++ client/gui-sdl/connectdlg.c 12 Oct 2003 10:06:45 -0000
@@ -1148,3 +1148,25 @@
     add_autoconnect_to_timer();
   }
 }
+/************************************************************
+These are stub functions, to satisfy the linker
+Implement them if you want server control in this client
+************************************************************/
+void handle_player_list_reply(struct packet_player_list *packet){
+}                                                                
+void popdown_connect_dialog(){
+}                             
+int get_aifill_setting(){
+  return -1;
+}           
+const char*  get_aiskill_setting(){
+  return "foo";
+}              
+const char* get_ruleset_setting(){
+  return "";
+}           
+void gui_handle_rulesets(char* x){
+}                                 
+int change_server_options(char* x){
+  return 0;
+}
Index: client/gui-xaw/connectdlg.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-xaw/connectdlg.c,v
retrieving revision 1.30
diff -u -r1.30 connectdlg.c
--- client/gui-xaw/connectdlg.c 10 Jul 2003 03:34:29 -0000      1.30
+++ client/gui-xaw/connectdlg.c 12 Oct 2003 10:06:45 -0000
@@ -503,3 +503,25 @@
   try_to_autoconnect(NULL, NULL);
   XtSetSensitive(toplevel, True);
 }
+/************************************************************
+These are stub functions, to satisfy the linker
+Implement them if you want server control in this client
+************************************************************/
+void handle_player_list_reply(struct packet_player_list *packet){
+}                                                                
+void popdown_connect_dialog(){
+}                             
+int get_aifill_setting(){
+  return -1;
+}           
+const char*  get_aiskill_setting(){
+  return "foo";
+}              
+const char* get_ruleset_setting(){
+  return "";
+}           
+void gui_handle_rulesets(char* x){
+}                                 
+int change_server_options(char* x){
+  return 0;
+}
Index: client/include/connectdlg_g.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/include/connectdlg_g.h,v
retrieving revision 1.3
diff -u -r1.3 connectdlg_g.h
--- client/include/connectdlg_g.h       10 Jul 2003 03:34:30 -0000      1.3
+++ client/include/connectdlg_g.h       12 Oct 2003 10:06:45 -0000
@@ -20,7 +20,24 @@
 void handle_authentication_request(struct packet_authentication_request *
                                    packet);
 
+#include "shared.h"
+#include "packets.h"
+
 void gui_server_connect(void);
 void server_autoconnect(void);
+
+void gui_handle_rulesets(char*);
+
+void handle_player_list_reply(struct packet_player_list *packet);
+void popdown_connect_dialog(void);
+
+void send_save_game(char *filename);
+void send_save_game_with_filename(void);
+
+/* used in send_final_server_commands() */ 
+int get_aifill_setting(void);
+const char *get_aiskill_setting(void);
+const char *get_ruleset_setting(void);
+const char *get_new_username(void);
 
 #endif  /* FC__CONNECTDLG_G_H */
Index: client/include/repodlgs_g.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/include/repodlgs_g.h,v
retrieving revision 1.6
diff -u -r1.6 repodlgs_g.h
--- client/include/repodlgs_g.h 15 Sep 2003 19:40:53 -0000      1.6
+++ client/include/repodlgs_g.h 12 Oct 2003 10:06:45 -0000
@@ -29,4 +29,6 @@
 void popup_activeunits_report_dialog(bool make_modal);
 void popup_endgame_report_dialog(struct packet_endgame_report *packet);
 
+int change_server_options(char * opts);
+
 #endif  /* FC__REPODLGS_G_H */
Index: common/capstr.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/capstr.c,v
retrieving revision 1.146
diff -u -r1.146 capstr.c
--- common/capstr.c     2 Oct 2003 17:54:57 -0000       1.146
+++ common/capstr.c     12 Oct 2003 10:06:45 -0000
@@ -81,7 +81,7 @@
                    "+diplomacy2 +citizens_style +root_tech auth " \
                    "+nat_ulimit +retake +goto_pack borders dip " \
                    "+packet_short_unit +unit_occupied endgame_rep " \
-                   "+terr_flags"
+                   "+terr_flags client_hack"
 
 /* "+1.14.0" is protocol for 1.14.0 release.
  *
@@ -158,6 +158,10 @@
  *
  * "terr_flags" means terrain flags (with the TER_NO_BARBS flag) have been
  * added.
+ *
+ * "client_hack" indicates that the client is capable of claiming hack-level
+ * access for itself without the player's help, assuming the client and server
+ * are on the same machine with the same permissions
  */
 
 void init_our_capability(void)
Index: common/game.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/game.c,v
retrieving revision 1.170
diff -u -r1.170 game.c
--- common/game.c       23 Sep 2003 22:01:41 -0000      1.170
+++ common/game.c       12 Oct 2003 10:06:45 -0000
@@ -270,6 +270,8 @@
   game.num_impr_types = 0;
   game.num_tech_types = 0;
 
+  game.nation_count = 0;
+
   game.government_count = 0;
   game.default_government = G_MAGIC;        /* flag */
   game.government_when_anarchy = G_MAGIC;   /* flag */
Index: common/log.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/log.c,v
retrieving revision 1.43
diff -u -r1.43 log.c
--- common/log.c        4 Apr 2003 15:47:49 -0000       1.43
+++ common/log.c        12 Oct 2003 10:06:45 -0000
@@ -259,7 +259,7 @@
   if(level<=fc_log_level) {
     FILE *fs;
 
-    if(log_filename) {
+    if(log_filename && strcmp(log_filename,"-")) {
       if(!(fs=fopen(log_filename, "a"))) {
        fprintf(stderr, _("Couldn't open logfile: %s for appending.\n"), 
                log_filename);
@@ -313,8 +313,9 @@
     }
     bufbuf1 = !bufbuf1;
     fflush(fs);
-    if(log_filename)
+    if(log_filename && strcmp(log_filename,"-")){
       fclose(fs);
+    }
   }
 }
 void real_freelog(int level, const char *message, ...)
Index: common/mem.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/mem.c,v
retrieving revision 1.8
diff -u -r1.8 mem.c
--- common/mem.c        11 Dec 2002 10:39:42 -0000      1.8
+++ common/mem.c        12 Oct 2003 10:06:45 -0000
@@ -115,7 +115,7 @@
 char *real_mystrdup(const char *str, 
                    const char *called_as, int line, const char *file)
 {
-  char *dest = (char *)fc_real_malloc(strlen(str)+1, called_as, line, file);
+  char *dest = (char*)fc_real_malloc(strlen(str)+1, called_as, line, file);
   /* no need to check whether dest is non-NULL! */
   strcpy(dest, str);
   return dest;
Index: common/packets.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/packets.c,v
retrieving revision 1.260
diff -u -r1.260 packets.c
--- common/packets.c    7 Oct 2003 18:55:08 -0000       1.260
+++ common/packets.c    12 Oct 2003 10:06:46 -0000
@@ -314,6 +314,7 @@
   case PACKET_SELECT_NATION_OK:
   case PACKET_FREEZE_HINT:
   case PACKET_THAW_HINT:
+  case PACKET_PLAYER_LIST_REQUEST:
     return receive_packet_generic_empty(pc);
 
   case PACKET_NEW_YEAR:
@@ -333,6 +334,7 @@
   case PACKET_CITY_REFRESH:
   case PACKET_INCITE_INQ:
   case PACKET_CITY_NAME_SUGGEST_REQ:
+  case PACKET_CLIENT_WANTS_HACK:
     return receive_packet_generic_integer(pc);
 
   case PACKET_PLAYER_CANCEL_PACT:
@@ -461,6 +463,9 @@
   case PACKET_PING_INFO:
     return receive_packet_ping_info(pc);
     
+  case PACKET_PLAYER_LIST_REPLY:
+    return receive_packet_player_list_reply(pc);
+
   case PACKET_ENDGAME_REPORT:
     return receive_packet_endgame_report(pc);
     
@@ -1701,6 +1706,11 @@
     dio_put_uint32(&dout, pc->id);
   }
 
+  if (has_capability("client_hack", pc->capability)) {
+    dio_put_string(&dout, reply->challenge);
+    dio_put_string(&dout, reply->rulesets);
+  }
+
   SEND_PACKET_END;
 }
 
@@ -1723,6 +1733,12 @@
     packet->conn_id = 0;
   }
 
+  /* ah, this too */ 
+  if (has_capability("client_hack", packet->capability)) {
+    dio_get_string(&din, packet->challenge, sizeof(packet->challenge));
+    dio_get_string(&din, packet->rulesets, sizeof(packet->rulesets));
+  }
+
   RECEIVE_PACKET_END(packet);
 }
 
@@ -3508,4 +3524,51 @@
   }
 
   connection_do_unbuffer(pconn);
+}
+
+/************************************************************************** 
+...
+**************************************************************************/ 
+int send_packet_player_list_reply(struct connection * pc, 
+                                  const struct packet_player_list * packet)
+{
+  int i;
+  SEND_PACKET_START(PACKET_PLAYER_LIST_REPLY);
+
+  dio_put_uint8(&dout, packet->nplayers);
+  dio_put_string(&dout, packet->load_filename);
+
+  for(i = 0; i < packet->nplayers; i++) {
+    dio_put_string(&dout, packet->name[i]);
+    dio_put_string(&dout, packet->username[i]);
+    dio_put_string(&dout, packet->nation_name[i]);
+    dio_put_string(&dout, packet->nation_flag[i]);
+    dio_put_bool8(&dout, packet->is_alive[i]);
+    dio_put_bool8(&dout, packet->ai[i]);
+  }
+
+  SEND_PACKET_END;
+}
+
+/************************************************************************** 
+...
+**************************************************************************/ 
+struct packet_player_list * 
+                      receive_packet_player_list_reply(struct connection * pc)
+{
+  int i;
+  RECEIVE_PACKET_START(packet_player_list, packet);
+
+  dio_get_uint8(&din, &packet->nplayers);
+  dio_get_string(&din, packet->load_filename, sizeof(packet->load_filename));
+  for(i = 0; i < packet->nplayers; i++) {
+    dio_get_string(&din, packet->name[i], sizeof(packet->name[i]));
+    dio_get_string(&din, packet->username[i], sizeof(packet->username[i]));
+    dio_get_string(&din, packet->nation_name[i], 
sizeof(packet->nation_name[i]));
+    dio_get_string(&din, packet->nation_flag[i], 
sizeof(packet->nation_flag[i]));
+    dio_get_bool8(&din, &packet->is_alive[i]);
+    dio_get_bool8(&din, &packet->ai[i]);
+  }
+
+  RECEIVE_PACKET_END(packet);
 }
Index: common/packets.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/packets.h,v
retrieving revision 1.156
diff -u -r1.156 packets.h
--- common/packets.h    7 Oct 2003 18:55:09 -0000       1.156
+++ common/packets.h    12 Oct 2003 10:06:46 -0000
@@ -23,7 +23,7 @@
 #include "worklist.h"
 
 #define MAX_LEN_USERNAME        10        /* see below */
-#define MAX_LEN_MSG             1536
+#define MAX_LEN_MSG             2048 
 #define MAX_ATTRIBUTE_BLOCK     (256*1024)     /* largest attribute block */
 #define ATTRIBUTE_CHUNK_SIZE    (1024*2)  /* attribute chunk size to use */
 
@@ -133,6 +133,9 @@
   PACKET_PING_INFO,
   PACKET_AUTHENTICATION_REQUEST,
   PACKET_AUTHENTICATION_REPLY,
+  PACKET_PLAYER_LIST_REQUEST,
+  PACKET_PLAYER_LIST_REPLY,
+  PACKET_CLIENT_WANTS_HACK,
   PACKET_ENDGAME_REPORT,
   PACKET_LAST  /* leave this last */
 };
@@ -445,6 +448,8 @@
   bool you_can_login;             /* true/false */
   char message[MAX_LEN_MSG];
   char capability[MAX_LEN_CAPSTR];
+  char challenge[MAX_PATH];
+  char rulesets[MAX_LEN_MSG];
   int conn_id;                 /* clients conn id as known in server */
 };
 
@@ -971,6 +976,19 @@
   unsigned char data[ATTRIBUTE_CHUNK_SIZE];
 };
 
+/* this isn't cheap, but we only do it once and only for single- player */ 
+struct packet_player_list
+{
+  int nplayers;
+  char load_filename[1024];
+  char name[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS][MAX_LEN_NAME];
+  char username[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS][MAX_LEN_NAME];
+  char nation_name[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS][MAX_LEN_NAME];
+  char nation_flag[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS][64];
+  bool is_alive[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS]; 
+  bool ai[MAX_NUM_PLAYERS + MAX_NUM_BARBARIANS]; 
+};
+
 /*********************************************************
 ...
 *********************************************************/
@@ -1243,6 +1261,11 @@
 struct packet_endgame_report *
 receive_packet_endgame_report(struct connection *pc);
 
+
+int send_packet_player_list_reply(struct connection *pc, 
+                                   const struct packet_player_list *packet);
+struct packet_player_list 
+                      *receive_packet_player_list_reply(struct connection *pc);
 
 #include "packets_lsend.h"             /* lsend_packet_* functions */
 
Index: common/packets_lsend.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/packets_lsend.c,v
retrieving revision 1.18
diff -u -r1.18 packets_lsend.c
--- common/packets_lsend.c      15 Sep 2003 19:40:53 -0000      1.18
+++ common/packets_lsend.c      12 Oct 2003 10:06:46 -0000
@@ -416,3 +416,11 @@
   conn_list_iterate_end;
 }
 
+void lsend_packet_player_list_reply(struct conn_list *dest, 
+                                   const struct packet_player_list *packet)
+{
+  conn_list_iterate(* dest, pconn)
+    send_packet_player_list_reply(pconn, packet);
+  conn_list_iterate_end;
+}
+
Index: common/packets_lsend.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/packets_lsend.h,v
retrieving revision 1.16
diff -u -r1.16 packets_lsend.h
--- common/packets_lsend.h      15 Sep 2003 19:40:53 -0000      1.16
+++ common/packets_lsend.h      12 Oct 2003 10:06:46 -0000
@@ -117,3 +117,5 @@
 void lsend_packet_endgame_report(struct conn_list *dest,
                                  enum packet_type pt,
                                  const struct packet_endgame_report *packet);
+void lsend_packet_player_list_reply(struct conn_list *dest, 
+                                    const struct packet_player_list *packet);
Index: common/shared.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/shared.c,v
retrieving revision 1.103
diff -u -r1.103 shared.c
--- common/shared.c     1 Oct 2003 21:01:52 -0000       1.103
+++ common/shared.c     12 Oct 2003 10:06:46 -0000
@@ -754,7 +754,7 @@
 
   num_dirs, if not NULL, will be set to the number of entries in the list.
 ***************************************************************************/
-static const char **get_data_dirs(int *num_dirs)
+const char **get_data_dirs(int *num_dirs)
 {
   const char *path;
   char *path2, *tok;
Index: common/shared.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/shared.h,v
retrieving revision 1.118
diff -u -r1.118 shared.h
--- common/shared.h     1 Oct 2003 21:01:52 -0000       1.118
+++ common/shared.h     12 Oct 2003 10:06:46 -0000
@@ -62,6 +62,10 @@
 #define MAX_LEN_NAME     32
 #define MAX_LEN_ADDR     256   /* see also MAXHOSTNAMELEN and RFC 1123 2.1 */
 
+#ifndef MAX_PATH
+#define MAX_PATH 4095
+#endif
+
 /* Use FC_INFINITY to denote that a certain event will never occur or
    another unreachable condition. */
 #define FC_INFINITY            (1000 * 1000 * 1000)
@@ -220,5 +224,6 @@
                               int *ind_result);
 
 const char *freeciv_motto(void);
+const char **get_data_dirs(int *num_dirs);
 
 #endif  /* FC__SHARED_H */
Index: server/civserver.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/civserver.c,v
retrieving revision 1.215
diff -u -r1.215 civserver.c
--- server/civserver.c  5 May 2003 12:11:13 -0000       1.215
+++ server/civserver.c  12 Oct 2003 10:06:47 -0000
@@ -66,7 +66,7 @@
   inx = 1;
   while (inx < argc) {
     if ((option = get_option("--file", argv, &inx, argc)))
-      srvarg.load_filename = option;
+      sz_strlcpy(srvarg.load_filename, option);
     else if (is_option("--help", argv[inx])) {
       showhelp = TRUE;
       break;
Index: server/connecthand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/connecthand.c,v
retrieving revision 1.14
diff -u -r1.14 connecthand.c
--- server/connecthand.c        23 Sep 2003 22:01:41 -0000      1.14
+++ server/connecthand.c        12 Oct 2003 10:06:47 -0000
@@ -88,6 +88,10 @@
   sz_strlcpy(packet.capability, our_capability);
   my_snprintf(packet.message, sizeof(packet.message), _("%s Welcome"),
               pconn->username);
+  my_snprintf(packet.challenge, sizeof(packet.challenge), 
+              "%s", create_challenge_filename());
+  my_snprintf(packet.rulesets, sizeof(packet.rulesets), 
+              "%s", list_ruleset_dirs());
   send_packet_login_reply(pconn, &packet);
 
   /* "establish" the connection */
@@ -148,6 +152,9 @@
   if (!pconn->player) {
     notify_conn(dest, _("You are logged in as '%s' connected to no player."),
                 pconn->username);
+    if (has_capability("client_hack", pconn->capability)){
+      handle_player_list_request(pconn);
+    }
   } else if (strcmp(pconn->player->name, ANON_PLAYER_NAME) == 0) {
     notify_conn(dest, _("You are logged in as '%s' connected to an "
                         "anonymous player."),
Index: server/gamehand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/gamehand.c,v
retrieving revision 1.128
diff -u -r1.128 gamehand.c
--- server/gamehand.c   1 Oct 2003 01:39:47 -0000       1.128
+++ server/gamehand.c   12 Oct 2003 10:06:47 -0000
@@ -16,6 +16,8 @@
 #endif
 
 #include <assert.h>
+#include <stdio.h> /* for remove() */ 
+#include <time.h>
 
 #include "events.h"
 #include "fcintl.h"
@@ -24,6 +26,9 @@
 #include "mem.h"
 #include "packets.h"
 #include "rand.h"
+#include "registry.h"
+#include "support.h"
+  
 
 #include "maphand.h"
 #include "plrhand.h"
@@ -31,6 +36,14 @@
 
 #include "gamehand.h"
 
+#define NUMBER_OF_TRIES 500
+#ifndef __VMS
+#  define CHALLENGE_ROOT ".freeciv-tmp"
+#else /*VMS*/
+#  define CHALLENGE_ROOT "freeciv-tmp"
+#endif
+
+static char challenge_filename[MAX_PATH];
 
 /****************************************************************************
   Initialize the game.id variable to a random string of characters.
@@ -357,4 +370,74 @@
          game.timeoutint - game.timeoutcounter);
 
   return game.timeout;
+}
+
+/************************************************************************** 
+find a suitably random file that we can write too, and return it's name.
+**************************************************************************/ 
+char *create_challenge_filename(void)
+{
+  int i; 
+  unsigned int tmp = 0;
+
+  /* find a suitable file to place the challenge in, we'll remove the file
+   * once the challenge is  */ 
+  sz_strlcpy(challenge_filename, user_home_dir());
+  sz_strlcat(challenge_filename, "/");
+  sz_strlcat(challenge_filename, CHALLENGE_ROOT);
+  
+  for (i=0; i<NUMBER_OF_TRIES; i++) {
+    char test_str[MAX_PATH];
+    
+    sz_strlcpy(test_str, challenge_filename);
+    tmp = time(NULL);
+    cat_snprintf(test_str, MAX_PATH, "-%d", tmp);
+    
+    /* file doesn't exist but we can create one and write to it */ 
+    if (!is_reg_file_for_access(test_str, FALSE) &&
+        is_reg_file_for_access(test_str, TRUE)) {
+      cat_snprintf(challenge_filename, MAX_PATH, "-%d", tmp);
+      break;
+    } else {
+      die("we can't seem to write to the filesystem!");
+    }
+  }
+
+  return challenge_filename; 
+}
+
+/************************************************************************** 
+opens a file specified by the packet and compares the packet values with
+the file values. Sends an answer to the client once it's done.
+**************************************************************************/ 
+void handle_client_wants_hack(struct connection *pc, 
+                              struct packet_generic_integer *packet)
+{
+  struct section_file file;
+  struct packet_generic_integer p;
+  int entropy = 0;
+  bool success = TRUE;
+
+  if (section_file_load_nodup(&file, challenge_filename)) {
+    entropy = secfile_lookup_int_default(&file, 0, "challenge.entropy");
+    section_file_free(&file);
+  } else {
+    freelog(LOG_ERROR, "couldn't load temporary file: %s", challenge_filename);
+    success = FALSE;
+  }
+
+  if (!success || packet->value != entropy) {
+    p.value = 0;
+  } else {
+    p.value = 1;
+    pc->access_level = ALLOW_HACK;
+  }
+    
+  /* remove temp file */ 
+  if (remove(challenge_filename) != 0) {
+    freelog(LOG_ERROR, "couldn't remove temporary file: %s", 
+            challenge_filename);
+  }
+
+  send_packet_generic_integer(pc, PACKET_CLIENT_WANTS_HACK, &p);
 }
Index: server/gamehand.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/gamehand.h,v
retrieving revision 1.10
diff -u -r1.10 gamehand.h
--- server/gamehand.h   25 Apr 2002 14:09:38 -0000      1.10
+++ server/gamehand.h   12 Oct 2003 10:06:47 -0000
@@ -15,6 +15,8 @@
 
 struct section_file;
 struct conn_list;
+struct connection;
+struct packet_generic_integer;
 
 void init_new_game(void);
 void send_year_to_clients(int year);
@@ -23,5 +25,9 @@
 void send_start_turn_to_clients(void);
 
 int update_timeout(void);
+
+char *create_challenge_filename(void);
+void handle_client_wants_hack(struct connection *pc, 
+                              struct packet_generic_integer *packet);
 
 #endif  /* FC__GAMEHAND_H */
Index: server/gamelog.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/gamelog.c,v
retrieving revision 1.29
diff -u -r1.29 gamelog.c
--- server/gamelog.c    23 Jul 2003 20:24:36 -0000      1.29
+++ server/gamelog.c    12 Oct 2003 10:06:47 -0000
@@ -59,7 +59,11 @@
   if (level > gamelog_level)
     return;
 
-  fs=fopen(gamelog_filename, "a");
+  if (!strcmp(gamelog_filename, "-")){
+    fs=stderr;
+  }else{
+    fs=fopen(gamelog_filename, "a");
+  }
   if (!fs) {
     freelog(LOG_FATAL, _("Couldn't open gamelogfile \"%s\" for appending."), 
            gamelog_filename);
Index: server/mapgen.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/mapgen.c,v
retrieving revision 1.118
diff -u -r1.118 mapgen.c
--- server/mapgen.c     3 Oct 2003 11:29:31 -0000       1.118
+++ server/mapgen.c     12 Oct 2003 10:06:47 -0000
@@ -1123,7 +1123,7 @@
     counter++;
     if (counter > MAXTRIES) {
       char filename[] = "map_core.sav";
-      save_game(filename);
+      save_game(NULL, filename);
       die(_("The server appears to have gotten into an infinite loop "
            "in the allocation of starting positions, and will abort.\n"
            "The map has been saved into %s.\n"
Index: server/plrhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/plrhand.c,v
retrieving revision 1.294
diff -u -r1.294 plrhand.c
--- server/plrhand.c    22 Sep 2003 20:25:56 -0000      1.294
+++ server/plrhand.c    12 Oct 2003 10:06:48 -0000
@@ -1109,7 +1109,7 @@
   if (pplayer) {
     dest = (struct conn_list*)&pplayer->connections;
   } else {
-    dest = &game.game_connections;
+    dest = &game.est_connections;
   }
   
   va_start(args, format);
@@ -1128,7 +1128,7 @@
   if (pplayer) {
     dest = (struct conn_list*)&pplayer->connections;
   } else {
-    dest = &game.game_connections;
+    dest = &game.est_connections;
   }
   
   va_start(args, format);
@@ -1893,3 +1893,41 @@
 {
   send_attribute_block(pplayer, pplayer->current_conn);
 }
+
+/************************************************************************** 
+we should only send this packet to a client starting up in cmdlevel hack.
+for now, there's no better place for this to go.
+**************************************************************************/ 
+void handle_player_list_request(struct connection * pconn)
+{
+  int i;
+  struct packet_player_list packet;
+
+  packet.nplayers = game.nplayers;
+
+  sz_strlcpy(packet.load_filename, srvarg.load_filename);
+
+  for (i = 0; i < game.nplayers; i++) {
+    struct player *pplayer = &game.players[i];
+
+    if (game.nation_count && is_barbarian(pplayer)){
+      packet.nplayers--;
+      continue;
+    }
+
+    sz_strlcpy(packet.name[i], pplayer->name);
+    sz_strlcpy(packet.username[i], pplayer->username);
+    sz_strlcpy(packet.username[i], pplayer->username);
+    if (game.nation_count){
+      sz_strlcpy(packet.nation_name[i], get_nation_name(pplayer->nation));
+      sz_strlcpy(packet.nation_flag[i], 
get_nation_by_plr(pplayer)->flag_graphic_str);
+    }else{ /* No nations picked */ 
+      sz_strlcpy(packet.nation_name[i], "");
+      sz_strlcpy(packet.nation_flag[i], "");
+    }
+    packet.is_alive[i] = pplayer->is_alive;
+    packet.ai[i] = pplayer->ai.control;
+  }
+  send_packet_player_list_reply(pconn, &packet);
+}
+
Index: server/plrhand.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/plrhand.h,v
retrieving revision 1.57
diff -u -r1.57 plrhand.h
--- server/plrhand.h    22 Sep 2003 20:25:56 -0000      1.57
+++ server/plrhand.h    12 Oct 2003 10:06:48 -0000
@@ -71,6 +71,10 @@
 void handle_player_attribute_chunk(struct player *pplayer,
                                   struct packet_attribute_chunk *chunk);
 void handle_player_attribute_block(struct player *pplayer);
+
+void handle_player_list_request(struct connection *pconn);
+
+
 void found_new_tech(struct player *plr, int tech_found, bool was_discovery, 
                    bool saving_bulbs);
 void found_new_future_tech(struct player *pplayer);
Index: server/report.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/report.c,v
retrieving revision 1.46
diff -u -r1.46 report.c
--- server/report.c     15 Sep 2003 19:40:53 -0000      1.46
+++ server/report.c     12 Oct 2003 10:06:48 -0000
@@ -1188,3 +1188,24 @@
   
   lsend_packet_generic_message(dest, PACKET_PAGE_MSG, &genmsg);
 }
+
+void page_conn_flagged(struct conn_list *dest, const char *caption,
+                           const char *headline, const char *lines) 
+{
+  int len;
+  struct packet_generic_message genmsg;
+
+  len = my_snprintf(genmsg.message, sizeof(genmsg.message),
+                   "%s\n%s\n%s", caption, headline, lines);
+  if (len == -1) {
+    freelog(LOG_ERROR, "Message truncated in page_conn_etype()!");
+  }
+  genmsg.event = E_REPORT;
+
+  genmsg.x = 0; /*this is the flag*/
+  /* unused but at least initialized */
+  genmsg.y = -1;
+  genmsg.event = -1;
+  
+  lsend_packet_generic_message(dest, PACKET_PAGE_MSG, &genmsg);
+}
Index: server/report.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/report.h,v
retrieving revision 1.7
diff -u -r1.7 report.h
--- server/report.h     15 Sep 2003 19:40:53 -0000      1.7
+++ server/report.h     12 Oct 2003 10:06:48 -0000
@@ -20,6 +20,8 @@
 
 void page_conn(struct conn_list *dest, const char *caption, const char 
*headline,
               const char *lines);
+void page_conn_flagged(struct conn_list *dest, const char *caption,
+                      const char *headline, const char *lines);
 
 void make_history_report(void);
 void report_wonders_of_the_world(struct conn_list *dest);
Index: server/ruleset.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/ruleset.c,v
retrieving revision 1.155
diff -u -r1.155 ruleset.c
--- server/ruleset.c    2 Oct 2003 17:54:58 -0000       1.155
+++ server/ruleset.c    12 Oct 2003 10:06:48 -0000
@@ -16,6 +16,8 @@
 #endif
 
 #include <assert.h>
+#include <dirent.h>
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -2920,4 +2922,91 @@
   send_ruleset_cities(dest);
 
   conn_list_do_unbuffer(dest);
+}
+
+/**************************************************************************
+  The ruleset directories in the data path.
+**************************************************************************/
+char **find_ruleset_dirs(void)
+{
+  const char **dirs = get_data_dirs(NULL);
+  int dir_num;
+  static char **out = NULL;
+  int outi = 0;
+
+  if (!out) {
+    out = (char**)fc_malloc(32 * sizeof(char*));
+    for (outi = 0; outi < 32; outi++) {
+      out[outi] = (char*)fc_malloc(128 * sizeof(char));
+    }
+    outi = 0;
+  }
+
+  for (dir_num=0; dirs[dir_num]; dir_num++) {
+    DIR *dir;
+    struct dirent *entry;
+    /* Open the directory for reading. */ 
+    dir = opendir(dirs[dir_num]);
+    if (!dir) {
+      if (errno == ENOENT) {
+       freelog(LOG_VERBOSE, "Skipping non-existing data directory %s.", 
+               dirs[dir_num]);
+      } else {
+       freelog(LOG_ERROR, _("Could not read data directory %s: %s."), 
+               dirs[dir_num], strerror(errno));
+      }
+      continue;
+    }
+    while ((entry = readdir(dir))) {
+      DIR *prsd; /* Potential RuleSet Directory */ 
+      char prsdn[256];
+      struct dirent *i;
+      sprintf(prsdn, "%s/%s", dirs[dir_num], entry->d_name);
+      prsd = opendir(prsdn);
+      if (!prsd) {
+        continue; /* probably not a directory */ 
+      }
+      while ((i = readdir(prsd))) {
+        /* If it has *any* appropriate rulesets, it's good
+         * we can't just check the extension because of the nation directory 
+          */ 
+       if (!strcmp(i->d_name, "buildings.ruleset")) {
+         strcpy(out[outi++], entry->d_name);
+         break;
+       }
+       if (!strcmp(i->d_name, "cities.ruleset")) {
+         strcpy(out[outi++], entry->d_name);
+         break;
+       }
+       if (!strcmp(i->d_name, "game.ruleset")) {
+         strcpy(out[outi++], entry->d_name);
+         break;
+       }
+       if (!strcmp(i->d_name, "governments.ruleset")) {
+         strcpy(out[outi++], entry->d_name);
+         break;
+       }
+       if (!strcmp(i->d_name, "nations.ruleset")) {
+         strcpy(out[outi++], entry->d_name);
+         break;
+       }
+       if (!strcmp(i->d_name, "techs.ruleset")) {
+         strcpy(out[outi++], entry->d_name);
+         break;
+       }
+       if (!strcmp(i->d_name, "terrain.ruleset")) {
+         strcpy(out[outi++], entry->d_name);
+         break;
+       }
+       if (!strcmp(i->d_name, "units.ruleset")) {
+         strcpy(out[outi++], entry->d_name);
+         break;
+       }
+      }
+      closedir(prsd);
+    }
+    closedir(dir);
+  }
+  out[outi][0] = '\0';
+  return out;
 }
Index: server/ruleset.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/ruleset.h,v
retrieving revision 1.9
diff -u -r1.9 ruleset.h
--- server/ruleset.h    18 Dec 2002 17:36:20 -0000      1.9
+++ server/ruleset.h    12 Oct 2003 10:06:48 -0000
@@ -17,5 +17,6 @@
 
 void load_rulesets(void);
 void send_rulesets(struct conn_list *dest);
+char **find_ruleset_dirs(void);
 
 #endif  /* FC__RULESET_H */
Index: server/srv_main.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.c,v
retrieving revision 1.139
diff -u -r1.139 srv_main.c
--- server/srv_main.c   23 Sep 2003 15:59:05 -0000      1.139
+++ server/srv_main.c   12 Oct 2003 10:06:49 -0000
@@ -170,7 +170,7 @@
 
   srvarg.log_filename = NULL;
   srvarg.gamelog_filename = NULL;
-  srvarg.load_filename = NULL;
+  srvarg.load_filename[0] = '\0';
   srvarg.script_filename = NULL;
 
   srvarg.quitidle = 0;
@@ -543,7 +543,7 @@
 Note that if !HAVE_LIBZ, then game.save_compress_level should never
 become non-zero, so no need to check HAVE_LIBZ explicitly here as well.
 **************************************************************************/
-void save_game(char *orig_filename)
+void save_game(struct connection *pconn, char *orig_filename)
 {
   char filename[600];
   struct section_file file;
@@ -570,10 +570,19 @@
     }
   }
 
-  if(!section_file_save(&file, filename, game.save_compress_level))
-    con_write(C_FAIL, _("Failed saving game as %s"), filename);
-  else
-    con_write(C_OK, _("Game saved as %s"), filename);
+  if (!section_file_save(&file, filename, game.save_compress_level)) {
+    if (pconn) {
+      notify_conn(&pconn->self, _("Failed saving game as %s"), filename);
+    } else {
+      con_write(C_FAIL, _("Failed saving game as %s"), filename);
+    }
+  } else {
+    if (pconn) {
+      notify_conn(&pconn->self, _("Game saved as %s"), filename);
+    } else {
+      con_write(C_OK, _("Game saved as %s"), filename);
+    }
+  }
 
   section_file_free(&file);
 
@@ -593,7 +602,7 @@
   
   my_snprintf(filename, sizeof(filename),
              "%s%+05d.sav", game.save_name, game.year);
-  save_game(filename);
+  save_game(NULL, filename);
   gamelog_save();              /* should this be in save_game()? --dwp */
 }
 
@@ -631,8 +640,9 @@
 {
   struct conn_list *dest = &pconn->self;
   
-  if (server_state != RUN_GAME_STATE && server_state != GAME_OVER_STATE
-      && type != REPORT_SERVER_OPTIONS1 && type != REPORT_SERVER_OPTIONS2) {
+  if (((server_state != RUN_GAME_STATE && server_state != GAME_OVER_STATE) ||
+       !pconn->player) &&
+      type != REPORT_SERVER_OPTIONS1 && type != REPORT_SERVER_OPTIONS2) {
     freelog(LOG_ERROR, "Got a report request %d before game start", type);
     return;
   }
@@ -744,9 +754,34 @@
     return TRUE;
   }
 
+  if (type == PACKET_CLIENT_WANTS_HACK) {
+    handle_client_wants_hack(pconn, (struct packet_generic_integer*)packet);
+    free(packet);
+    return TRUE;
+  }
+
+  if (type == PACKET_PLAYER_LIST_REQUEST) {
+    handle_player_list_request(pconn);
+    free(packet);
+    return TRUE;
+  }
+
+  if (type == PACKET_REPORT_REQUEST) {
+    handle_report_request(pconn,
+                         ((struct packet_generic_integer *)packet)->value);
+    return TRUE;
+  }
+
+  /* need this here so we can rename ourselves to a player if need be. */ 
+  if (type == PACKET_CHAT_MSG) {
+    handle_chat_msg(pconn, (struct packet_generic_message*)packet);
+    free(packet);
+    return TRUE;
+  }
+
   pplayer = pconn->player;
 
-  if(!pplayer) {
+  if (!pplayer) {
     /* don't support these yet */
     freelog(LOG_ERROR, "Received packet from non-player connection %s",
            conn_description(pconn));
@@ -893,10 +928,6 @@
   case PACKET_DIPLOMAT_ACTION:
     handle_diplomat_action(pplayer, (struct packet_diplomat_action *)packet);
     break;
-  case PACKET_REPORT_REQUEST:
-    handle_report_request(pconn,
-                         ((struct packet_generic_integer *)packet)->value);
-    break;
   case PACKET_DIPLOMACY_INIT_MEETING:
     handle_diplomacy_init(pplayer, (struct packet_diplomacy_info *)packet);
     break;
@@ -1563,7 +1594,7 @@
   server_open_socket();
 
   /* load a saved game */
-  if (srvarg.load_filename) {
+  if (srvarg.load_filename[0] != '\0') {
     load_command(NULL, srvarg.load_filename);
   } 
 
Index: server/srv_main.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/srv_main.h,v
retrieving revision 1.15
diff -u -r1.15 srv_main.h
--- server/srv_main.h   24 Mar 2003 22:20:58 -0000      1.15
+++ server/srv_main.h   12 Oct 2003 10:06:49 -0000
@@ -13,6 +13,7 @@
 #ifndef FC__SRV_MAIN_H
 #define FC__SRV_MAIN_H
 
+#include "connection.h"
 #include "game.h"
 #include "packets.h"
 
@@ -32,7 +33,7 @@
   /* filenames */
   char *log_filename;
   char *gamelog_filename;
-  char *load_filename;
+  char load_filename[512];
   char *script_filename;
   /* extra info for the metaserver */
   char extra_metaserver_info[256];
@@ -46,7 +47,7 @@
 
 bool handle_packet_input(struct connection *pconn, void *packet, int type);
 void start_game(void);
-void save_game(char *orig_filename);
+void save_game(struct connection *pconn, char *orig_filename);
 void pick_ai_player_name(Nation_Type_id nation, char *newname);
 void send_all_info(struct conn_list *dest);
 void check_for_full_turn_done(void);
Index: server/stdinhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.c,v
retrieving revision 1.296
diff -u -r1.296 stdinhand.c
--- server/stdinhand.c  9 Oct 2003 12:06:22 -0000       1.296
+++ server/stdinhand.c  12 Oct 2003 10:06:49 -0000
@@ -31,6 +31,7 @@
 
 #include "astring.h"
 #include "capability.h"
+#include "connection.h"
 #include "cm.h"
 #include "events.h"
 #include "fcintl.h"
@@ -70,7 +71,7 @@
 static enum cmdlevel_id   first_access_level = ALLOW_CTRL;
 
 static void cut_client_connection(struct connection *caller, char *name);
-static void show_help(struct connection *caller, char *arg);
+static void show_help(struct connection *caller, char *arg, enum cmdlevel_id 
cmd);
 static void show_list(struct connection *caller, char *arg);
 static void show_connections(struct connection *caller);
 static void set_ai_level(struct connection *caller, char *name, int level);
@@ -809,7 +810,7 @@
             is_valid_demography,
             GAME_DEFAULT_DEMOGRAPHY)
 
-  GEN_INT("saveturns", game.save_nturns, SSET_META, SSET_SERVER_ONLY,
+  GEN_INT("saveturns", game.save_nturns, SSET_META, SSET_TO_CLIENT,
          N_("Turns per auto-save"),
          N_("The game will be automatically saved per this number of turns.\n"
             "Zero means never auto-save."), NULL, 
@@ -840,7 +841,7 @@
          GAME_NO_COMPRESS_LEVEL)
 #endif
 
-  GEN_STRING("savename", game.save_name, SSET_META, SSET_SERVER_ONLY,
+  GEN_STRING("savename", game.save_name, SSET_META, SSET_TO_CLIENT,
             N_("Auto-save name prefix"),
             N_("Automatically saved games will have name "
                "\"<prefix><year>.sav\".\nThis setting sets "
@@ -976,6 +977,7 @@
   /* old one-letter commands: */
   CMD_START_GAME = 0,
   CMD_HELP,
+  CMD_HELP_POPUP, 
   CMD_LIST,
   CMD_QUIT,
   CMD_CUT,
@@ -1028,7 +1030,8 @@
   /* pseudo-commands: */
   CMD_NUM,             /* the number of commands - for iterations */
   CMD_UNRECOGNIZED,    /* used as a possible iteration result */
-  CMD_AMBIGUOUS                /* used as a possible iteration result */
+  CMD_AMBIGUOUS,                /* used as a possible iteration result */ 
+  CMD_FLUSH             /* tells cmd_reply to flush any buffers */ 
 };
 
 
@@ -1064,6 +1067,23 @@
       "The argument may be abbreviated where unambiguous.")
   },
 
+  {"helppopup",        ALLOW_INFO, 
+   /* TRANS: translate text between <> only */ 
+   N_("helppopup\n"
+      "helppopup commands\n"
+      "helppopup options\n"
+      "helppopup <command-name>\n"
+      "helppopup <option-name>"), 
+   N_("Show help about server commands and server options in a popup window in 
a client."), 
+   N_("With no arguments gives some introductory help.  "
+      "With argument \"commands\" or \"options\" gives respectively "
+      "a list of all commands or all options.  "
+      "Otherwise the argument is taken as a command name or option name, "
+      "and help is given for that command or option.  For options, the help "
+      "information includes the current and default values for that option.  "
+      "The argument may be abbreviated where unambiguous.")
+  }, 
+
   {"list",     ALLOW_INFO,
    "list\n"
    "list players\n"
@@ -1148,11 +1168,13 @@
    N_("rulesetdir <directory>"),
    N_("Choose new ruleset directory or modpack. Calling this\n "
       "without any arguments will show you the currently selected "
-      "ruleset."), NULL
+      "ruleset and a list of rulesets that appear to be installed."), NULL
   },
-  {"rename",   ALLOW_CTRL,
-   NULL,
-   N_("This command is not currently implemented."), NULL
+  {"rename",    ALLOW_CTRL, 
+   /* TRANS: translate text between <> only */ 
+   N_("rename <old username> <new username>"), 
+   N_("Used to associate connections with players created when "
+      "a savegame is loaded."), NULL
   },
   {"metainfo", ALLOW_CTRL,
    /* TRANS: translate text between <> only */
@@ -1263,7 +1285,7 @@
       "has no effect.")
   },
 #endif
-  {"cmdlevel", ALLOW_HACK,  /* confusing to leave this at ALLOW_CTRL */
+  {"cmdlevel", ALLOW_HACK,  /* confusing to leave this at ALLOW_CTRL */ 
    /* TRANS: translate text between <> only */
    N_("cmdlevel\n"
       "cmdlevel <level>\n"
@@ -1487,10 +1509,36 @@
                              const char *format, va_list ap)
 {
   char buf[4096];
+  static char long_buf[4096] = {0};
   char *c0, *c1;
 
   my_vsnprintf(buf, sizeof(buf), format, ap);
 
+  if (cmd == CMD_HELP_POPUP){
+    strcat(long_buf, buf);
+    strcat(long_buf, _("\n"));
+    return;
+  }
+
+  if (cmd == CMD_FLUSH){
+    struct conn_list *dest;
+    struct conn_list tmp;
+    if (!*long_buf) return;
+    if (caller){
+      dest = &tmp;
+      conn_list_init(dest);
+      conn_list_insert(dest, caller);
+    }else{
+      dest = &(game.est_connections);
+    }
+    page_conn(dest, _("Help"), "", long_buf);
+    if (caller){
+      conn_list_unlink(dest, caller);
+    }
+    long_buf[0] = '\0';
+    return;
+  }
+
   c0 = buf;
   while ((c1=strstr(c0, "\n"))) {
     *c1 = '\0';
@@ -1783,7 +1831,7 @@
              _("The game cannot be saved before it is started."));
     return;
   }
-  save_game(arg);
+  save_game(caller, arg);
 }
 
 /**************************************************************************
@@ -1961,10 +2009,10 @@
 }
 
 /**************************************************************************
-...
+  ...
 **************************************************************************/
-static void rename_player(struct connection *caller, char *arg)
-{
+static void rename_player(struct connection *caller, char *str)
+{ 
   generic_not_implemented(caller, "rename");
 }
 
@@ -2439,9 +2487,16 @@
   if (may_view_option(caller, id)) {
     switch (op->type) {
     case SSET_BOOL:
-      cmd_reply(help_cmd, caller, C_COMMENT,
-               _("Value: %d, Minimum: 0, Default: %d, Maximum: 1"),
-               (*(op->bool_value)) ? 1 : 0, op->bool_default_value ? 1 : 0);
+      if (help_cmd == CMD_HELP_POPUP){ /* graphical interface, don't confuse 
with numbers */ 
+       cmd_reply(help_cmd, caller, C_COMMENT, 
+                 _("Value: %s, Default: %s, "), 
+                 (* (op->bool_value)) ? _("Yes") : _("No"), 
+                 op->bool_default_value ? _("Yes") : _("No"));
+      }else{
+       cmd_reply(help_cmd, caller, C_COMMENT, 
+                 _("Value: %d, Minimum: 0, Default: %d, Maximum: 1"), 
+                 (* (op->bool_value)) ? 1 : 0, op->bool_default_value ? 1 : 0);
+      }
       break;
     case SSET_INT:
       cmd_reply(help_cmd, caller, C_COMMENT,
@@ -2546,17 +2601,21 @@
   char buffer[4096];
   char title[128];
   char *caption;
+  int test_option;
   buffer[0]=0;
   my_snprintf(title, sizeof(title), _("%-20svalue  (min , max)"), _("Option"));
   caption = (which == 1) ?
     _("Server Options (initial)") :
     _("Server Options (ongoing)");
+  test_option = (which == 1) ?
+    lookup_option("xsize") :
+    lookup_option("fogofwar");
 
   for (i=0;settings[i].name;i++) {
     struct settings_s *op = &settings[i];
     if (!sset_is_to_client(i)) continue;
-    if (which==1 && op->sclass > SSET_GAME_INIT) continue;
-    if (which==2 && op->sclass <= SSET_GAME_INIT) continue;
+    if (which == 1 && op->sclass > SSET_RULES) continue;
+    if (which == 2 && op->sclass <= SSET_RULES) continue;
     switch (op->type) {
     case SSET_BOOL:
       cat_snprintf(buffer, sizeof(buffer), "%-20s%c%-6d (0,1)\n",
@@ -2579,7 +2638,12 @@
     }
   }
   freelog(LOG_DEBUG, "report_server_options buffer len %d", i);
-  page_conn(dest, caption, title, buffer);
+  if ((sset_is_changeable(0) || which != 1) &&
+      conn_list_get(dest,0)->access_level >= ALLOW_CTRL) {
+    page_conn_flagged(dest, caption, title, buffer);
+  } else {
+    page_conn(dest, caption, title, buffer);
+  }
 }
 
 /******************************************************************
@@ -3750,10 +3814,14 @@
   loadtimer = new_timer_start(TIMER_CPU, TIMER_ACTIVE);
   uloadtimer = new_timer_start(TIMER_USER, TIMER_ACTIVE);
 
+  sz_strlcpy(srvarg.load_filename, arg);
+
   game_load(&file);
   section_file_check_unused(&file, arg);
   section_file_free(&file);
 
+  cmd_reply(CMD_LOAD, caller, C_OK, _("Loading saved game: %s..."), arg);
+
   freelog(LOG_VERBOSE, "Load time: %g seconds (%g apparent)",
           read_timer_seconds_free(loadtimer),
           read_timer_seconds_free(uloadtimer));
@@ -3773,6 +3841,34 @@
     } players_iterate_end;
   } conn_list_iterate_end;
 
+  /*  send_rulesets(&(game.est_connections));*/ 
+
+}
+
+/**************************************************************************
+  Provide a textual list of installed ruleset directories, and
+  seperate them with slashes because that's the one character filenames 
+  can't contain.
+**************************************************************************/
+char* list_ruleset_dirs(void)
+{
+  char **dirs = find_ruleset_dirs();
+  int i;
+  static char out[MAX_LEN_MSG];
+
+  out[0] = '\0';
+  for(i = 0; dirs[i][0]; i++){
+    int j = 0;
+    while (strcmp(dirs[i], dirs[j])){
+      j++;
+    }
+    if (j < i) { /* Dupe */
+      continue; 
+    }
+    strcat(out, "/");
+    strcat(out, dirs[i]);
+  }
+  return(out + 1); /* cut off leading slash */
 }
 
 /**************************************************************************
@@ -3781,9 +3877,11 @@
 static void set_rulesetdir(struct connection *caller, char *str)
 {
   char filename[512], *pfilename;
-  if ((str == NULL) || (strlen(str)==0)) {
+  if ((str == NULL) || (strlen(str) == 0)) {
     cmd_reply(CMD_RULESETDIR, caller, C_SYNTAX,
-             _("Current ruleset directory is \"%s\""), game.rulesetdir);
+             _("Current ruleset directory is \"%s\".  "
+              "Available options are %s"), game.rulesetdir, 
+              list_ruleset_dirs());
     return;
   }
   my_snprintf(filename, sizeof(filename), "%s", str);
@@ -3953,7 +4051,8 @@
     metaserver_command(caller,arg);
     break;
   case CMD_HELP:
-    show_help(caller, arg);
+  case CMD_HELP_POPUP:
+    show_help(caller, arg, cmd);
     break;
   case CMD_LIST:
     show_list(caller, arg);
@@ -4204,7 +4303,7 @@
     static struct astring abuf = ASTRING_INIT;
     const char *help = _(cmd->extra_help);
       
-    astr_minsize(&abuf, strlen(help)+1);
+    astr_minsize(&abuf, strlen(help) + 1);
     strcpy(abuf.str, help);
     wordwrap_string(abuf.str, 76);
     cmd_reply(help_cmd, caller, C_COMMENT, _("Description:"));
@@ -4282,7 +4381,7 @@
 /**************************************************************************
 ...
 **************************************************************************/
-static void show_help(struct connection *caller, char *arg)
+static void show_help(struct connection *caller, char *arg, enum command_id 
cmd)
 {
   enum m_pre_result match_result;
   int ind;
@@ -4293,18 +4392,24 @@
   match_result = match_prefix(helparg_accessor, HELP_ARG_NUM, 0,
                              mystrncasecmp, arg, &ind);
 
-  if (match_result==M_PRE_EMPTY) {
-    show_help_intro(caller, CMD_HELP);
+  if (match_result == M_PRE_EMPTY) {
+    show_help_intro(caller, cmd);
     return;
   }
-  if (match_result==M_PRE_AMBIGUOUS) {
-    cmd_reply(CMD_HELP, caller, C_FAIL,
+  if (match_result == M_PRE_AMBIGUOUS) {
+    cmd_reply(cmd, caller, C_FAIL, 
              _("Help argument '%s' is ambiguous."), arg);
+    if (cmd == CMD_HELP_POPUP) {
+      cmd_reply(CMD_FLUSH, caller, C_OK, " ");
+    }
     return;
   }
-  if (match_result==M_PRE_FAIL) {
-    cmd_reply(CMD_HELP, caller, C_FAIL,
+  if (match_result == M_PRE_FAIL) {
+    cmd_reply(cmd, caller, C_FAIL, 
              _("No match for help argument '%s'."), arg);
+    if (cmd == CMD_HELP_POPUP) {
+      cmd_reply(CMD_FLUSH, caller, C_OK, " ");
+    }
     return;
   }
 
@@ -4312,26 +4417,38 @@
   assert(match_result < M_PRE_AMBIGUOUS);
   
   if (ind < CMD_NUM) {
-    show_help_command(caller, CMD_HELP, ind);
+    show_help_command(caller, cmd, ind);
+    if (cmd == CMD_HELP_POPUP) {
+      cmd_reply(CMD_FLUSH, caller, C_OK, " ");
+    }
     return;
   }
   ind -= CMD_NUM;
   
   if (ind == HELP_GENERAL_OPTIONS) {
-    show_help_option_list(caller, CMD_HELP);
+    show_help_option_list(caller, cmd);
+    if (cmd == CMD_HELP_POPUP) {
+      cmd_reply(CMD_FLUSH, caller, C_OK, " ");
+    }
     return;
   }
   if (ind == HELP_GENERAL_COMMANDS) {
-    show_help_command_list(caller, CMD_HELP);
+    show_help_command_list(caller, cmd);
+    if (cmd == CMD_HELP_POPUP) {
+      cmd_reply(CMD_FLUSH, caller, C_OK, " ");
+    }
     return;
   }
   ind -= HELP_GENERAL_NUM;
   
   if (ind < SETTINGS_NUM) {
-    show_help_option(caller, CMD_HELP, ind);
+    show_help_option(caller, cmd, ind);
+    if (cmd == CMD_HELP_POPUP) {
+      cmd_reply(CMD_FLUSH, caller, C_OK, " ");
+    }
     return;
   }
-  
+
   /* should have finished by now */
   freelog(LOG_ERROR, "Bug in show_help!");
 }
Index: server/stdinhand.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.h,v
retrieving revision 1.26
diff -u -r1.26 stdinhand.h
--- server/stdinhand.h  17 Dec 2002 15:14:48 -0000      1.26
+++ server/stdinhand.h  12 Oct 2003 10:06:49 -0000
@@ -26,6 +26,7 @@
 void set_ai_level_directer(struct player *pplayer, int level);
 bool read_init_script(struct connection *caller, char *script_filename);
 void show_players(struct connection *caller);
+char* list_ruleset_dirs(void);
 
 void load_command(struct connection *caller, char *arg);
 

[Prev in Thread] Current Thread [Next in Thread]