diff src/findslot.c @ 4:aa38447a4b21

First entry of Paradise Server 2.9 patch 10 Beta
author darius
date Sat, 06 Dec 1997 04:37:03 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/findslot.c	Sat Dec 06 04:37:03 1997 +0000
@@ -0,0 +1,491 @@
+/*--------------------------------------------------------------------------
+NETREK II -- Paradise
+
+Permission to use, copy, modify, and distribute this software and its
+documentation, or any derivative works thereof, for any NON-COMMERCIAL
+purpose and without fee is hereby granted, provided that this copyright
+notice appear in all copies.  No representations are made about the
+suitability of this software for any purpose.  This software is provided
+"as is" without express or implied warranty.
+
+    Xtrek Copyright 1986                            Chris Guthrie
+    Netrek (Xtrek II) Copyright 1989                Kevin P. Smith
+                                                    Scott Silvey
+    Paradise II (Netrek II) Copyright 1993          Larry Denys
+                                                    Kurt Olsen
+                                                    Brandon Gillespie
+--------------------------------------------------------------------------*/
+
+
+#include "config.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <errno.h>
+#include <pwd.h>
+#include <string.h>
+#include <ctype.h>
+#include "defs.h"
+#include "struct.h"
+#include "data.h"
+#include "packets.h"
+#include "shmem.h"
+
+int grabslot( /* int */ );
+void mapWaitCount( /* int */ );
+
+
+extern int isClientDead();
+extern unsigned int sleep();
+extern int sendQueuePacket();
+extern int flushSockBuf();
+
+int
+findslot(overload, homeaway)
+  int overload;
+  enum HomeAway homeaway;
+{
+  int i;
+
+  i = grabslot(overload, homeaway);
+  players[i].p_pos = -1;
+  memset(&players[i].p_stats, 0, sizeof(struct stats));
+  players[i].p_stats.st_tticks = 1;
+#if 0
+  for (j = 0; j < 95; j++)
+  {
+    players[i].p_stats.st_keymap[j] = j + 32;
+  }
+  players[i].p_stats.st_keymap[95] = 0;
+#endif
+  players[i].p_stats.st_flags = ST_INITIAL;
+  return (i);
+}
+
+static int 
+allocate_slot(homeaway, overload, allocate)
+  enum HomeAway homeaway;
+  int overload;
+  int allocate;
+{
+  int i;
+  if (overload)
+  {
+    for (i = MAXPLAYER - 1; i >= 0; i--)
+    {
+      if (players[i].p_status == PFREE)
+      {
+	if (allocate)
+	{
+	  players[i].p_status = POUTFIT;
+	  players[i].p_team = NOBODY;
+	}
+	return i;
+      }
+    }
+  }
+  else
+  {
+#ifdef LEAGUE_SUPPORT
+    if (status2->league)
+    {
+      /*
+       * for league play, make sure one team doesn't crowd out the other.
+       */
+      int count = 0;
+      for (i = 0; i < MAXPLAYER; i++)
+      {
+	if (players[i].p_status == PFREE)
+	  continue;
+	if (players[i].p_homeaway == homeaway)
+	  count++;
+      }
+      if (count * 2 >= MAXPLAYER - configvals->ntesters)
+	return -1;		/* our team is full */
+    }
+#endif
+    for (i = 0; i < MAXPLAYER - configvals->ntesters; i++)
+    {
+      if (players[i].p_status == PFREE)
+      {
+	if (allocate)
+	{
+	  players[i].p_status = POUTFIT;
+	  players[i].p_team = NOBODY;
+	}
+	return i;
+      }
+    }
+  }
+  return -1;
+}
+#if 0
+static int 
+ImAllowed(slotnum, homeaway)
+  int slotnum;
+  enum HomeAway homeaway;
+{
+#ifdef LEAGUE_SUPPORT
+  int half;
+  if (!status2->league)
+    return 1;
+
+  half = (MAXPLAYER - configvals->ntesters) / 2;
+  return (homeaway == AWAY) ? (slotnum < half) :
+    (slotnum >= half);
+#else
+  return 1;
+#endif
+}
+#endif
+/*
+ * The following code for grabslot() is really bizarre, and needs an
+ * explaination.
+ * 
+ * Basically, the queue works like this:  Each process that needs to wait takes
+ * a number, and when that number comes up, the process enters the game.
+ * status->count is the next number to take for a new process, and
+ * status->wait is the current process being served.
+ * 
+ * However, this is not enough to determine exactly how many people are waiting,
+ * because people may drop out.  So, 3 more variables are added,
+ * status->request posts a request of some sort to all of the other
+ * processes, and status->number is the process posting the request. Also,
+ * status->answer is available for any kind of response needed from the other
+ * processes.  (Needless to say, only one process can make a request at any
+ * one time).
+ * 
+ * Every process will wait for a second (sleep(1)), and then check the queue
+ * status, do what is appropriate, and repeat.
+ * 
+ * Above and beyond this, processes may die, and not report it (for whatever
+ * reason, and every process waiting on the queue watches the behavior of
+ * every other supposed process to make sure it is still alive).
+ * 
+ * When a space opens up, and the person who holds the number currently being
+ * served does not respond, then the processes argue over who deserves to get
+ * in next.  The first process to decide that no one is going to take the
+ * free slot says that his number is the next on to be served. He waits for
+ * anyone to disagree with him.  Any other processes which notice that their
+ * number is lower than the number being served claim that their number is
+ * the next one to be served.  They also wait for anyone to disagree with
+ * them.  Eventually, everyone is done waiting, and the process with the
+ * lowest count will be served.
+ * 
+ * Variables: status->wait:  	Number being served. status->count:	Next
+ * number to take.
+ * 
+ * status->request:	Process request. (status->number) (status->answer)
+ * REQFREE:	No requests pending. REQWHO:		How many people are
+ * before me?  In this case, every process in from of this process (whose
+ * position is recorded in status->number) increments status->answer.
+ * REQDEAD:	I am leaving the queue (Either to quit, or enter game).  Any
+ * process whose position is higher than this process will note that they are
+ * closer to the top of the queue.
+ * 
+ * (Local) waitWin:	The window. qwin:	The quit half of waitWin.
+ * countWin:	The count half of waitWin. count:	My number (Position
+ * in queue). pseudocount: Number of people in front of me (-1 means I don't
+ * know). myRequest:	This keeps track of requests I've made.  If it is non
+ * zero, then this is the number of times I will leave it there before I get
+ * my answer (if there is one), and reset status->request. idie:	This
+ * is set to one if I need to leave the queue.  When I get a chance, I will
+ * submit my request, and leave. wearein:	This is the slot we grabbed
+ * to enter the game.  If it is -1, then we haven't got a slot yet.  If we
+ * can grab a slot, then wearein is set to the slot #, and idie is set to 1.
+ * 
+ * Because we need to monitor other processes, the following variables also
+ * exist:
+ * 
+ * oldcount:	The number that was being served last time I looked. waits:
+ * Number of times I've seen oldcount as the number being served. If a
+ * position opens in the game, and no one takes it for a while, we assume
+ * that someone died. lastRequest:	The last request I have seen.
+ * lastNumber:	The process making this request. reqCount:	Number of
+ * times I've seen this request.  If I see this request 9 times, I assume it
+ * is obsolete, and I reset it.
+ */
+
+int
+grabslot(overload, homeaway)
+  int overload;			/* Indicates a request for a tester's slot. */
+  enum HomeAway homeaway;
+{
+  int count;			/* My number */
+  int oldcount;			/* Number that was being served last check */
+  int i;
+  int waits;			/* # times I have waited for someone else to
+				 * act */
+  int oldwait;			/* Number being served last check */
+  int pseudocount = -1;		/* Count on queue for sake of person waiting */
+  int myRequest = 0;		/* To keep track of any request I'm making */
+  int lastRequest = 0;		/* Last request I've seen */
+  int lastNumber = 0;		/* Last person making request */
+  int reqCount = 0;		/* Number of times I've seen this */
+  int idie = 0;			/* Do I want to die? */
+  int wearein = -1;		/* Slot we got in the game */
+  int rep = 0;
+
+  /* If other players waiting, we get in line behind them */
+  if (!overload && status->wait != status->count)
+  {
+    count = status->count++;
+  }
+  else
+  {
+    /* Get in game if posible */
+#if 1
+    i = allocate_slot(homeaway, overload, 1);
+    if (i >= 0)
+      return i;
+#else
+    if (overload)
+      for (i = MAXPLAYER - 1; i >= 0; i--)
+      {
+	if (players[i].p_status == PFREE)
+	{			/* We have a free slot */
+	  players[i].p_status = POUTFIT;	/* possible race code */
+	  players[i].p_team = NOBODY;
+	  return (i);
+	}
+      }
+    else
+      for (i = 0; i < MAXPLAYER - configvals->ntesters; i++)
+      {
+	if (ImAllowed(i, homeaway)
+	    && players[i].p_status == PFREE)
+	{			/* We have a free slot */
+	  players[i].p_status = POUTFIT;	/* possible race code */
+	  players[i].p_team = NOBODY;
+	  return (i);
+	}
+      }
+#endif
+    /* Game full.  We will wait. */
+    count = status->count++;
+  }
+  waits = 0;
+  oldwait = -1;
+  oldcount = status->wait;
+
+  /* For count = 0,1,2  I know that it is right */
+  if (count - status->wait < 1)
+  {
+    pseudocount = count - status->wait;
+  }
+  for (;;)
+  {
+    /* Send packets occasionally to see if he is accepting... */
+    if (rep++ % 10 == 0)
+    {
+      mapWaitCount(pseudocount);
+    }
+    if (isClientDead())
+    {
+      if (count == status->count - 1)
+      {
+	status->count--;
+	exit(0);
+      }
+      /* If we are at top, decrease it */
+      if (count == status->wait)
+      {
+	status->wait++;
+      }
+      idie = 1;
+      /* break;		warning, function has return e and return */
+      exit(0);
+    }
+    if (status->wait != oldcount)
+    {
+      mapWaitCount(pseudocount);
+      oldcount = status->wait;
+    }
+    /* To mimize process overhead and aid synchronization */
+    sleep(1);
+    /* Message from daemon that it died */
+    if (status->count == 0)
+      exit(0);
+    /* I have a completed request? */
+    if (myRequest != 0 && --myRequest == 0)
+    {
+      if (idie && status->request == REQDEAD)
+      {
+	status->request = REQFREE;
+	/* Out of queue, into game */
+	if (wearein != -1)
+	{
+	  return (wearein);
+	}
+	exit(0);
+      }
+      pseudocount = status->answer;
+      status->request = REQFREE;
+      if (pseudocount > 18)
+	idie = 1;
+      mapWaitCount(pseudocount);
+    }
+    /* Tell the world I am going bye bye */
+    if (idie && status->request == REQFREE)
+    {
+      status->request = REQDEAD;
+      status->number = count;
+      myRequest = 4;
+    }
+    /* Should I request a count for # of people waiting? */
+    if (pseudocount == -1 && status->request == REQFREE)
+    {
+      status->request = REQWHO;
+      status->number = count;
+      status->answer = 0;
+      myRequest = 4;
+      /* I give people 4 seconds to respond */
+    }
+    /* Is someone else making a request? */
+    if (status->request != REQFREE && myRequest == 0)
+    {
+      if (status->request == lastRequest &&
+	  status->number == lastNumber)
+      {
+	reqCount++;
+	/* 9 occurances of the same request implies that the process */
+	/* died.  I will reset request. */
+	if (reqCount > 8)
+	{
+	  status->request = REQFREE;
+	}
+      }
+      else
+      {
+	lastRequest = status->request;
+	lastNumber = status->number;
+	reqCount = 1;
+	if (lastRequest == REQWHO)
+	{
+	  if (count < lastNumber)
+	  {
+	    status->answer++;
+	  }
+	}
+	else if (lastRequest == REQDEAD)
+	{
+	  if (count > lastNumber && pseudocount != -1)
+	  {
+	    pseudocount--;
+	    mapWaitCount(pseudocount);
+	  }
+	}
+      }
+    }
+    /* If someone raised wait too high, I claim that I * am next in line */
+    if (status->wait > count && !idie)
+    {
+      status->wait = count;
+      /* Give people a chance to correct me */
+      sleep(2);
+    }
+#if 1
+    if (idie)
+      continue;
+    if (count == status->wait)
+    {
+      i = allocate_slot(homeaway, overload, 1);
+      if (i < 0)
+	continue;
+      status->wait++;
+      wearein = i;
+      idie = 1;
+    }
+    else
+    {
+      i = allocate_slot(homeaway, overload, 0);
+      if (i < 0)
+	continue;
+      if (oldwait == status->wait)
+      {
+	waits++;
+      }
+      else
+      {
+	oldwait = status->wait;
+	waits = 1;
+      }
+      /* If this is our fifth wait (5 sec), then something is */
+      /* wrong.  We assume someone died, and fix this problem */
+      if (waits == 5 && !idie)
+      {
+	/* I want to be next in line, so I say so. */
+	status->wait = count;
+	/* And I allow someone to correct me if I'm wrong */
+	sleep(2);
+	waits = 0;
+      }
+
+    }
+#else
+    for (i = 0; i < MAXPLAYER - configvals->ntesters; i++)
+    {
+      /* If we want to die anyway, we have no right looking for */
+      /* a free slot */
+      if (idie)
+	break;
+      if (ImAllowed(i, homeaway) && players[i].p_status == PFREE)
+      {
+	/* If I am next in line... */
+	if (count == status->wait)
+	{
+	  players[i].p_status = POUTFIT;
+	  players[i].p_team = NOBODY;
+	  /* Increase count for next player */
+	  status->wait++;
+	  /* I should check idie, but maybe he wants in, eh? */
+	  wearein = i;
+	  idie = 1;
+	  break;
+	}
+	else
+	{
+	  if (oldwait == status->wait)
+	  {
+	    waits++;
+	  }
+	  else
+	  {
+	    oldwait = status->wait;
+	    waits = 1;
+	  }
+	  /* If this is our fifth wait (5 sec), then something is */
+	  /* wrong.  We assume someone died, and fix this problem */
+	  if (waits == 5 && !idie)
+	  {
+	    /* I want to be next in line, so I say so. */
+	    status->wait = count;
+	    /* And I allow someone to correct me if I'm wrong */
+	    sleep(2);
+	    waits = 0;
+	  }
+	  break;
+	}
+      }
+    }
+#endif
+    /* this location is skipped if we didn't find a slot */
+  }
+}
+
+void
+mapWaitCount(count)
+  unsigned int count;
+{
+  if (count == -1)
+    return;
+
+  sendQueuePacket((short) count);
+  blk_flag = 1;
+  updateMOTD();
+  blk_flag = 0;
+
+  undeferDeferred();		/* send the MOTD through the TCP buffers */
+  flushSockBuf();
+}