[Commit] rrserver clients.5c,NONE,1.1 send.5c,NONE,1.1 dispatch.5c,1.4,1.5 games.5c,1.2,1.3 lex.5c,1.2,1.3 net.5c,1.1,1.2 readreq.5c,1.2,1.3 server.5c,1.2,1.3 connect.5c,1.2,NONE

Keith Packard commit@keithp.com
Thu, 29 May 2003 10:46:31 -0700


Committed by: keithp

Update of /local/src/CVS/rrserver
In directory home.keithp.com:/tmp/cvs-serv22376

Modified Files:
	dispatch.5c games.5c lex.5c net.5c readreq.5c server.5c 
Added Files:
	clients.5c send.5c 
Removed Files:
	connect.5c 
Log Message:
Rename Connect namespace to Clients
Use exceptions to manage dispatch errors
Add global lock
Add broadcast/respond functions to simplify responses
Clean up newline handling


--- NEW FILE: clients.5c ---
(This appears to be a binary file; contents omitted.)

--- NEW FILE: send.5c ---
(This appears to be a binary file; contents omitted.)

Index: dispatch.5c
===================================================================
RCS file: /local/src/CVS/rrserver/dispatch.5c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- dispatch.5c	29 May 2003 08:32:19 -0000	1.4
+++ dispatch.5c	29 May 2003 17:46:29 -0000	1.5
@@ -23,8 +23,9 @@
  */
 
 autoload RR
+autoload RR::Send
 autoload Server
-autoload Server::Connect
+autoload Server::Clients
 autoload Server::Readreq
 autoload Server::Show
 autoload Server::Games
@@ -35,20 +36,40 @@
 
 	public void set_server_id (string id) { server_id = id; }
 
-	public void client (file f) {
-	    &Client	c = Connect::new (f);
+	void client_locked (file f) {
+	    &Client	c = Clients::new (f);
 	    
-	    void	helo (string username) {
-		c.user.username = username;
-		File::fprintf (f, "HELO \"%s\"\n", server_id);
+	    exception	notactive ();
+	    exception	notingame ();
+	    exception	invalidname ();
+	    exception	nonameset ();
+
+	    void	broadcast (string fmt, poly args...) {
+		RR::Send::send (stdout, "broadcast: ");
+		RR::Send::send (stdout, fmt, args...);
+		void	message_client (&Client o) {
+		    if (&o != &c)
+		    {
+			RR::Send::send (o.f, fmt, args...);
+			File::flush (o.f);
+		    }
+		}
+		Clients::iterate (message_client);
+		RR::Send::send (stdout, "done\n");
 	    }
 
-	    void print_client (&Client c) {
-		union switch (c.user) {
+	    void	respond (string fmt, poly args...) {
+		RR::Send::send (stdout, "respond: ");
+		RR::Send::send (stdout, fmt, args...);
+		RR::Send::send (f, fmt, args...);
+	    }
+
+	    void print_client (&Client o) {
+		union switch (o.user) {
 		case none:
 		    break;
 		case username u:
-		    File::fprintf (f, " \"%s\"", u);
+		    respond (" %s", u);
 		    break;
 		}
 	    }
@@ -58,55 +79,58 @@
 		case none:
 		    break;
 		case username u:
-		    File::fprintf (f, " \"%s\" %d", u, c.score);
+		    respond (" %s %d", u, c.score);
 		    break;
 		}
 	    }
 	    
+	    void	helo (string username) {
+		if (Clients::find (username) != ClientRef.none)
+		    raise invalidname ();
+		c.user.username = username;
+		respond ("HELO %s\n", server_id);
+		broadcast ("NOTICE USER %s\n", username);
+	    }
+
 	    void	who () {
-		File::fprintf (f, "WHO");
-		Connect::iterate (print_client);
-		File::fprintf (f, "\n");
+		respond ("WHO");
+		Clients::iterate (print_client);
+		respond ("\n");
 	    }
 
 	    void	games() {
-		File::fprintf (f, "GAMES");
+		respond ("GAMES");
 		Games::iterate (void func(&Game g) {
-		    File::fprintf (f, " \"%s\"", g.name);
+		    respond (" %s", g.name);
 		});
-		File::fprintf (f, "\n");
+		respond ("\n");
 	    }
 
 	    void	users(string game) {
-		try {
-		    &Game	g = Games::find (game);
-		    File::fprintf (f, "USERS");
-		    Games::iterate_client (&g, print_client_score);
-		    File::fprintf (f, "\n");
-		} catch Games::no_such_game (string name) {
-		    File::fprintf (f, "ERROR NOGAME\n");
-		}
+		&Game	g = Games::find (game);
+		respond ("USERS");
+		Games::iterate_client (&g, print_client_score);
+		respond ("\n");
 	    }
 
 	    void	join (string game) {
-		try {
-		    &Game g = Games::find (game);
-		    Games::add_client (&g, &c);
-		} catch Games::no_such_game (string game) {
-		    File::fprintf (f, "ERROR NOGAME\n");
-		}
+		&Game g = Games::find (game);
+		Games::add_client (&g, &c);
+		respond ("JOIN\n");
+		broadcast ("NOTICE JOIN %s %s\n", g.name, c.user.username);
 	    }
 	    
 	    void	new(string game) {
-		&Game g = Games::new (game);
-		join (g.name);
-		File::fprintf (f, "NEW \"%s\"\n", g.name);
+		&Game g =Games::new (game);
+		Games::add_client (&g, &c);
+		respond ("NEW %s\n", g.name);
+		broadcast ("NOTICE NEW %s\n", g.name);
 	    }
 
 	    void	show () {
 		union switch (c.game) {
 		case none:
-		    File::fprintf (f, "ERROR NOTINGAME\n");
+		    raise notingame ();
 		    break;
 		case game g:
 		    File::fprintf (f, "SHOW \"\n");
@@ -117,86 +141,73 @@
 	    }
 
 	    void	bid (int number) {
-		File::fprintf (f, "BID\n");
+		respond ("BID\n");
+		broadcast ("BID %s %d\n", c.user.username, number);
 	    }
 
 	    void	move (Color color, Direction direction) {
 		union switch (c.game) {
 		case none:
-		    File::fprintf (f, "ERROR NOTINGAME\n");
-		    break;
+		    raise notingame ();
 		case game g:
-		    try {
-			Games::move (&g, &c, color, direction);
-			File::fprintf (f, "MOVE\n");
-		    } catch Games::notactive (&Game g, &Client c) {
-			File::fprintf (f, "ERROR NOTACTIVE\n");
-		    }
+		    Games::move (&g, &c, color, direction);
+		    respond ("MOVE\n");
 		    break;
 		}
+		broadcast ("NOTICE MOVE %C %D\n", color, direction);
 	    }
 
 	    void	undo () {
 		union switch (c.game) {
 		case none:
-		    File::fprintf (f, "ERROR NOTINGAME\n");
+		    raise notingame ();
 		    break;
 		case game g:
-		    try {
-			Games::undo (&g, &c);
-			File::fprintf (f, "UNDO\n");
-		    } catch Games::notactive (&Game g, &Client c) {
-			File::fprintf (f, "ERROR NOTACTIVE\n");
-		    }
+		    Games::undo (&g, &c);
+		    respond ("UNDO\n");
 		    break;
 		}
+		broadcast ("NOTICE UNDO\n");
 	    }
 
 	    void	reset () {
 		union switch (c.game) {
 		case none:
-		    File::fprintf (f, "ERROR NOTINGAME\n");
+		    raise notingame ();
 		    break;
 		case game g:
-		    try {
-			Games::reset (&g, &c);
-			File::fprintf (f, "RESET\n");
-		    } catch Games::notactive (&Game g, &Client c) {
-			File::fprintf (f, "ERROR NOTACTIVE\n");
-		    }
+		    Games::reset (&g, &c);
+		    respond ("RESET\n");
 		    break;
 		}
+		broadcast ("NOTICE RESET\n");
 	    }
 
 	    void	turn () {
 		union switch (c.game) {
 		case none:
-		    File::fprintf (f, "ERROR NOTINGAME\n");
-		    break;
+		    raise notingame ();
 		case game g:
 		    if (Games::solved (&g))
-			Games::next_target (&g);
-		    File::fprintf (f, "TURN\n");
+			Games::next_turn (&g);
+		    respond ("TURN\n");
 		    break;
 		}
+		broadcast ("NOTICE TURN\n");
 	    }
 
 	    void	pass () {
 	    }
 
 	    void	message (string text) {
-		File::fprintf (f, "MESSAGE\n");
-		Connect::iterate (void func (&Client o) {
-		    string u = c.user == User.none ? "anonymous" :
-		    c.user.username;
-		    File::fprintf (o.f, "NOTICE MESSAGE \"%s\" \"%s\"\n",
-				   u, text);
-		    File::flush (o.f);
-		});
+		respond ("MESSAGE\n");
+		string u = (c.user == User.none ? "anonymous" :
+			    c.user.username);
+		broadcast ("NOTICE MESSAGE %s %s\n", u, text);
 	    }
 
 	    void	quit () {
-		File::fprintf (f, "QUIT\n");
+		respond ("QUIT\n");
 		raise Readreq::request_closed();
 	    }
 
@@ -207,6 +218,15 @@
 		    File::flush (f);
 		    Request	r = Readreq::read (f);
 		    union switch (r) {
+		    case HELO:
+		    case QUIT:
+			 break;
+		    default:
+			if (c.user == User.none)
+			    raise nonameset ();
+			break;
+		    }
+		    union switch (r) {
 		    case HELO h:    
 			helo (h.username);
 			break;
@@ -257,26 +277,41 @@
 			break;
 		    }
 		} catch Readreq::invalid_request (string w) {
-		    File::fprintf (f, "ERROR COMMAND %s\n", w);
-		} catch Readreq::request_closed () {
-		    printf ("Client closed\n");
-		    Connect::dispose (&c);
-		    File::close (f);
-		    return;
+		    respond ("ERROR COMMAND %s\n", w);
 		} catch RR::Lex::syntax() {
-		    File::fprintf (f, "ERROR SYNTAX\n");
-		    RR::Lex::skipline (f);
+		    respond ("ERROR SYNTAX\n");
 		} catch RR::Lex::invalid_color (string color) {
-		    File::fprintf (f, "ERROR NOTCOLOR %s\n", color);
-		    RR::Lex::skipline (f);
+		    respond ("ERROR NOTCOLOR %s\n", color);
 		} catch RR::Lex::invalid_shape (string shape) {
-		    File::fprintf (f, "ERROR NOTSHAPE %s\n", shape);
-		    RR::Lex::skipline (f);
+		    respond ("ERROR NOTSHAPE %s\n", shape);
 		} catch RR::Lex::invalid_direction (string direction) {
-		    File::fprintf (f, "ERROR NOTDIRECTION %s\n", direction);
-		    RR::Lex::skipline (f);
+		    respond ("ERROR NOTDIRECTION %s\n", direction);
+		} catch Games::notactive (&Game g, &Client c) {
+		    respond ("ERROR NOTACTIVE\n");
+		} catch notingame () {
+		    respond ("ERROR NOTINGAME\n");
+		} catch Games::no_such_game (string name) {
+		    respond ("ERROR NOGAME\n");
+		} catch nonameset () {
+		    respond ("ERROR NONAMESET\n");
+		} catch invalidname () {
+		    respond ("ERROR INVALIDNAME\n");
+		} catch Readreq::request_closed () {
+		    User user = c.user;
+		    printf ("Client closed %v\n", c.user);
+		    Clients::dispose (&c);
+		    File::close (f);
+		    if (c.user != User.none)
+			broadcast ("NOTICE PART %s\n", c.user.username);
+		    return;
 		}
 	    }
+	}
+
+	public void client (file f)
+	{
+	    twixt (lock (); unlock ())
+		client_locked (f);
 	}
     }
 }

Index: games.5c
===================================================================
RCS file: /local/src/CVS/rrserver/games.5c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- games.5c	29 May 2003 08:15:44 -0000	1.2
+++ games.5c	29 May 2003 17:46:29 -0000	1.3
@@ -37,7 +37,7 @@
 	void remove (&Game g) {
 	    Array::remove (&games, &g);
 	}
-
+	
 	public void iterate (void (&Game g) f) {
 	    Array::iterate (&games, f);
 	}
@@ -92,7 +92,7 @@
 	    return t;
 	}
 	    
-	public void next_target (&Game g) {
+	void next_target (&Game g) {
 	    Target  t = g.targets[0];
 	    g.targets = (Target[dim(g.targets)-1]) { [i] = g.targets[i+1] };
 	    g.history = (ObjectLoc[*]) {};
@@ -100,6 +100,10 @@
 	    Boards::set_target (&g.board, t.color, t.shape);
 	}
 
+	public void next_turn (&Game g) {
+	    next_target (&g);
+	}
+
 	public &Game new (string suggestion) {
 	    string name;
 	    for (int n = 0; 
@@ -119,7 +123,6 @@
 
 	public void remove_client (&Game g, &Client c) {
 	    Array::remove (&g.clients, &c);
-	    c.score = 0;
 	}
 
 	public &Client add_client (&Game g, &Client c) {
@@ -142,6 +145,8 @@
 	public exception notactive (&Game g, &Client c);
 	
 	public bool undo (&Game g, &Client c) {
+	    if (g.active != (ClientRef.client) (&c))
+		raise notactive (&g, &c);
 	    try {
 		ObjectLoc ol = Array::pop (&g.history);
 		Boards::position_robot (&g.board, ol.object.robot.robot.color,
@@ -153,8 +158,6 @@
 	}
 
 	public void reset (&Game g, &Client c) {
-	    if (g.active != (ClientRef.client) (&c))
-		raise notactive (&g, &c);
 	    while (dim (g.history) > 0)
 		undo (&g, &c);
 	}

Index: lex.5c
===================================================================
RCS file: /local/src/CVS/rrserver/lex.5c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- lex.5c	29 May 2003 08:15:44 -0000	1.2
+++ lex.5c	29 May 2003 17:46:29 -0000	1.3
@@ -44,15 +44,18 @@
 	    return -1;
 	}
 
-	void skipwhite (file f) {
+	bool skipwhite (file f, bool allow_newline) {
 	    while ((int c = lexc (f)) != -1) {
-		if (c == '\n')
-		    raise syntax ();
+		if (c == '\n' && !allow_newline) {
+		    File::ungetc (c, f);
+		    return true;
+		}
 		if (!Ctype::isspace(c)) {
 		    File::ungetc (c, f);
-		    break;
+		    return false;
 		}
 	    }
+	    return true;
 	}
 
 	public void skipline (file f) {
@@ -94,24 +97,8 @@
 	    return s;
 	}
 
-	public string word (file f) {
-	    skipwhite (f);
-	    return next_word (f);
-	}
-
-	public string firstword (file f) {
-	    for (;;) {
-		try {
-		    skipwhite (f);
-		    return next_word (f);
-		} catch syntax () {
-		    ;
-		}
-	    }
-	}
-
 	public bool eol (file f) {
-	    skipwhite (f);
+	    skipwhite (f, false);
 	    int c = lexc (f);
 	    if (c == -1)
 		return true;
@@ -119,6 +106,17 @@
 	    if (c == '\n')
 		return true;
 	    return false;
+	}
+
+	public string word (file f) {
+	    if (skipwhite (f, false))
+		raise syntax ();
+	    return next_word (f);
+	}
+
+	public string firstword (file f) {
+    	    skipwhite (f, true);
+    	    return next_word (f);
 	}
 
 	public int number (file f) {

Index: net.5c
===================================================================
RCS file: /local/src/CVS/rrserver/net.5c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- net.5c	29 May 2003 06:45:37 -0000	1.1
+++ net.5c	29 May 2003 17:46:29 -0000	1.2
@@ -23,7 +23,6 @@
  */
  
 autoload RR
-autoload Server::Connect
 autoload Server::Readreq
 
 extend namespace Server {

Index: readreq.5c
===================================================================
RCS file: /local/src/CVS/rrserver/readreq.5c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- readreq.5c	29 May 2003 06:45:37 -0000	1.2
+++ readreq.5c	29 May 2003 17:46:29 -0000	1.3
@@ -24,6 +24,7 @@
 
 autoload RR::Lex
 autoload Ctype
+autoload Server
 
 extend namespace Server {
     public namespace Readreq {
@@ -41,7 +42,7 @@
 	    return r;
 	}
 
-	public Request read (file f) {
+	Request read_unlocked (file f) {
 	    struct {} packet;
 	    void username_ (&struct { string username; } packet) {
 		packet.username = word (f); 
@@ -64,62 +65,88 @@
 	    void text_ (&struct { string text; } packet) {
 		packet.text = word (f);
 	    }
+	    Request req;
 	    switch (string w = upper (firstword (f))) {
 	    case "HELO":
 		packet = (struct { string username; }) {};
 		username_ (&packet);
-		return (Request.HELO) packet;
+		req = (Request.HELO) packet;
+		break;
 	    case "WHO":
-		return Request.WHO;
+		req = Request.WHO;
+		break;
 	    case "GAMES":
-		return Request.GAMES;
+		req = Request.GAMES;
+		break;
 	    case "USERS":
 		packet = (struct { string game; }) {};
 		game_ (&packet);
-		return (Request.USERS) packet;
+		req = (Request.USERS) packet;
+		break;
 	    case "NEW":
 		packet = (struct { string game; }) {};
 		game_ (&packet);
-		return (Request.NEW) packet;
+		req = (Request.NEW) packet;
+		break;
 	    case "JOIN":
 		packet = (struct { string game; }) {};
 		game_ (&packet);
-		return (Request.JOIN) packet;
+		req = (Request.JOIN) packet;
+		break;
 	    case "WATCH":
 		packet = (struct { string game; }) {};
 		game_ (&packet);
-		return (Request.WATCH) packet;
+		req = (Request.WATCH) packet;
+		break;
 	    case "SHOW":
-		return Request.SHOW;
+		req = Request.SHOW;
+		break;
 	    case "BID":
 		packet = (struct { int number; }) {};
 		number_ (&packet);
-		return (Request.BID) packet;
+		req = (Request.BID) packet;
+		break;
 	    case "MOVE":
 		packet = (struct { Color color; Direction direction; }) {};
 		color_ (&packet);
 		direction_ (&packet);
-		return (Request.MOVE) packet;
+		req = (Request.MOVE) packet;
+		break;
 	    case "UNDO":
-		return Request.UNDO;
+		req = Request.UNDO;
+		break;
 	    case "RESET":
-		return Request.RESET;
+		req = Request.RESET;
+		break;
 	    case "TURN":
-		return Request.TURN;
+		req = Request.TURN;
+		break;
 	    case "PASS":
-		return Request.PASS;
+		req = Request.PASS;
+		break;
 	    case "MESSAGE":
 		packet = (struct { string text; }) {};
 		text_ (&packet);
-		return (Request.MESSAGE) packet;
+		req = (Request.MESSAGE) packet;
+		break;
 	    case "QUIT":
-		return Request.QUIT;
+		req = Request.QUIT;
+		break;
 	    case "":
 		raise request_closed ();
 	    default:
-		skipline (f);
 		raise invalid_request (w);
 	    }
+	    if (!eol (f))
+		raise syntax ();
+	    return req;
+	}
+
+	public Request read (file f) {
+	    twixt (unlock (); lock ())
+		twixt (true; skipline (f))
+		    return read_unlocked (f);
+	    raise notreached ();
 	}
     }
 }

Index: server.5c
===================================================================
RCS file: /local/src/CVS/rrserver/server.5c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- server.5c	29 May 2003 08:15:44 -0000	1.2
+++ server.5c	29 May 2003 17:46:29 -0000	1.3
@@ -31,6 +31,20 @@
     public typedef Client;
     public typedef Game;
 
+    mutex   server_mutex = Mutex::new ();
+
+    public bool lock () {
+	Mutex::acquire (server_mutex);
+	printf ("lock %v\n", Thread::current ());
+	return true;
+    }
+
+    public bool unlock () {
+	printf ("unlock %v\n", Thread::current ());
+	Mutex::release (server_mutex);
+	return true;
+    }
+
     public typedef struct {
 	int	x, y;
 	Object	object;
@@ -72,4 +86,6 @@
 	ClientRef	active;
 	ObjectLoc[*]    history;
     } Game;
+
+    public exception notreached ();
 }

--- connect.5c DELETED ---