[Commit] rrserver clients.5c,1.2,1.3 dispatch.5c,1.7,1.8 games.5c,1.6,1.7 protocol,1.15,1.16 readreq.5c,1.3,1.4 rr.5c,1.2,1.3 send.5c,1.2,1.3 server.5c,1.4,1.5

Keith Packard commit at keithp.com
Fri May 30 14:26:10 PDT 2003


Committed by: keithp

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

Modified Files:
	clients.5c dispatch.5c games.5c protocol readreq.5c rr.5c 
	send.5c server.5c 
Log Message:
Move game_send from Clients to Games.

Rework client termination to place NOTICEs where the datastructures were
modified so that all paths will emit the right messages.

Use general 'error' exception to pass errors to clients.

Add WATCH support, change USERS to PLAYERS, add WATCHERS

Add REVOKE and PART.

Rework game state management to automate things when clients depart or
change bids.

Update protocol to reflect existing errors.


Index: clients.5c
===================================================================
RCS file: /local/src/CVS/rrserver/clients.5c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- clients.5c	30 May 2003 06:47:05 -0000	1.2
+++ clients.5c	30 May 2003 20:26:07 -0000	1.3
@@ -49,14 +49,6 @@
 	    Clients::iterate (message_client);
 	}
 
-	public void game_send (&Game g, string fmt, poly args...) {
-	    void	message_client (&Client o) {
-		if (o.game == (GameRef.game) (&g))
-		    client_send (&o, fmt, args...);
-	    }
-	    Clients::iterate (message_client);
-	}
-
 	public &Client select (file f) {
 	    exception found (&Client c);
 	    try {
@@ -92,12 +84,18 @@
 				    user = User.none, 
 				    game = GameRef.none,
 				    score = 0,
+				    bid = Bid.none,
+				    playing = false
 				  }));
 	    return &clients[dim(clients)-1];
 	}
 
 	public void dispose (&Client c) {
+	    printf ("Client closed %v\n", c.user);
+	    if (c.user != User.none)
+		Clients::server_send ("NOTICE QUIT %s\n", c.user.username);
 	    Array::remove (&clients, &c);
+	    File::close (c.f);
 	}
 
 	public void print (&Client c) {

Index: dispatch.5c
===================================================================
RCS file: /local/src/CVS/rrserver/dispatch.5c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- dispatch.5c	30 May 2003 08:44:46 -0000	1.7
+++ dispatch.5c	30 May 2003 20:26:07 -0000	1.8
@@ -67,18 +67,18 @@
 	    void assert_user ()
 	    {
 		if (c.user == User.none)
-		    raise nonameset ();
+		    raise error (Error.NONAMESET);
 	    }
 
 	    void assert_game ()
 	    {
 		if (c.game == GameRef.none)
-		    raise notingame ();
+		    raise error (Error.NOTINGAME);
 	    }
 		
 	    void	helo (string username) {
 		if (Clients::find (username) != ClientRef.none)
-		    raise invalidname ();
+		    raise error (Error.INVALIDNAME);
 		c.user.username = username;
 		respond ("HELO %s\n", server_id);
 		Clients::server_send ("NOTICE USER %s\n", username);
@@ -100,14 +100,29 @@
 		respond ("\n");
 	    }
 
-	    void	users(string game) {
+	    void	players(string game) {
 		assert_user ();
 		&Game	g = Games::find (game);
-		respond ("USERS");
-		Games::iterate_client (&g, print_client_score);
+		respond ("PLAYERS");
+		Games::iterate_client (&g, print_client_score, true, false);
+		respond ("\n");
+	    }
+
+	    void	watchers(string game) {
+		assert_user ();
+		&Game	g = Games::find (game);
+		respond ("WATCHERS");
+		Games::iterate_client (&g, print_client_score, false, true);
 		respond ("\n");
 	    }
 
+	    void	new(string game) {
+		assert_user ();
+		&Game g = Games::new (game);
+		Games::add_client (&g, &c, true);
+		respond ("NEW %s\n", g.name);
+	    }
+
 	    void	join (string game) {
 		assert_user ();
 		&Game g = Games::find (game);
@@ -115,14 +130,13 @@
 		respond ("JOIN\n");
 	    }
 	    
-	    void	new(string game) {
+	    void	watch (string game) {
 		assert_user ();
-		&Game g =Games::new (game);
+		&Game g = Games::find (game);
 		Games::add_client (&g, &c, false);
-		respond ("NEW %s\n", g.name);
-		Clients::server_send ("NOTICE NEW %s\n", g.name);
+		respond ("WATCH\n");
 	    }
-
+	    
 	    void	show () {
 		assert_user ();
 		assert_game ();
@@ -138,6 +152,13 @@
 		respond ("BID\n");
 	    }
 
+	    void	revoke () {
+		assert_user ();
+		assert_game ();
+		Games::revoke (&c.game.game, &c);
+		respond ("REVOKE\n");
+	    }
+
 	    void	move (Color color, Direction direction) {
 		assert_user ();
 		assert_game ();
@@ -180,6 +201,13 @@
 		Clients::server_send ("NOTICE MESSAGE %s %s\n", u, text);
 	    }
 
+	    void	part () {
+		assert_user ();
+		assert_game ();
+		Games::remove_client (&c);
+		respond ("PART\n");
+	    }
+	    
 	    void	quit () {
 		respond ("QUIT\n");
 		raise Readreq::request_closed();
@@ -202,8 +230,17 @@
 		    case GAMES:
 			games ();
 			break;
-		    case USERS u:
-			users (u.game);
+		    case PLAYERS p:
+			players (p.game);
+			break;
+		    case WATCHERS w:
+			watchers (w.game);
+			break;
+		    case GAMEINFO g:
+#			gameinfo (g.game);
+			break;
+		    case USERINFO u:
+#			userinfo (u.username);
 			break;
 		    case NEW n:
 			new (n.game);
@@ -212,7 +249,7 @@
 			join (j.game);
 			break;
 		    case WATCH w:
-#			watch (w.game);
+			watch (w.game);
 			break;
 		    case SHOW:
 			show ();
@@ -220,6 +257,9 @@
 		    case BID b:
 			bid (b.number);
 			break;
+		    case REVOKE:
+			revoke ();
+			break;
 		    case MOVE m:
 			move (m.color, m.direction);
 			break;
@@ -238,62 +278,32 @@
 		    case MESSAGE m:
 			message (m.text);
 			break;
+		    case PART:
+			part ();
+			break;
 		    case QUIT:
 			quit ();
 			break;
 		    }
-		} catch Readreq::invalid_request (string w) {
-		    respond ("ERROR COMMAND %s\n", w);
-		} catch RR::Lex::syntax() {
-		    respond ("ERROR SYNTAX\n");
-		} catch RR::Lex::invalid_color (string color) {
-		    respond ("ERROR NOTCOLOR %s\n", color);
-		} catch RR::Lex::invalid_shape (string shape) {
-		    respond ("ERROR NOTSHAPE %s\n", shape);
-		} catch RR::Lex::invalid_direction (string direction) {
-		    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 Games::notbidding (&Game g) {
-		    respond ("ERROR NOTBIDDING\n");
-		} catch Games::toomanymoves (&Game g, &Client c) {
-		    respond ("ERROR TOOMANYMOVES\n");
-		} catch Games::blocked (&Game g, &Client c) {
-		    respond ("ERROR BLOCKED\n");
-		} catch Games::notlower (&Game g, &Client c, int bid) {
-		    respond ("ERROR NOTLOWER\n");
+		} catch error (Error e) {
+		    respond ("ERROR %E\n", e);
 		} catch Readreq::request_closed () {
-		    User user = c.user;
-		    printf ("Client closed %v\n", c.user);
-		    Clients::dispose (&c);
-		    if (c.user != User.none)
-			Clients::server_send ("NOTICE PART %s\n", c.user.username);
 		    return;
 		}
 	    }
 	}
 
 	void client_cleanup (&Client c) {
-	    if (c.game != GameRef.none)
-		Games::remove_client (&c.game.game, &c);
+	    Games::remove_client (&c);
 	    Clients::dispose (&c);
 	}
 	
 	public void client (file f)
 	{
-    	    twixt (true; File::close (f))
-		twixt (lock (); unlock ())
-		    twixt ((&Client c = Clients::new (f)), true;
-			   client_cleanup (&c))
-			client_locked (f, &c);
+	    twixt (lock (); unlock ())
+		twixt ((&Client c = Clients::new (f)), true;
+		       client_cleanup (&c))
+		    client_locked (f, &c);
 	}
     }
 }

Index: games.5c
===================================================================
RCS file: /local/src/CVS/rrserver/games.5c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- games.5c	30 May 2003 20:10:38 -0000	1.6
+++ games.5c	30 May 2003 20:26:07 -0000	1.7
@@ -40,17 +40,14 @@
 	    Array::remove (&games, &g);
 	}
 	
+	/* iterate over the available games */
 	public void iterate (void (&Game g) f) {
 	    Array::iterate (&games, f);
 	}
     
 	public exception no_such_game (string name);
-	public exception notactive (&Game g, &Client c);
-	public exception notbidding (&Game g);
-	public exception notlower (&Game g, &Client c, int bid);
-	public exception toomanymoves (&Game g, &Client c);
-	public exception blocked (&Game g, &Client c);
 	
+	/* find a game by name */
 	public &Game find (string name) {
 	    exception	found (&Game g);
 	    try {
@@ -77,28 +74,160 @@
 	    return false;
 	}
 
-	public void remove_client (&Game g, &Client c) {
-	    if (g.active == (ClientRef.client) (&c))
-		g.active = ClientRef.none;
+	/* list clients associated with a game */
+	public void iterate_client (&Game g, void (&Client c) f, 
+				    bool playing, bool watching) {
+	    void pick (&Client c) {
+		if ((playing && c.playing) || (watching && !c.playing))
+		    f (&c);
+	    }
+	    Array::iterate (&g.clients, pick);
+	}
+
+	public void game_send (&Game g, string fmt, poly args...) {
+	    void	message_client (&Client o) {
+		Clients::client_send (&o, fmt, args...);
+	    }
+	    iterate_client (&g, message_client, true, true);
+	}
+
+	bool any_bids (&Game g) {
+	    bool    any = false;
+	    void bid_set (&Client c) {
+		if (c.bid != Bid.none)
+		    any = true;
+	    }
+	    iterate_client (&g, bid_set, true, false);
+	    return any;
+	}
+
+	ClientRef lowest_bidder (&Game g) {
+	    Bid		min = Bid.none;
+	    ClientRef	min_client = ClientRef.none;
+	    void lower_bid (&Client c) {
+		union switch (c.bid) {
+		case none:
+		    break;
+		case number n:
+		    if (min == Bid.none || min.number > n)
+		    {
+			min = (Bid.number) n;
+			min_client = (ClientRef.client) (&c);
+		    }
+		    break;
+		}
+	    }
+	    iterate_client (&g, lower_bid, true, false);
+	    return min_client;
+	}
+
+	void set_active (&Game g) {
+	    ClientRef	active = lowest_bidder (&g);
+
+	    if (active != g.active) {
+		g.active = active;
+		union switch (active) {
+		case none:
+		    break;
+		case client c:
+		    game_send (&g, "NOTICE ACTIVE %s %d\n",
+			       c.user.username, c.bid.number);
+		    break;
+		}
+	    }
+	}
+
+	void set_state (&Game g, GameState state) {
+	    g.state = state;
+	    game_send (&g, "NOTICE GAMESTATE %G\n", state);
+	    switch (state) {
+	    case GameState.Bidding:
+		int timer_serial = ++g.timer_serial;
+		
+		bool validate () {
+		    if (g.state != GameState.Bidding || 
+			g.timer_serial != timer_serial)
+			    return false;
+		    return true;
+		}
+		
+		void notify (int remain) {
+		    game_send (&g, "NOTICE TIMER %d\n", remain);
+		}
+
+		void expire () {
+		    set_state (&g, GameState.Showing);
+		}
+
+		Timer::start (60, 10, lock, unlock, validate, notify, expire);
+		break;
+	    case GameState.Showing:
+		set_active (&g);
+		break;
+	    }
+	}
+
+	/* remove a client from any game */
+	public void remove_client (&Client c) {
+	    if (c.game == GameRef.none)
+		return;
+	    &Game g = &c.game.game;
 	    Array::remove (&g.clients, &c);
+	    game_send (&g, "NOTICE PART %s\n", c.user.username);
+	    /* correct the state if necessary */
+	    switch (g.state) {
+	    case GameState.New:
+		break;
+	    case GameState.Bidding:
+		if (!any_bids (&g))
+		    set_state (&g, GameState.New);
+		break;
+	    case GameState.Showing:
+		set_active (&g);
+		if (g.active == ClientRef.none)
+		    set_state (&g, GameState.New);
+		break;
+	    case GameState.Solved:
+		break;
+	    }
 	}
 
+	/* add a client to a game */
 	public &Client add_client (&Game g, &Client c, bool playing) {
-	    if (c.game != GameRef.none)
-		remove_client (&c.game.game, &c);
+	    remove_client (&c);
 	    c.game = (GameRef.game) (&g);
 	    c.playing = playing;
 	    c.score = 0;
 	    c.bid = Bid.none;
 	    Array::append (&g.clients, &c);
-	    Clients::game_send (&g, "NOTICE %s %s %s\n", 
-				playing ? "JOIN" : "WATCH",
-				g.name, c.user.username);
+	    game_send (&g, "NOTICE %s %s\n", 
+		       playing ? "JOIN" : "WATCH",
+		       c.user.username);
 	    return &c;
 	}
 
-	public void iterate_client (&Game g, void (&Client c) f) {
-	    Array::iterate (&g.clients, f);
+	/* select the next target */
+	void next_target (&Game g) {
+	    /* score */
+	    if (g.active != ClientRef.none && Boards::solved (&g.board))
+		g.active.client.score++;
+
+	    Target  t = g.targets[0];
+	    g.targets = (Target[dim(g.targets)-1]) { [i] = g.targets[i+1] };
+	    g.history = (ObjectLoc[*]) {};
+	    g.time = Time.none;
+	    g.state = GameState.New;
+	    Boards::set_target (&g.board, t.color, t.shape);
+
+	    void reset_client (&Client c) {
+		c.bid = Bid.none;
+	    }
+	    iterate_client (&g, reset_client, true, false);
+	    game_send (&g, "NOTICE TURN %C %S\n", t.color, t.shape);
+	}
+
+	public void next_turn (&Game g) {
+	    next_target (&g);
 	}
 
 	Target[*] random_targets () {
@@ -123,25 +252,6 @@
 	    return t;
 	}
 	    
-	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[*]) {};
-	    g.time = Time.none;
-	    g.state = GameState.New;
-	    Boards::set_target (&g.board, t.color, t.shape);
-
-	    void reset_client (&Client c) {
-		c.bid = Bid.none;
-	    }
-	    iterate_client (&g, reset_client);
-	    Clients::game_send (&g, "NOTICE TURN %C %S\n", t.color, t.shape);
-	}
-
-	public void next_turn (&Game g) {
-	    next_target (&g);
-	}
-
 	public &Game new (string suggestion) {
 	    string name;
 	    for (int n = 0; 
@@ -157,6 +267,7 @@
 	    g.active = ClientRef.none;
 	    g.timer_serial = 0;
 	    next_target (&g);
+	    Clients::server_send ("NOTICE NEW %s\n", g.name);
 	    return &g;
 	}
 
@@ -166,7 +277,7 @@
 
 	void assert_active (&Game g, &Client c) {
 	    if (g.active != (ClientRef.client) (&c))
-		raise notactive (&g, &c);
+		raise error (Error.NOTACTIVE);
 	}
 	
 	void undo_move (&Game g)
@@ -181,7 +292,7 @@
 	    if (dim (g.history) > 0)
 	    {
 		undo_move (&g);
-		Clients::game_send (&g, "NOTICE UNDO\n");
+		game_send (&g, "NOTICE UNDO\n");
 	    }
 	}
 
@@ -189,71 +300,7 @@
 	    assert_active (&g, &c);
 	    while (dim (g.history) > 0)
 		undo (&g, &c);
-	    Clients::game_send (&g, "NOTICE RESET\n");
-	}
-
-	ClientRef lowest_bidder (&Game g) {
-	    Bid		min = Bid.none;
-	    ClientRef	min_client = ClientRef.none;
-	    void lower_bid (&Client c) {
-		union switch (c.bid) {
-		case none:
-		    break;
-		case number n:
-		    if (min == Bid.none || min.number > n)
-		    {
-			min = (Bid.number) n;
-			min_client = (ClientRef.client) (&c);
-		    }
-		    break;
-		}
-	    }
-	    iterate_client (&g, lower_bid);
-	    return min_client;
-	}
-
-	void set_active (&Game g) {
-	    g.active = lowest_bidder (&g);
-
-	    union switch (g.active) {
-	    case none:
-		break;
-	    case client c:
-		Clients::game_send (&g, "NOTICE ACTIVE %s %d\n",
-				    c.user.username, c.bid.number);
-		break;
-	    }
-	}
-
-	void set_state (&Game g, GameState state) {
-	    g.state = state;
-	    Clients::game_send (&g, "NOTICE GAMESTATE %G\n", state);
-	    switch (state) {
-	    case GameState.Bidding:
-		int timer_serial = ++g.timer_serial;
-		
-		bool validate () {
-		    if (g.state != GameState.Bidding || 
-			g.timer_serial != timer_serial)
-			    return false;
-		    return true;
-		}
-		
-		void notify (int remain) {
-		    Clients::game_send (&g, "NOTICE TIMER %d\n",
-					remain);
-		}
-
-		void expire () {
-		    set_state (&g, GameState.Showing);
-		}
-
-		Timer::start (60, 10, lock, unlock, validate, notify, expire);
-		break;
-	    case GameState.Showing:
-		set_active (&g);
-		break;
-	    }
+	    game_send (&g, "NOTICE RESET\n");
 	}
 
 	public void bid (&Game g, &Client c, int number) {
@@ -264,15 +311,23 @@
 		break;
 	    case GameState.Showing:
 	    case GameState.Solved:
-		raise notbidding (&g);
+		raise error (Error.NOTBIDDING);
 		break;
 	    }
 	    /*
 	    if (c.bid != Bid.none && c.bid.number <= number)
-		raise notlower (&g, &c, number);
+		raise error (Error.NOTLOWER);
 	     */
 	    c.bid = (Bid.number) number;
-	    Clients::game_send (&g, "NOTICE BID %s %d\n", c.user.username, number);
+	    game_send (&g, "NOTICE BID %s %d\n", c.user.username, number);
+	}
+
+	public void revoke (&Game g, &Client c) {
+	    if (c.bid == Bid.none)
+		raise error (Error.NOBID);
+	    c.bid = Bid.none;
+	    if (!any_bids(&g))
+		set_state (&g, GameState.New);
 	}
 	
 	public int count (&Game g) {
@@ -282,15 +337,14 @@
 	public void move (&Game g, &Client c, Color color, Direction dir) {
 	    assert_active (&g, &c);
 	    if (count (&g) >= c.bid.number)
-		raise toomanymoves (&g, &c);
+		raise error (Error.TOOMANYMOVES);
 	    ObjectLoc	src = Boards::find_robot (&g.board, color);
 	    ObjectLoc	dst = Boards::move_robot (&g.board, color, dir);
 	    if (src == dst)
-		raise blocked (&g, &c);
+		raise error (Error.BLOCKED);
 	    Array::push (&g.history, src);
 	    Boards::position_robot (&g.board, color, dst.x, dst.y);
-	    Clients::game_send (&g, "NOTICE MOVE %d %C %D\n",
-				Games::count (&g), color, dir);
+	    game_send (&g, "NOTICE MOVE %d %C %D\n", count (&g), color, dir);
 	    if (Boards::solved (&g.board))
 		set_state (&g, GameState.Solved);
 	}

Index: protocol
===================================================================
RCS file: /local/src/CVS/rrserver/protocol,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -d -r1.15 -r1.16
--- protocol	30 May 2003 06:47:05 -0000	1.15
+++ protocol	30 May 2003 20:26:07 -0000	1.16
@@ -63,13 +63,21 @@
 
 	GAMES <game1> <game2> ...
 
-1.4. Listing users in a game
+1.4. Listing players in a game
 
-	USERS <game>
+	PLAYERS <game>
 
 	->
 
-	USERS <username1> <score1> <username2> <score2> ...
+	PLAYERS <username1> <score1> <username2> <score2> ...
+
+1.5. Listing watchers of a game
+
+	WATCHERS <game>
+
+	->
+
+	WATCERS <username1> <username2> ...
 
 1.5. Get game information
 
@@ -195,7 +203,17 @@
 
 	Possible errors: NOTINGAME, NOTBIDDING, NOTNUMBER, NOTLOWER
 
-1.12. Move
+1.12. Revoke
+
+	REVOKE
+
+	->
+
+	REVOKE
+
+	Possible errors: NOTINGAME, NOTBIDDING, NOBID
+
+1.13. Move
 
 	MOVE <color> <dir>
 
@@ -208,7 +226,7 @@
 
 	Possible errors: NOTINGAME, NOTACTIVE, BLOCKED, TOOMANYMOVES
 
-1.13. Undo
+1.14. Undo
 
 	UNDO
 
@@ -222,7 +240,7 @@
 
 	XXX: Do we add another error code for no further undo?
 
-1.14. Reset
+1.15. Reset
 
 	RESET
 
@@ -234,7 +252,7 @@
 
 	Possible errors: NOTINGAME, NOTACTIVE
 
-1.14. Next turn
+1.16. Next turn
 
 	TURN
 
@@ -254,7 +272,7 @@
 
 	Possible errors: NOTINGAME
 
-1.15. Pass the bid to the next lowest bidder
+1.17. Pass the bid to the next lowest bidder
 
 	PASS
 
@@ -264,7 +282,7 @@
 
 	Possible errors: NOTINGAME
 
-1.16. Message
+1.18. Message
 
 	MESSAGE <text>
 
@@ -274,7 +292,7 @@
 
 	Possible errors: NOTINGAME
 
-1.17. Part
+1.19. Part
 
 	PART
 
@@ -284,7 +302,7 @@
 
 	Departs the current game
 
-1.17. Quit
+1.20. Quit
 
 	QUIT
 
@@ -329,39 +347,43 @@
 
 	NOTICE BID <username> <number>
 
-2.7. Timer (game)
+2.7. Revoke (game)
+
+	NOTICE REVOKE <username>
+
+2.8. Timer (game)
 
 	NOTICE TIME <seconds>
 
 	Timer ticks are sent every 10 seconds
 
-2.8. Select active player (game)
+2.9. Select active player (game)
 
 	NOTICE ACTIVE <username> <bid>
 
 	Only the active player may move the robots
 
-2.9. Game state change
+2.10. Game state change
 
 	NOTICE GAMESTATE <state>
 
-2.9. Undo (game)
+2.11. Undo (game)
 
 	NOTICE UNDO
 
-2.11. Reset (game)
+2.12. Reset (game)
 
 	NOTICE RESET
 
-2.12. Next turn (game)
+2.13. Next turn (game)
 
 	NOTICE TURN <color> <shape>
 
-2.13. Message (all)
+2.14. Message (all)
 
 	NOTICE MESSAGE <username> <text>
 
-2.14. Abandon request (game)
+2.15. Abandon request (game)
 
 	NOTICE ABANDON <username>
 
@@ -372,7 +394,15 @@
 	selected with the robots reset to their position before the
 	turn started.
 
-2.15. User departed game (game)
+2.16. Join game (game)
+
+	NOTICE JOIN <username>
+
+2.17. Watch game (game)
+
+	NOTICE WATCH <username>
+
+2.18. User departed game (game)
 
 	NOTICE PART <username>
 
@@ -412,7 +442,15 @@
 
 	Possibly returned by: BID
 
-3.4. Not active
+3.4. No bid
+
+	ERROR NOBID
+
+	A revoke was requested when no bid had been entered
+
+	Possibly returned by: REVOKE
+
+3.5. Not active
 
 	ERROR NOTACTIVE
 
@@ -421,19 +459,71 @@
 	
 	Possibly returned by: MOVE, RESET, UNDO
 
-3.5. Not number
+3.6. Not number
 
 	ERROR NOTNUMBER
 
 	A non-numeric value was supplied where a number was required
 	
-3.6. Blocked
+3.7. Blocked
 
 	ERROR BLOCKED
 
 	The robot cannot move the requested direction.
 
 	Possibly returned by: MOVE
+
+3.8. TOOMANYMOVES
+
+	ERROR TOOMANYMOVES
+
+	An attempt was made to make more moves than the users bid
+
+	Possibly returned by: MOVE
+
+3.8. Command
+
+	ERROR COMMAND
+
+	An invalid command was specified
+
+3.9. Syntax
+
+	ERROR SYNTAX
+
+	A syntax error was detected
+
+3.10. Not color
+
+	ERROR NOTCOLOR
+
+	The color name specified in the command was invalid
+
+3.11. Not shape
+
+	ERROR NOTSHAPE
+
+	The shape name specified in the command was invalid
+
+3.12. Not direction
+
+	ERROR NOTDIRECTION
+
+	The direction name specified in the command was invalid
+
+3.13. No name set
+
+	ERROR NONAMESET
+
+	'helo' must be sent before any command other than 'quit'.
+
+3.14. Invalid name
+
+	ERROR INVALIDNAME
+
+	All names must be unique.
+
+
 
 Some comments/questions
 -----------------------

Index: readreq.5c
===================================================================
RCS file: /local/src/CVS/rrserver/readreq.5c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- readreq.5c	29 May 2003 17:46:29 -0000	1.3
+++ readreq.5c	30 May 2003 20:26:07 -0000	1.4
@@ -78,10 +78,25 @@
 	    case "GAMES":
 		req = Request.GAMES;
 		break;
-	    case "USERS":
+	    case "PLAYERS":
 		packet = (struct { string game; }) {};
 		game_ (&packet);
-		req = (Request.USERS) packet;
+		req = (Request.PLAYERS) packet;
+		break;
+	    case "WATCHERS":
+		packet = (struct { string game; }) {};
+		game_ (&packet);
+		req = (Request.WATCHERS) packet;
+		break;
+	    case "GAMEINFO":
+		packet = (struct { string game; }) {};
+		game_ (&packet);
+		req = (Request.GAMEINFO) packet;
+		break;
+	    case "USERINFO":
+		packet = (struct { string username; }) {};
+		username_ (&packet);
+		req = (Request.USERINFO) packet;
 		break;
 	    case "NEW":
 		packet = (struct { string game; }) {};
@@ -106,6 +121,9 @@
 		number_ (&packet);
 		req = (Request.BID) packet;
 		break;
+	    case "REVOKE":
+		req = Request.REVOKE;
+		break;
 	    case "MOVE":
 		packet = (struct { Color color; Direction direction; }) {};
 		color_ (&packet);
@@ -129,23 +147,36 @@
 		text_ (&packet);
 		req = (Request.MESSAGE) packet;
 		break;
+	    case "PART":
+		req = Request.PART;
+		break;
 	    case "QUIT":
 		req = Request.QUIT;
 		break;
 	    case "":
 		raise request_closed ();
 	    default:
-		raise invalid_request (w);
+		raise error (Error.COMMAND);
 	    }
 	    if (!eol (f))
-		raise syntax ();
+		raise error (Error.SYNTAX);
 	    return req;
 	}
 
 	public Request read (file f) {
-	    twixt (unlock (); lock ())
-		twixt (true; skipline (f))
-		    return read_unlocked (f);
+	    try {
+		twixt (unlock (); lock ())
+		    twixt (true; skipline (f))
+			return read_unlocked (f);
+	    } catch RR::Lex::syntax() {
+		raise error (Error.SYNTAX);
+	    } catch RR::Lex::invalid_color (string color) {
+		raise error (Error.NOTCOLOR);
+	    } catch RR::Lex::invalid_shape (string shape) {
+		raise error (Error.NOTSHAPE);
+	    } catch RR::Lex::invalid_direction (string direction) {
+		raise error (Error.NOTDIRECTION);
+	    } 
 	    raise notreached ();
 	}
     }

Index: rr.5c
===================================================================
RCS file: /local/src/CVS/rrserver/rr.5c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- rr.5c	30 May 2003 06:47:05 -0000	1.2
+++ rr.5c	30 May 2003 20:26:07 -0000	1.3
@@ -74,36 +74,47 @@
     public typedef union {
 	struct {
 	    string	username;
-	}	    HELO;
+	}	HELO;
 	void    WHO;
 	void    GAMES;
 	struct {
 	    string	game;
-	}	    USERS;
+	}	PLAYERS;
 	struct {
 	    string	game;
-	}	    NEW;
+	}	WATCHERS;
 	struct {
 	    string	game;
-	}	    JOIN;
+	}	GAMEINFO;
+	struct {
+	    string	username;
+	}	USERINFO;
 	struct {
 	    string	game;
-	}	    WATCH;
+	}	NEW;
+	struct {
+	    string	game;
+	}	JOIN;
+	struct {
+	    string	game;
+	}	WATCH;
 	void    SHOW;
 	struct {
 	    int	number;
-	}	    BID;
+	}	BID;
+	void	REVOKE;
 	struct {
 	    Color	color;
 	    Direction   direction;
-	}	    MOVE;
+	}	MOVE;
 	void    UNDO;
 	void    RESET;
 	void    TURN;
 	void    PASS;
 	struct {
 	    string	text;
-	}	    MESSAGE;
+	}	MESSAGE;
+	void	PART;
 	void	QUIT;
     } Request;
 
@@ -163,6 +174,9 @@
 	    string	username;
 	}	BID;
 	struct {
+	    string	username;
+	}	REVOKE;
+	struct {
 	    int	seconds;
 	}	TIME;
 	struct {
@@ -183,10 +197,23 @@
 	}	ABANDON;
     } Notice;
 
-    public typedef union {
-	void    NOGAME;
-	void    TIMEOUT;
-	void    NOTACTIVE;
+    public typedef enum {
+	NOGAME,
+	NOTINGAME,
+	NOTBIDDING,
+	NOTLOWER,
+	NOBID,
+	NOTACTIVE,
+	NOTNUMBER,
+	BLOCKED,
+	COMMAND,
+	SYNTAX,
+	NOTCOLOR,
+	NOTSHAPE,
+	NOTDIRECTION,
+	TOOMANYMOVES,
+	NONAMESET,
+	INVALIDNAME
     } Error;
 
     public typedef union {

Index: send.5c
===================================================================
RCS file: /local/src/CVS/rrserver/send.5c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- send.5c	30 May 2003 06:47:05 -0000	1.2
+++ send.5c	30 May 2003 20:26:07 -0000	1.3
@@ -98,6 +98,27 @@
 		}
 	    }
 
+	    void put_error (Error e) {
+		switch (e) {
+		case Error.NOGAME:	File::fprintf (f, "NOGAME"); break;
+		case Error.NOTINGAME:	File::fprintf (f, "NOTINGAME"); break;
+		case Error.NOTBIDDING:	File::fprintf (f, "NOTBIDDING"); break;
+		case Error.NOTLOWER:	File::fprintf (f, "NOTLOWER"); break;
+		case Error.NOBID:	File::fprintf (f, "NOBID"); break;
+		case Error.NOTACTIVE:	File::fprintf (f, "NOTACTIVE"); break;
+		case Error.NOTNUMBER:	File::fprintf (f, "NOTNUMBER"); break;
+		case Error.BLOCKED:	File::fprintf (f, "BLOCKED"); break;
+		case Error.COMMAND:	File::fprintf (f, "COMMAND"); break;
+		case Error.SYNTAX:	File::fprintf (f, "SYNTAX"); break;
+		case Error.NOTCOLOR:	File::fprintf (f, "NOTCOLOR"); break;
+		case Error.NOTSHAPE:	File::fprintf (f, "NOTSHAPE"); break;
+		case Error.NOTDIRECTION:File::fprintf (f, "NOTDIRECTION"); break;
+		case Error.TOOMANYMOVES:File::fprintf (f, "TOOMANYMOVES"); break;
+		case Error.NONAMESET:	File::fprintf (f, "NONAMESET"); break;
+		case Error.INVALIDNAME:	File::fprintf (f, "INVALIDNAME"); break;
+		}
+	    }
+
 	    void put_number (int n) {
 		File::fprintf (f, "%d", n);
 	    }
@@ -185,6 +206,7 @@
 		    case 'D':	put_direction (a); break;
 		    case 'S':	put_shape (a); break;
 		    case 'G':	put_gamestate (a); break;
+		    case 'E':	put_error (a); break;
 		    }
 		}
 		else

Index: server.5c
===================================================================
RCS file: /local/src/CVS/rrserver/server.5c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- server.5c	30 May 2003 06:47:05 -0000	1.4
+++ server.5c	30 May 2003 20:26:07 -0000	1.5
@@ -37,6 +37,8 @@
 
     public void set_server_id (string id) { server_id = id; }
 
+    public exception error (Error e);
+
     public bool lock () {
 	Mutex::acquire (server_mutex);
 #	printf ("lock %v\n", Thread::current());




More information about the Commit mailing list