Complete.Org: Mailing Lists: Archives: freeciv-dev: August 2002:
[Freeciv-Dev] Re: switching tilesets at runtime (PR#1930)
Home

[Freeciv-Dev] Re: switching tilesets at runtime (PR#1930)

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: freeciv-dev@xxxxxxxxxxx
Cc: bugs@xxxxxxxxxxxxxxxxxxx
Subject: [Freeciv-Dev] Re: switching tilesets at runtime (PR#1930)
From: Jason Short <jdorje@xxxxxxxxxxxxxxxxxxxxx>
Date: Fri, 16 Aug 2002 12:03:27 -0700 (PDT)

jdorje@xxxxxxxxxxxxxxxxxxxxx wrote:
The time has finally come...the client is, IMO, ready for a patch that will switch tilesets at runtime.

And here is a patch.

This is obviously not ready for inclusion...there is no GUI-specific code for tileset switching yet. But it does demonstrate one possible interface.

Support for GTK, GTK2, and XAW is included.  GTK and XAW tested.

Note that without the tilespec fix (PR#1932) you can easily crash the XAW client with this.

jason
? civscore.log
? civserver-gcc2.95-O3
? civserver-gcc3.1-O3
? freeciv-patches.tgz
? freeciv.kdevprj
? freeciv.kdevses
? jason-game.gz
? option_list-2.diff
? option_list-3.diff
? option_list-gtk.diff
? option_list.diff
? output
? output-gcc2.95
? output-gcc3.1
? rc
? test.pl
? tileset_switching-3.diff
? tilespec_fix.diff
? client/gui-gtk/diff
Index: client/civclient.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/civclient.c,v
retrieving revision 1.142
diff -u -r1.142 civclient.c
--- client/civclient.c  2002/08/06 22:26:58     1.142
+++ client/civclient.c  2002/08/16 18:37:30
@@ -73,7 +73,6 @@
    needed (hence, it is not 'extern'd in civclient.h) */
 bool is_server = FALSE;
 
-char tile_set_name[512] = "\0";
 char sound_plugin_name[512] = "\0";
 char sound_set_name[512] = "\0";
 char server_host[512] = "\0";
@@ -195,8 +194,6 @@
   agents_init();
   load_general_options();
 
-  if (tile_set_name[0] == '\0') 
-    sz_strlcpy(tile_set_name, default_tile_set_name); 
   if (sound_set_name[0] == '\0') 
     sz_strlcpy(sound_set_name, default_sound_set_name); 
   if (sound_plugin_name[0] == '\0')
Index: client/options.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/options.c,v
retrieving revision 1.65
diff -u -r1.65 options.c
--- client/options.c    2002/08/15 20:24:08     1.65
+++ client/options.c    2002/08/16 18:37:30
@@ -45,7 +45,7 @@
 char default_server_host[512] = "localhost";
 int  default_server_port = DEFAULT_SOCK_PORT;
 char default_metaserver[512] = METALIST_ADDR;
-char default_tile_set_name[512] = "\0";
+char tile_set_name[512] = "\0";
 char default_sound_set_name[512] = "stdsounds";
 char default_sound_plugin_name[512] = "\0";
 
@@ -68,28 +68,32 @@
 bool meta_accelerators = TRUE;
 bool map_scrollbars = TRUE;
 
+static void change_tile_set(struct client_option *o);
+
 #define GEN_INT_OPTION(name, desc) { #name, desc, COT_INT, \
-                                     &name, NULL, NULL, 0, NULL, NULL }
+                                     &name, NULL, NULL, 0, NULL, NULL, NULL }
 #define GEN_BOOL_OPTION(name, desc) { #name, desc, COT_BOOL, \
-                                      NULL, &name, NULL, 0, NULL, NULL }
-#define GEN_STR_OPTION(name, desc, dflt) { #name, desc, COT_STR, \
+                                      NULL, &name, NULL, 0, NULL, NULL, NULL }
+#define GEN_STR_OPTION(name, desc, dflt, cb) { #name, desc, COT_STR, \
                                      NULL, NULL, name, sizeof(name), \
-                                     dflt, NULL }
+                                     cb, dflt, NULL }
 #define GEN_OPTION_TERMINATOR { NULL, NULL, COT_BOOL, \
                                 NULL, NULL, NULL, 0, NULL }
 
 client_option options[] = {
   GEN_STR_OPTION(default_player_name,       N_("Default player's username"),
-                NULL), 
-  GEN_STR_OPTION(default_server_host,       N_("Default server"), NULL),
+                NULL, NULL), 
+  GEN_STR_OPTION(default_server_host,       N_("Default server"),
+                NULL, NULL),
   GEN_INT_OPTION(default_server_port,       N_("Default server's port")),
-  GEN_STR_OPTION(default_metaserver,        N_("Default metaserver"), NULL),
-  GEN_STR_OPTION(default_tile_set_name,     N_("Default tileset"),
-                get_tileset_list),
+  GEN_STR_OPTION(default_metaserver,        N_("Default metaserver"),
+                NULL, NULL),
+  GEN_STR_OPTION(tile_set_name,     N_("Tileset"),
+                get_tileset_list, change_tile_set),
   GEN_STR_OPTION(default_sound_set_name,    N_("Default name of sound set"),
-                get_soundset_list),
+                get_soundset_list, NULL),
   GEN_STR_OPTION(default_sound_plugin_name, N_("Default sound plugin"),
-                get_soundplugin_list),
+                get_soundplugin_list, NULL),
 
   GEN_BOOL_OPTION(solid_color_behind_units, N_("Solid unit background color")),
   GEN_BOOL_OPTION(sound_bell_at_new_turn,   N_("Sound bell at new turn")),
@@ -262,6 +266,20 @@
   GEN_EV(N_("Wonder: Will Finish Next Turn"),         E_WONDER_WILL_BE_BUILT),
   GEN_EV_TERMINATOR
 };
+
+/*
+ * Option change callbacks.
+ */
+
+
+/**************************************************************************
+  A callback function invoked when the tileset option has been changed.  It
+  in turn calls a function to read the new tilespec data.
+**************************************************************************/
+static void change_tile_set(struct client_option *o)
+{
+  tilespec_reread(o->p_string_value);
+}
 
 /* 
  * Maps from enum event_type to indexes of events[]. Set by
Index: client/options.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/options.h,v
retrieving revision 1.20
diff -u -r1.20 options.h
--- client/options.h    2002/08/15 20:24:08     1.20
+++ client/options.h    2002/08/16 18:37:30
@@ -22,7 +22,7 @@
 extern char default_server_host[512];
 extern int default_server_port; 
 extern char default_metaserver[512];
-extern char default_tile_set_name[512];
+extern char tile_set_name[512];
 extern char default_sound_set_name[512];
 extern char default_sound_plugin_name[512];
 
@@ -51,7 +51,7 @@
   COT_STR
 };
 
-typedef struct {
+typedef struct client_option {
   char *name;
   char *description;
   enum client_option_type type;
@@ -59,6 +59,7 @@
   bool *p_bool_value;
   char *p_string_value;
   size_t string_length;
+  void (*change_callback) (struct client_option * o);
 
   /* 
    * A function to return a static NULL-terminated list of possible
Index: client/tilespec.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.c,v
retrieving revision 1.82
diff -u -r1.82 tilespec.c
--- client/tilespec.c   2002/08/15 20:24:09     1.82
+++ client/tilespec.c   2002/08/16 18:37:31
@@ -46,6 +46,7 @@
 #include "climisc.h" /* for tile_get_known() */
 #include "control.h" /* for fill_xxx */
 #include "graphics_g.h"
+#include "mapview_g.h" /* for mapcanvas centering */
 #include "options.h" /* for fill_xxx */
 
 #include "tilespec.h"
@@ -116,6 +117,9 @@
 */
 static bool no_backdrop = FALSE;
 
+
+static void tilespec_free_tiles(void);
+
 /**********************************************************************
   Returns a static list of tilesets available on the system by
   searching all data directories for files matching TILESPEC_SUFFIX.
@@ -230,6 +234,130 @@
 }
 
 /**********************************************************************
+  Frees the tilespec toplevel data, in preparation for re-reading it.
+
+  See tilespec_read_toplevel().
+***********************************************************************/
+static void tilespec_free_toplevel(void)
+{
+  if (city_names_font) {
+    free(city_names_font);
+    city_names_font = NULL;
+  }
+  if (city_productions_font_name) {
+    free(city_productions_font_name);
+    city_productions_font_name = NULL;
+  }
+  if (main_intro_filename) {
+    free(main_intro_filename);
+    main_intro_filename = NULL;
+  }
+  if (minimap_intro_filename) {
+    free(minimap_intro_filename);
+    minimap_intro_filename = NULL;
+  }
+  /* FIXME: free spec_filenames */
+}
+
+/**********************************************************************
+  Frees the tilespec spite data, in preparation for re-reading it.
+
+  See tilespec_setup_***().
+***********************************************************************/
+static void tilespec_free_sprites(void)
+{
+  tilespec_free_city_tiles(game.styles_count);
+}
+
+/**********************************************************************
+  Read a new tilespec in from scratch.
+
+  Unlike the initial reading code, which reads pieces one at a time,
+  this gets rid of the old data and reads in the new all at once.
+
+  It will also call the necessary functions to redraw the graphics.
+***********************************************************************/
+void tilespec_reread(const char *tileset_name)
+{
+  int id;
+  int mapview_center_x, mapview_center_y;
+
+  get_center_tile_mapcanvas(&mapview_center_x, &mapview_center_y);
+
+  freelog(LOG_ERROR, "Loading tileset %s.", tileset_name);
+
+  /*
+   * Step 1:  Cleanup.
+   *
+   * We free any old data in preparation for re-reading it.
+   * This is pretty certainly incomplete, although the memory leak
+   * doesn't seem to be debilitating.
+   */
+  tilespec_free_sprites();
+  tilespec_free_tiles();
+  tilespec_free_toplevel();
+
+  /*
+   * Step 2:  Read.
+   *
+   * We read in the new tileset.  This should be pretty straightforward.
+   */
+  tilespec_read_toplevel(tileset_name);
+  tilespec_load_tiles();
+
+  /*
+   * Step 3: Setup
+   *
+   * The rest of this is a seriously sticky problem.  On
+   * startup, we build a hash from all the sprite data.
+   * Then, when we connect to a server, the server sends us
+   * ruleset data a piece at a time and we use this data to
+   * assemble the sprite structures.  But if we change
+   * while connected we have to reassemble all of these.  This
+   * _should_ just involve calling tilespec_setup_*** on
+   * everything.  But how do we tell what "everything" is?
+   *
+   * The below code assumes that everything really _is_
+   * everything.  But this probably isn't true.  In fact,
+   * it's _certainly_ not true if we haven't received the
+   * info from the server yet.  OTOH, this may just
+   * initialize things to NULL in this case.
+   */
+
+  for (id = T_FIRST; id < T_COUNT; id++) {
+    tilespec_setup_tile_type(id);
+  }
+  unit_type_iterate(id) {
+    tilespec_setup_unit_type(id);
+  } unit_type_iterate_end;
+  for (id = 0; id < game.government_count; id++) {
+    tilespec_setup_government(id);
+  }
+  for (id = 0; id < game.nation_count; id++) {
+    tilespec_setup_nation_flag(id);
+  }
+
+  /* tilespec_load_tiles reverts the city tile pointers to 0.  This
+     is a temporary workaround. */
+  tilespec_alloc_city_tiles(game.styles_count);
+  for (id = 0; id < game.styles_count; id++) {
+    tilespec_setup_city_tiles(id);
+  }
+
+  /*
+   * Step 4:  Draw.
+   *
+   * Do any necessary redraws.
+   *
+   * Redrawing the canvas is tricky.  It will (probably) get manually
+   * done by the GUI after we change things, but until that happens
+   * the gui variables (map_canvas_store_twidth, etc.) aren't
+   * initialized so anything we call here must account for that.
+   */
+  /* center_tile_mapcanvas(mapview_center_x, mapview_center_y); */
+}
+
+/**********************************************************************
   Finds and reads the toplevel tilespec file based on given name.
   Sets global variables, including tile sizes and full names for
   intro files.
@@ -708,6 +836,28 @@
 }
 
 /**********************************************************************
+  Frees the tilespec tile data, in preparation for re-reading it.
+
+  See tilespec_read_tiles().
+***********************************************************************/
+static void tilespec_free_tiles(void)
+{
+  while (hash_num_entries(sprite_hash) > 0) {
+    const void* key = hash_key_by_number(sprite_hash, 0);
+    struct Sprite* sprite = hash_delete_entry(sprite_hash, key);
+#if 0
+    /* Problem - this results in some sprites being freed multiple
+       times, leading to a segfault.  The alternative is just to leak
+       memory. */
+    free_sprite(sprite);
+#endif
+  }
+
+  hash_free(sprite_hash);
+  sprite_hash = NULL;
+}
+
+/**********************************************************************
   Lookup sprite to match tag, or else to match alt if don't find,
   or else return NULL, and emit log message.
 ***********************************************************************/
@@ -1753,7 +1903,9 @@
 }
 
 /**********************************************************************
-  alloc memory for city tiles sprites
+  free memory for city tiles sprites
+
+  see tilespec_alloc_city_tiles
 ***********************************************************************/
 void tilespec_free_city_tiles(int count)
 {
Index: client/tilespec.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/tilespec.h,v
retrieving revision 1.29
diff -u -r1.29 tilespec.h
--- client/tilespec.h   2002/08/15 20:24:09     1.29
+++ client/tilespec.h   2002/08/16 18:37:31
@@ -31,6 +31,8 @@
 void tilespec_read_toplevel(const char *tileset_name);
 void tilespec_load_tiles(void);
 
+void tilespec_reread(const char *tileset_name);
+
 void tilespec_setup_unit_type(int id);
 void tilespec_setup_tile_type(int id);
 void tilespec_setup_government(int id);
Index: client/gui-gtk/gamedlgs.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/gamedlgs.c,v
retrieving revision 1.27
diff -u -r1.27 gamedlgs.c
--- client/gui-gtk/gamedlgs.c   2002/08/15 20:24:10     1.27
+++ client/gui-gtk/gamedlgs.c   2002/08/16 18:37:31
@@ -376,22 +376,36 @@
 {
   client_option *o;
   char *dp;
+  bool b;
+  int i;
 
   for (o=options; o->name; ++o) {
     switch (o->type) {
     case COT_BOOL:
+      b = *(o->p_bool_value);
       *(o->p_bool_value) = GTK_TOGGLE_BUTTON(o->p_gui_data)->active;
+      if (b != *(o->p_bool_value) && o->change_callback) {
+       (o->change_callback)(o);
+      }
       break;
     case COT_INT:
+      i = *(o->p_int_value);
       dp = gtk_entry_get_text(GTK_ENTRY(o->p_gui_data));
       sscanf(dp, "%d", o->p_int_value);
+      if (i != *(o->p_int_value) && o->change_callback) {
+       (o->change_callback)(o);
+      }
       break;
     case COT_STR:
       if (o->p_string_vals) {
-       mystrlcpy(o->p_string_value,
-                 gtk_entry_get_text(GTK_ENTRY
-                                    (GTK_COMBO(o->p_gui_data)->entry)),
-                 o->string_length);
+       char* new_value = gtk_entry_get_text(GTK_ENTRY
+                                           (GTK_COMBO(o->p_gui_data)->entry));
+       if (strcmp(o->p_string_value, new_value)) {
+         mystrlcpy(o->p_string_value, new_value, o->string_length);
+         if (o->change_callback) {
+           (o->change_callback)(o);
+         }
+       }
       } else {
        mystrlcpy(o->p_string_value,
                  gtk_entry_get_text(GTK_ENTRY(o->p_gui_data)),
Index: client/gui-gtk/graphics.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/client/gui-gtk/graphics.c,v
retrieving revision 1.41
diff -u -r1.41 graphics.c
--- client/gui-gtk/graphics.c   2002/08/07 11:21:41     1.41
+++ client/gui-gtk/graphics.c   2002/08/16 18:37:32
@@ -347,6 +347,22 @@
   return mysprite;
 }
 
+/*
+void free_sprite(struct Sprite *sprite)
+{
+  if (!sprite) {
+    return;
+  }
+
+  gdk_imlib_destroy_image(sprite->pixmap);
+  if (sprite->has_mask) {
+    gdk_imlib_destroy_mask(sprite->mask);
+  }
+
+  free(sprite);
+}
+*/
+
 /***************************************************************************
    Deletes a sprite.  These things can use a lot of memory.
 ***************************************************************************/

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