<URL: http://bugs.freeciv.org/Ticket/Display.html?id=40552 >

Attached patch moves the voting code out of stdinhand.[ch]
and into its own code modules voting.[ch].


-----------------------------------------------------------------------
いざという時は、誰に投票するの?
commit c4c237468058e1b40f85af7c36139d10db2a5406
Author: Madeline Book <[EMAIL PROTECTED]>
Date:   Thu Nov 6 16:02:37 2008 -0500

    Voting cleanup.
---
 server/Makefile.am |    4 +-
 server/sernet.c    |    1 +
 server/srv_main.c  |    4 +
 server/stdinhand.c |  463 +++----------------------------------------------
 server/stdinhand.h |    3 -
 server/voting.c    |  490 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 server/voting.h    |   58 ++++++
 7 files changed, 583 insertions(+), 440 deletions(-)

diff --git a/server/Makefile.am b/server/Makefile.am
index 793975e..09f5812 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -85,7 +85,9 @@ libcivserver_a_SOURCES = \
 		unithand.c	\
 		unithand.h	\
 		unittools.c	\
-		unittools.h
+		unittools.h	\
+		voting.c	\
+		voting.h
 
 # hand_gen.c & hand_gen.h are generated files, but as they are generated
 # outside this directory (when building common) there's no point in
diff --git a/server/sernet.c b/server/sernet.c
index fe56b5f..4801e4b 100644
--- a/server/sernet.c
+++ b/server/sernet.c
@@ -83,6 +83,7 @@
 #include "plrhand.h"
 #include "srv_main.h"
 #include "stdinhand.h"
+#include "voting.h"
 
 #include "sernet.h"
 
diff --git a/server/srv_main.c b/server/srv_main.c
index 9a98491..433b9f8 100644
--- a/server/srv_main.c
+++ b/server/srv_main.c
@@ -99,6 +99,7 @@
 #include "techtools.h"
 #include "unithand.h"
 #include "unittools.h"
+#include "voting.h"
 
 #include "advdiplomacy.h"
 #include "advmilitary.h"
@@ -876,6 +877,7 @@ static void end_turn(void)
   update_diplomatics();
   make_history_report();
   stdinhand_turn();
+  voting_turn();
   send_player_turn_notifications(NULL);
 
   freelog(LOG_DEBUG, "Gamenextyear");
@@ -1054,6 +1056,7 @@ void server_quit(void)
 #endif /* HAVE_AUTH */
 
   stdinhand_free();
+  voting_free();
   close_connections_and_socket();
   exit(EXIT_SUCCESS);
 }
@@ -1940,6 +1943,7 @@ static void srv_prepare(void)
   con_flush();
 
   stdinhand_init();
+  voting_init();
   diplhand_init();
 
   /* init network */  
diff --git a/server/stdinhand.c b/server/stdinhand.c
index e9de673..26d1c1c 100644
--- a/server/stdinhand.c
+++ b/server/stdinhand.c
@@ -67,6 +67,7 @@
 #include "settings.h"
 #include "srv_main.h"
 #include "stdinhand.h"
+#include "voting.h"
 
 #include "advmilitary.h"	/* assess_danger_player() */
 #include "ailog.h"
@@ -96,62 +97,11 @@ static bool detach_command(struct connection *caller, char *name, bool check);
 static bool end_command(struct connection *caller, char *str, bool check);
 static bool surrender_command(struct connection *caller, char *str, bool check);
 
-enum vote_type {
-  VOTE_ABSTAIN = 0,
-  VOTE_YES,
-  VOTE_NO,
-
-  NUM_VOTE_TYPES
-};
-
-struct vote_cast {
-  enum vote_type vote_cast;
-  int conn_id;
-};
-
-#define SPECLIST_TAG vote_cast
-#define SPECLIST_TYPE struct vote_cast
-#include "speclist.h"
-#define vote_cast_list_iterate(alist, pvc) \
-    TYPED_LIST_ITERATE(struct vote_cast, alist, pvc)
-#define vote_cast_list_iterate_end  LIST_ITERATE_END
-
-struct vote {
-  int caller_id;     /* caller connection id */
-  char command[MAX_LEN_CONSOLE_LINE]; /* [0] == \0 if none in action */
-  struct vote_cast_list *votes_cast;
-  int vote_no; /* place in the queue */
-  int yes, no, abstain;
+/* Vote command helpers. NB: Must match enum vote_type. */
+static const char *const vote_args[] = {
+  "abstain", "yes", "no", NULL
 };
 
-#define SPECLIST_TAG vote
-#define SPECLIST_TYPE struct vote
-#include "speclist.h"
-#define vote_list_iterate(alist, pvote) \
-      TYPED_LIST_ITERATE(struct vote, alist, pvote)
-#define vote_list_iterate_end  LIST_ITERATE_END
-
-static struct vote_list *vote_list = 0;
-static int vote_number_sequence = 0;
-
-static void voting_init(void);
-static void voting_turn(void);
-static void voting_free(void);
-static int count_voters(void);
-static bool connection_can_vote(struct connection *pconn);
-static void connection_vote(struct connection *pconn, struct vote *pvote,
-                            enum vote_type type);
-static struct vote *vote_new(struct connection *caller,
-                             const char *full_command);
-static void free_vote(struct vote *pvote);
-static struct vote *get_vote_by_no(int vote_no);
-static struct vote *get_vote_by_caller(const struct connection *caller);
-static void remove_vote(struct vote *pvote);
-static struct vote_cast *vote_cast_new(struct vote *pvote);
-static void remove_vote_cast(struct vote *pvote, struct vote_cast *pvc);
-static struct vote_cast *find_vote_cast(struct vote *pvote, int conn_id);
-
-
 static const char horiz_line[] =
 "------------------------------------------------------------------------------";
 
@@ -232,363 +182,7 @@ static enum command_id command_named(const char *token, bool accept_ambiguity)
 **************************************************************************/
 void stdinhand_init(void)
 {
-  voting_init();
-}
-
-/**************************************************************************
-  Initialize voting related data structures.
-**************************************************************************/
-static void voting_init(void)
-{
-  if (!vote_list) {
-    vote_list = vote_list_new();
-    vote_number_sequence = 0;
-  }
-}
-
-/**************************************************************************
-  Cannot vote if:
-    * is not connected
-    * access level < info
-    * isn't a living player
-**************************************************************************/
-static bool connection_can_vote(struct connection *pconn)
-{
-  if (pconn != NULL && pconn->playing != NULL && !pconn->observer
-      && pconn->playing->is_alive && pconn->access_level >= ALLOW_INFO) {
-    return TRUE;
-  }
-  return FALSE;
-}
-
-/**************************************************************************
-  Returns the total number of users that are allowed to vote.
-**************************************************************************/
-static int count_voters(void)
-{
-  int num_voters = 0;
-
-  conn_list_iterate(game.est_connections, pconn) {
-    if (connection_can_vote(pconn)) {
-      num_voters++;
-    }
-  } conn_list_iterate_end;
-
-  return num_voters;
-}
-
-/**************************************************************************
-  Check if we satisfy the criteria for resolving a vote, and resolve it
-  if these critera are indeed met. Updates yes and no variables in voting 
-  struct as well.
-
-  Criteria:
-    Accepted immediately if: > 50% of votes for
-    Rejected immediately if: >= 50% of votes against
-**************************************************************************/
-static void check_vote(struct vote *pvote)
-{
-  struct connection *pconn = NULL;
-  int num_cast = 0, num_voters = 0, voting_base;
-  bool resolve = FALSE, passed = FALSE;
-  char cmdline[MAX_LEN_CONSOLE_LINE];
-
-  pvote->yes = 0;
-  pvote->no = 0;
-  pvote->abstain = 0;
-
-  num_voters = count_voters();
-
-  vote_cast_list_iterate(pvote->votes_cast, pvc) {
-    if (!(pconn = find_conn_by_id(pvc->conn_id))
-        || !connection_can_vote(pconn)) {
-      continue;
-    }
-    num_cast++;
-
-    switch (pvc->vote_cast) {
-    case VOTE_YES:
-      pvote->yes++;
-      break;
-    case VOTE_NO:
-      pvote->no++;
-      break;
-    case VOTE_ABSTAIN:
-      pvote->abstain++;
-      break;
-    default:
-      break;
-    }
-  } vote_cast_list_iterate_end;
-
-  voting_base = num_voters - pvote->abstain;
-
-  /* Check if we should resolve the vote */
-  if ((voting_base > 0 && (pvote->yes > voting_base / 2
-                          || pvote->no >= (voting_base + 1) / 2))
-      || num_cast >= num_voters) {
-    /* Yep, resolve this one */
-    resolve = TRUE;
-  }
-
-  if (!resolve) {
-    return;
-  }
-
-  if (pvote->yes > voting_base / 2) {
-    passed = TRUE;
-  }
-
-  if (passed) {
-    notify_conn(NULL, NULL, E_SETTING,
-                _("Vote \"%s\" is passed %d to %d with %d "
-                  "abstentions and %d that did not vote."),
-                pvote->command, pvote->yes, pvote->no,
-                pvote->abstain, num_voters - num_cast);
-    sz_strlcpy(cmdline, pvote->command);
-  } else {
-    notify_conn(NULL, NULL, E_SETTING,
-                _("Vote \"%s\" failed with %d against, %d for, "	  
-                  "%d abstentions, and %d that did not vote."),
-                pvote->command, pvote->no, pvote->yes, 
-                pvote->abstain, num_voters - num_cast);
-  }
-
-  remove_vote(pvote);
-
-  if (passed) {
-    handle_stdin_input(NULL, cmdline, FALSE);
-  }
-}
-
-/**************************************************************************
-  Remove the given vote.
-**************************************************************************/
-void remove_vote(struct vote *pvote)
-{
-  if (!pvote) {
-    return;
-  }
-
-  if (vote_list) {
-    vote_list_unlink(vote_list, pvote);
-  }
-
-  free_vote(pvote);
-}
-
-/**************************************************************************
-  Free the memory used by the vote structure.
-**************************************************************************/
-static void free_vote(struct vote *pvote)
-{
-  if (!pvote) {
-    return;
-  }
-
-  if (pvote->votes_cast) {
-    vote_cast_list_iterate(pvote->votes_cast, pvc) {
-      free(pvc);
-    } vote_cast_list_iterate_end;
-    vote_cast_list_free(pvote->votes_cast);
-    pvote->votes_cast = NULL;
-  }
-  free(pvote);
-}
-
-/**************************************************************************
-  Check votes at end of turn.
-**************************************************************************/
-static void voting_turn(void)
-{
-  vote_list_iterate(vote_list, pvote) {
-    check_vote(pvote);
-  } vote_list_iterate_end;
-}
-
-/**************************************************************************
-  Free vote related data structures.
-**************************************************************************/
-static void voting_free(void)
-{
-  clear_all_votes();
-  vote_list_free(vote_list);
-  vote_list = NULL;
-}
-
-/**************************************************************************
-  Remove all votes.
-**************************************************************************/
-void clear_all_votes(void)
-{
-  if (!vote_list) {
-    return;
-  }
-
-  vote_list_iterate(vote_list, pvote) {
-    free_vote(pvote);
-  } vote_list_iterate_end;
-  vote_list_clear(vote_list);
-}
-
-/**************************************************************************
-  Returns the vote associated to the given vote number.
-**************************************************************************/
-static struct vote *get_vote_by_no(int vote_no)
-{
-  if (!vote_list) {
-    return NULL;
-  }
-
-  vote_list_iterate(vote_list, pvote) {
-    if (pvote->vote_no == vote_no) {
-      return pvote;
-    }
-  } vote_list_iterate_end;
-
-  return NULL;
-}
-
-/**************************************************************************
-  Find the vote made by the given user.
-**************************************************************************/
-static struct vote *get_vote_by_caller(const struct connection *caller)
-{
-  if (vote_list == NULL || caller == NULL) {
-    return NULL;
-  }
-
-  vote_list_iterate(vote_list, pvote) {
-    if (pvote->caller_id == caller->id) {
-      return pvote;
-    }
-  } vote_list_iterate_end;
-
-  return NULL;
-}
-
-/**************************************************************************
-  Register a vote of type 'type' on the vote 'pvote' made by the user
-  'pconn'.
-**************************************************************************/
-void connection_vote(struct connection *pconn,
-                     struct vote *pvote,
-                     enum vote_type type)
-{
-  assert(vote_list != NULL);
-
-  struct vote_cast *pvc;
-
-  if (!connection_can_vote(pconn)) {
-    return;
-  }
-
-  /* Try to find a previous vote */
-  if ((pvc = find_vote_cast(pvote, pconn->id))) {
-    pvc->vote_cast = type;
-  } else if ((pvc = vote_cast_new(pvote))) {
-    pvc->vote_cast = type;
-    pvc->conn_id = pconn->id;
-  } else {
-    /* Must never happen */
-    assert(0);
-  }
-  check_vote(pvote);
-}
-
-/**************************************************************************
-  Cancel the votes of a lost or a detached connection...
-**************************************************************************/
-void cancel_connection_votes(struct connection *pconn)
-{
-  if (!pconn || !vote_list) {
-    return;
-  }
-
-  remove_vote(get_vote_by_caller(pconn));
-
-  vote_list_iterate(vote_list, pvote) {
-    remove_vote_cast(pvote, find_vote_cast(pvote, pconn->id));
-  } vote_list_iterate_end;
-}
-
-/**************************************************************************
-  Create a new vote.
-**************************************************************************/
-static struct vote *vote_new(struct connection *caller,
-                             const char *full_command)
-{
-  struct vote *pvote;
-
-  assert(vote_list != NULL);
-
-  if (!connection_can_vote(caller)) {
-    return NULL;
-  }
-
-  /* Cancel previous vote */
-  remove_vote(get_vote_by_caller(caller));
-
-  /* Make a new vote */
-  pvote = fc_calloc(1, sizeof(*pvote));
-  pvote->caller_id = caller->id;
-
-  sz_strlcpy(pvote->command, full_command);
-
-  pvote->votes_cast = vote_cast_list_new();
-  pvote->vote_no = ++vote_number_sequence;
-
-  vote_list_append(vote_list, pvote);
-
-  return pvote;
-}
-
-/**************************************************************************
-  Find the vote cast for the user id conn_id in a vote.
-**************************************************************************/
-static struct vote_cast *find_vote_cast(struct vote *pvote, int conn_id)
-{
-  assert(vote_list != NULL);
-
-  vote_cast_list_iterate(pvote->votes_cast, pvc) {
-    if (pvc->conn_id == conn_id) {
-      return pvc;
-    }
-  } vote_cast_list_iterate_end;
-
-  return NULL;
-}
-/**************************************************************************
-  Return a new vote cast.
-**************************************************************************/
-static struct vote_cast *vote_cast_new(struct vote *pvote)
-{
-  assert(vote_list != NULL);
-
-  struct vote_cast *pvc = fc_malloc(sizeof(struct vote_cast));
-
-  pvc->conn_id = -1;
-  pvc->vote_cast = VOTE_ABSTAIN;
-
-  vote_cast_list_append(pvote->votes_cast, pvc);
-
-  return pvc;
-}
-
-/**************************************************************************
-  Remove a vote cast.
-**************************************************************************/
-static void remove_vote_cast(struct vote *pvote, struct vote_cast *pvc)
-{
-  assert(vote_list != NULL);
-
-  if (!pvc) {
-    return;
-  }
-
-  vote_cast_list_unlink(pvote->votes_cast, pvc);
-  free(pvc);
-  check_vote(pvote);            /* Maybe can pass */
+  /* Nothing at the moment. */
 }
 
 /**************************************************************************
@@ -597,7 +191,7 @@ static void remove_vote_cast(struct vote *pvote, struct vote_cast *pvc)
 **************************************************************************/
 void stdinhand_turn(void)
 {
-  voting_turn();
+  /* Nothing at the moment. */
 }
 
 /**************************************************************************
@@ -605,7 +199,7 @@ void stdinhand_turn(void)
 **************************************************************************/
 void stdinhand_free(void)
 {
-  voting_free();
+  /* Nothing at the moment. */
 }
 
 /**************************************************************************
@@ -2495,16 +2089,9 @@ static bool team_command(struct connection *caller, char *str, bool check)
   return res;
 }
 
-  
 /******************************************************************
-  Vote command helpers. NB: Must match enum vote_type.
+  Vote command argument accessor.
 ******************************************************************/
-static const char *const vote_args[] = {
-  "abstain",
-  "yes",
-  "no",
-  NULL
-};
 static const char *vote_arg_accessor(int i)
 {
   return vote_args[i];
@@ -2535,15 +2122,18 @@ static bool vote_command(struct connection *caller, char *str,
   if (ntokens <= 0) {
     int num_voters, vote_count;
     
-    num_voters = count_voters();
-    vote_count = vote_list_size(vote_list);
+    num_voters = voting_get_voter_count();
+    vote_count = voting_get_vote_count();
 
-    vote_list_iterate(vote_list, pvote) {
+    vote_list_iterate(voting_get_vote_list(), pvote) {
       cmd_reply(CMD_VOTE, caller, C_COMMENT,
                 _("Vote %d \"%s\": %d for, "
                   "%d against, and %d abstained out of %d voters."),
-                pvote->vote_no, pvote->command,
-                pvote->yes, pvote->no, pvote->abstain, num_voters);
+                vote_number(pvote), vote_cmdline(pvote),
+                vote_cast_count(pvote, VOTE_YES),
+                vote_cast_count(pvote, VOTE_NO),
+                vote_cast_count(pvote, VOTE_ABSTAIN),
+                num_voters);
     } vote_list_iterate_end;
 
     if (vote_count == 0) {
@@ -2576,10 +2166,12 @@ static bool vote_command(struct connection *caller, char *str,
 
   if (ntokens == 1) {
     /* Applies to last vote */
-    if (vote_number_sequence > 0 && get_vote_by_no(vote_number_sequence)) {
-      vote_no = vote_number_sequence;
+
+    pvote = voting_get_last_vote();
+    if (pvote != NULL) {
+      vote_no = vote_number(pvote);
     } else {
-      int num_votes = vote_list_size(vote_list);
+      int num_votes = voting_get_vote_count();
       if (num_votes == 0) {
         cmd_reply(CMD_VOTE, caller, C_FAIL,
                   _("There are no votes running."));
@@ -2599,23 +2191,23 @@ static bool vote_command(struct connection *caller, char *str,
     }
   }
 
-  if (!(pvote = get_vote_by_no(vote_no))) {
+  if (!(pvote = voting_get_vote_by_no(vote_no))) {
     cmd_reply(CMD_VOTE, caller, C_FAIL, _("No such vote (%d)."), vote_no);
     goto CLEANUP;
   }
 
   if (i == VOTE_YES) {
     cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted for \"%s\""),
-              pvote->command);
+              vote_cmdline(pvote));
     connection_vote(caller, pvote, VOTE_YES);
   } else if (i == VOTE_NO) {
     cmd_reply(CMD_VOTE, caller, C_COMMENT, _("You voted against \"%s\""),
-              pvote->command);
+              vote_cmdline(pvote));
     connection_vote(caller, pvote, VOTE_NO);
   } else if (i == VOTE_ABSTAIN) {
     cmd_reply(CMD_VOTE, caller, C_COMMENT,
               _("You abstained from voting on \"%s\""),
-              pvote->command);
+              vote_cmdline(pvote));
     connection_vote(caller, pvote, VOTE_ABSTAIN);
   } else {
     freelog(LOG_FATAL, "Bad vote type %d in /vote command.", i);
@@ -4015,7 +3607,7 @@ bool handle_stdin_input(struct connection *caller, char *str, bool check)
     /* If we already have a vote going, cancel it in favour of the new
      * vote command (it will be cancelled by vote_new()). You can only
      * have one vote at a time. */
-    if (get_vote_by_caller(caller)) {
+    if (voting_get_vote_by_caller(caller)) {
       cmd_reply(CMD_VOTE, caller, C_COMMENT,
                 _("Your new vote cancelled your previous vote."));
     }
@@ -4025,8 +3617,7 @@ bool handle_stdin_input(struct connection *caller, char *str, bool check)
         && (pvote = vote_new(caller, full_command))) {
       notify_conn(NULL, NULL, E_SETTING,
                   _("New vote (number %d) by %s: %s."),
-                  pvote->vote_no,
-                  caller->username,
+                  vote_number(pvote), caller->username,
                   full_command);
 
       /* Vote on your own suggestion. */
diff --git a/server/stdinhand.h b/server/stdinhand.h
index 33aa18b..b2b331f 100644
--- a/server/stdinhand.h
+++ b/server/stdinhand.h
@@ -52,7 +52,4 @@ char **freeciv_completion(char *text, int start, int end);
 #endif
 #endif
 
-void clear_all_votes(void);
-void cancel_connection_votes(struct connection *pconn);
-
 #endif /* FC__STDINHAND_H */
diff --git a/server/voting.c b/server/voting.c
new file mode 100644
index 0000000..ca32e67
--- /dev/null
+++ b/server/voting.c
@@ -0,0 +1,490 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   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 "fciconv.h"
+#include "fcintl.h"
+#include "mem.h"
+
+#include "connection.h"
+#include "game.h"
+#include "player.h"
+
+#include "console.h"
+#include "plrhand.h"
+#include "stdinhand.h"
+#include "voting.h"
+
+struct vote_cast {
+  enum vote_type vote_cast;
+  int conn_id;
+};
+
+#define SPECLIST_TAG vote_cast
+#define SPECLIST_TYPE struct vote_cast
+#include "speclist.h"
+#define vote_cast_list_iterate(alist, pvc) \
+    TYPED_LIST_ITERATE(struct vote_cast, alist, pvc)
+#define vote_cast_list_iterate_end  LIST_ITERATE_END
+
+struct vote {
+  int caller_id;     /* caller connection id */
+  char command[MAX_LEN_CONSOLE_LINE]; /* [0] == \0 if none in action */
+  struct vote_cast_list *votes_cast;
+  int vote_no;
+  int yes, no, abstain;
+};
+
+struct vote_list *vote_list = 0;
+int vote_number_sequence = 0;
+
+
+static void check_vote(struct vote *pvote);
+static void remove_vote(struct vote *pvote);
+static void vote_free(struct vote *pvote);
+static struct vote_cast *find_vote_cast(struct vote *pvote, int conn_id);
+static struct vote_cast *vote_cast_new(struct vote *pvote);
+static void remove_vote_cast(struct vote *pvote, struct vote_cast *pvc);
+
+
+/**************************************************************************
+  Initialize voting related data structures.
+**************************************************************************/
+void voting_init(void)
+{
+  if (!vote_list) {
+    vote_list = vote_list_new();
+    vote_number_sequence = 0;
+  }
+}
+
+/**************************************************************************
+  Cannot vote if:
+    * is not connected
+    * access level < info
+    * isn't a living player
+**************************************************************************/
+bool connection_can_vote(struct connection *pconn)
+{
+  if (pconn != NULL && pconn->playing != NULL && !pconn->observer
+      && pconn->playing->is_alive && pconn->access_level >= ALLOW_INFO) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**************************************************************************
+  Returns the total number of users that are allowed to vote.
+**************************************************************************/
+int voting_get_voter_count(void)
+{
+  int num_voters = 0;
+
+  conn_list_iterate(game.est_connections, pconn) {
+    if (connection_can_vote(pconn)) {
+      num_voters++;
+    }
+  } conn_list_iterate_end;
+
+  return num_voters;
+}
+
+/**************************************************************************
+  Check if we satisfy the criteria for resolving a vote, and resolve it
+  if these critera are indeed met. Updates yes and no variables in voting 
+  struct as well.
+
+  Criteria:
+    Accepted immediately if: > 50% of votes for
+    Rejected immediately if: >= 50% of votes against
+**************************************************************************/
+static void check_vote(struct vote *pvote)
+{
+  struct connection *pconn = NULL;
+  int num_cast = 0, num_voters = 0, voting_base;
+  bool resolve = FALSE, passed = FALSE;
+  char cmdline[MAX_LEN_CONSOLE_LINE];
+
+  pvote->yes = 0;
+  pvote->no = 0;
+  pvote->abstain = 0;
+
+  num_voters = voting_get_voter_count();
+
+  vote_cast_list_iterate(pvote->votes_cast, pvc) {
+    if (!(pconn = find_conn_by_id(pvc->conn_id))
+        || !connection_can_vote(pconn)) {
+      continue;
+    }
+    num_cast++;
+
+    switch (pvc->vote_cast) {
+    case VOTE_YES:
+      pvote->yes++;
+      break;
+    case VOTE_NO:
+      pvote->no++;
+      break;
+    case VOTE_ABSTAIN:
+      pvote->abstain++;
+      break;
+    default:
+      break;
+    }
+  } vote_cast_list_iterate_end;
+
+  voting_base = num_voters - pvote->abstain;
+
+  /* Check if we should resolve the vote */
+  if ((voting_base > 0 && (pvote->yes > voting_base / 2
+                          || pvote->no >= (voting_base + 1) / 2))
+      || num_cast >= num_voters) {
+    /* Yep, resolve this one */
+    resolve = TRUE;
+  }
+
+  if (!resolve) {
+    return;
+  }
+
+  if (pvote->yes > voting_base / 2) {
+    passed = TRUE;
+  }
+
+  if (passed) {
+    notify_conn(NULL, NULL, E_SETTING,
+                _("Vote \"%s\" is passed %d to %d with %d "
+                  "abstentions and %d that did not vote."),
+                pvote->command, pvote->yes, pvote->no,
+                pvote->abstain, num_voters - num_cast);
+    sz_strlcpy(cmdline, pvote->command);
+  } else {
+    notify_conn(NULL, NULL, E_SETTING,
+                _("Vote \"%s\" failed with %d against, %d for, "	  
+                  "%d abstentions, and %d that did not vote."),
+                pvote->command, pvote->no, pvote->yes, 
+                pvote->abstain, num_voters - num_cast);
+  }
+
+  remove_vote(pvote);
+
+  if (passed) {
+    handle_stdin_input(NULL, cmdline, FALSE);
+  }
+}
+
+/**************************************************************************
+  Remove the given vote.
+**************************************************************************/
+static void remove_vote(struct vote *pvote)
+{
+  if (!pvote) {
+    return;
+  }
+
+  if (vote_list) {
+    vote_list_unlink(vote_list, pvote);
+  }
+
+  vote_free(pvote);
+}
+
+/**************************************************************************
+  Free the memory used by the vote structure.
+**************************************************************************/
+static void vote_free(struct vote *pvote)
+{
+  if (!pvote) {
+    return;
+  }
+
+  if (pvote->votes_cast) {
+    vote_cast_list_iterate(pvote->votes_cast, pvc) {
+      free(pvc);
+    } vote_cast_list_iterate_end;
+    vote_cast_list_free(pvote->votes_cast);
+    pvote->votes_cast = NULL;
+  }
+  free(pvote);
+}
+
+/**************************************************************************
+  Check votes at end of turn.
+**************************************************************************/
+void voting_turn(void)
+{
+  vote_list_iterate(vote_list, pvote) {
+    check_vote(pvote);
+  } vote_list_iterate_end;
+}
+
+/**************************************************************************
+  Free vote related data structures.
+**************************************************************************/
+void voting_free(void)
+{
+  clear_all_votes();
+  vote_list_free(vote_list);
+  vote_list = NULL;
+}
+
+/**************************************************************************
+  Remove all votes.
+**************************************************************************/
+void clear_all_votes(void)
+{
+  if (!vote_list) {
+    return;
+  }
+
+  vote_list_iterate(vote_list, pvote) {
+    vote_free(pvote);
+  } vote_list_iterate_end;
+  vote_list_clear(vote_list);
+}
+
+/**************************************************************************
+  Returns the vote associated to the given vote number.
+**************************************************************************/
+struct vote *voting_get_vote_by_no(int vote_no)
+{
+  if (!vote_list) {
+    return NULL;
+  }
+
+  vote_list_iterate(vote_list, pvote) {
+    if (pvote->vote_no == vote_no) {
+      return pvote;
+    }
+  } vote_list_iterate_end;
+
+  return NULL;
+}
+
+/**************************************************************************
+  Find the vote made by the given user.
+**************************************************************************/
+struct vote *voting_get_vote_by_caller(const struct connection *caller)
+{
+  if (vote_list == NULL || caller == NULL) {
+    return NULL;
+  }
+
+  vote_list_iterate(vote_list, pvote) {
+    if (pvote->caller_id == caller->id) {
+      return pvote;
+    }
+  } vote_list_iterate_end;
+
+  return NULL;
+}
+
+/**************************************************************************
+  Register a vote of type 'type' on the vote 'pvote' made by the user
+  'pconn'.
+**************************************************************************/
+void connection_vote(struct connection *pconn,
+                     struct vote *pvote,
+                     enum vote_type type)
+{
+  assert(vote_list != NULL);
+
+  struct vote_cast *pvc;
+
+  if (!connection_can_vote(pconn)) {
+    return;
+  }
+
+  /* Try to find a previous vote */
+  if ((pvc = find_vote_cast(pvote, pconn->id))) {
+    pvc->vote_cast = type;
+  } else if ((pvc = vote_cast_new(pvote))) {
+    pvc->vote_cast = type;
+    pvc->conn_id = pconn->id;
+  } else {
+    /* Must never happen */
+    assert(0);
+  }
+  check_vote(pvote);
+}
+
+/**************************************************************************
+  Cancel the votes of a lost or a detached connection...
+**************************************************************************/
+void cancel_connection_votes(struct connection *pconn)
+{
+  if (!pconn || !vote_list) {
+    return;
+  }
+
+  remove_vote(voting_get_vote_by_caller(pconn));
+
+  vote_list_iterate(vote_list, pvote) {
+    remove_vote_cast(pvote, find_vote_cast(pvote, pconn->id));
+  } vote_list_iterate_end;
+}
+
+/**************************************************************************
+  Create a new vote.
+**************************************************************************/
+struct vote *vote_new(struct connection *caller,
+                             const char *full_command)
+{
+  struct vote *pvote;
+
+  assert(vote_list != NULL);
+
+  if (!connection_can_vote(caller)) {
+    return NULL;
+  }
+
+  /* Cancel previous vote */
+  remove_vote(voting_get_vote_by_caller(caller));
+
+  /* Make a new vote */
+  pvote = fc_calloc(1, sizeof(*pvote));
+  pvote->caller_id = caller->id;
+
+  sz_strlcpy(pvote->command, full_command);
+
+  pvote->votes_cast = vote_cast_list_new();
+  pvote->vote_no = ++vote_number_sequence;
+
+  vote_list_append(vote_list, pvote);
+
+  return pvote;
+}
+
+/**************************************************************************
+  Find the vote cast for the user id conn_id in a vote.
+**************************************************************************/
+static struct vote_cast *find_vote_cast(struct vote *pvote, int conn_id)
+{
+  assert(vote_list != NULL);
+
+  vote_cast_list_iterate(pvote->votes_cast, pvc) {
+    if (pvc->conn_id == conn_id) {
+      return pvc;
+    }
+  } vote_cast_list_iterate_end;
+
+  return NULL;
+}
+/**************************************************************************
+  Return a new vote cast.
+**************************************************************************/
+static struct vote_cast *vote_cast_new(struct vote *pvote)
+{
+  assert(vote_list != NULL);
+
+  struct vote_cast *pvc = fc_malloc(sizeof(struct vote_cast));
+
+  pvc->conn_id = -1;
+  pvc->vote_cast = VOTE_ABSTAIN;
+
+  vote_cast_list_append(pvote->votes_cast, pvc);
+
+  return pvc;
+}
+
+/**************************************************************************
+  Remove a vote cast.
+**************************************************************************/
+static void remove_vote_cast(struct vote *pvote, struct vote_cast *pvc)
+{
+  assert(vote_list != NULL);
+
+  if (!pvc) {
+    return;
+  }
+
+  vote_cast_list_unlink(pvote->votes_cast, pvc);
+  free(pvc);
+  check_vote(pvote);            /* Maybe can pass */
+}
+
+/**************************************************************************
+  Return the number of currently active votes.
+**************************************************************************/
+int voting_get_vote_count(void)
+{
+  if (!vote_list) {
+    return 0;
+  }
+  return vote_list_size(vote_list);
+}
+
+/**************************************************************************
+  Return the command line that will be executed if this vote passes.
+**************************************************************************/
+const char *vote_cmdline(const struct vote *pvote)
+{
+  if (!pvote) {
+    return NULL;
+  }
+  return pvote->command;
+}
+
+/**************************************************************************
+  Return the number of votes cast of the given type.
+**************************************************************************/
+int vote_cast_count(const struct vote *pvote, enum vote_type vt)
+{
+  if (!pvote) {
+    return 0;
+  }
+  switch (vt) {
+  case VOTE_YES:
+    return pvote->yes;
+    break;
+  case VOTE_NO:
+    return pvote->no;
+    break;
+  case VOTE_ABSTAIN:
+    return pvote->abstain;
+    break;
+  default:
+    break;
+  }
+  return 0;
+}
+
+/**************************************************************************
+  Return the unique identifier number for this vote.
+**************************************************************************/
+int vote_number(const struct vote *pvote)
+{
+  return pvote->vote_no;
+}
+
+/**************************************************************************
+  Return a const pointer to the global vote list.
+**************************************************************************/
+const struct vote_list *voting_get_vote_list(void)
+{
+  return vote_list;
+}
+
+/**************************************************************************
+  Returns the last vote that was made, or NULL if none. We use the last
+  vote according to 'vote_number_sequence' rather than the last vote in
+  'vote_list' so that users do not mistakenly vote for the wrong vote if
+  the list is changed while they are voting.
+**************************************************************************/
+struct vote *voting_get_last_vote(void)
+{
+  return voting_get_vote_by_no(vote_number_sequence);
+}
diff --git a/server/voting.h b/server/voting.h
new file mode 100644
index 0000000..8a75299
--- /dev/null
+++ b/server/voting.h
@@ -0,0 +1,58 @@
+/********************************************************************** 
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   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__VOTE_H
+#define FC__VOTE_H
+
+enum vote_type {
+  VOTE_ABSTAIN = 0,
+  VOTE_YES,
+  VOTE_NO,
+
+  NUM_VOTE_TYPES
+};
+
+struct connection;
+struct vote;
+
+struct vote *vote_new(struct connection *caller,
+                      const char *full_command);
+int vote_number(const struct vote *pvote);
+int vote_cast_count(const struct vote *pvote, enum vote_type vt);
+const char *vote_cmdline(const struct vote *pvote);
+
+#define SPECLIST_TAG vote
+#define SPECLIST_TYPE struct vote
+#include "speclist.h"
+#define vote_list_iterate(alist, pvote) \
+      TYPED_LIST_ITERATE(struct vote, alist, pvote)
+#define vote_list_iterate_end  LIST_ITERATE_END
+
+void voting_init(void);
+void voting_turn(void);
+void voting_free(void);
+int voting_get_voter_count(void);
+int voting_get_vote_count(void);
+struct vote *voting_get_last_vote(void);
+struct vote *voting_get_vote_by_no(int vote_no);
+struct vote *voting_get_vote_by_caller(const struct connection *caller);
+const struct vote_list *voting_get_vote_list(void);
+
+bool connection_can_vote(struct connection *pconn);
+void connection_vote(struct connection *pconn, struct vote *pvote,
+                     enum vote_type type);
+
+void clear_all_votes(void);
+void cancel_connection_votes(struct connection *pconn);
+
+
+#endif /* FC__VOTE_H */
_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to