frontlib+Hedgeroid: Added support for the new client flags (chief, admin, reg)
authorMedo <smaxein@googlemail.com>
Sun, 16 Sep 2012 22:31:34 +0200
changeset 7691 55c0a856ecd0
parent 7689 855eeee4166f
child 7693 0bdb1dd83b63
frontlib+Hedgeroid: Added support for the new client flags (chief, admin, reg)
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Player.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/PlayerInRoom.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LobbyPlayerlistAdapter.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ClientFlagsUpdate.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ThreadedNetConnection.java
project_files/frontlib/extra/jnacontrol.c
project_files/frontlib/net/netconn.c
project_files/frontlib/net/netconn.h
project_files/frontlib/net/netconn_callbacks.c
project_files/frontlib/net/netconn_internal.h
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Player.java	Sun Sep 16 17:42:48 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/Player.java	Sun Sep 16 22:31:34 2012 +0200
@@ -40,9 +40,13 @@
 				+ ", admin=" + admin + "]";
 	}
 
-	public static Comparator<Player> NAME_ORDER = new Comparator<Player>() {
+	public static Comparator<Player> ADMIN_NAME_ORDER = new Comparator<Player>() {
 		public int compare(Player lhs, Player rhs) {
-			return lhs.name.compareToIgnoreCase(rhs.name);
+			if(lhs.admin != rhs.admin) {
+				return lhs.admin ? -1 : 1;
+			} else {
+				return lhs.name.compareToIgnoreCase(rhs.name);
+			}
 		}
 	};
 }
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/PlayerInRoom.java	Sun Sep 16 17:42:48 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Datastructures/PlayerInRoom.java	Sun Sep 16 22:31:34 2012 +0200
@@ -21,15 +21,17 @@
 
 public final class PlayerInRoom {
 	public final Player player;
-	public final boolean ready;
+	public final boolean ready, roomChief;
 	
-	public PlayerInRoom(Player player, boolean ready) {
+	public PlayerInRoom(Player player, boolean ready, boolean roomChief) {
 		this.player = player;
 		this.ready = ready;
+		this.roomChief = roomChief;
 	}
 
 	@Override
 	public String toString() {
-		return "PlayerInRoom [player=" + player + ", ready=" + ready + "]";
+		return "PlayerInRoom [player=" + player + ", ready=" + ready
+				+ ", roomChief=" + roomChief + "]";
 	}
 }
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LobbyPlayerlistAdapter.java	Sun Sep 16 17:42:48 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/LobbyPlayerlistAdapter.java	Sun Sep 16 22:31:34 2012 +0200
@@ -25,6 +25,13 @@
 import org.hedgewars.hedgeroid.Datastructures.Player;
 import org.hedgewars.hedgeroid.util.ObservableTreeMapAdapter;
 
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.StyleSpan;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -36,7 +43,7 @@
 public class LobbyPlayerlistAdapter extends ObservableTreeMapAdapter<String, Player> {
 	@Override
 	protected Comparator<Player> getEntryOrder() {
-		return Player.NAME_ORDER;
+		return Player.ADMIN_NAME_ORDER;
 	}
 
 	public View getView(int position, View convertView, ViewGroup parent) {
@@ -46,9 +53,16 @@
 			v = vi.inflate(R.layout.listview_player, null);
 		}
 
-		String player = getItem(position).name;
+		Player player = getItem(position);
 		TextView username = (TextView) v.findViewById(android.R.id.text1);
-		username.setText(player);
+		Spannable spannable = new SpannableString(player.name);
+		if(player.registered) {
+			spannable.setSpan(new StyleSpan(Typeface.BOLD), 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+		}
+		if(player.admin) {
+			spannable.setSpan(new ForegroundColorSpan(Color.YELLOW), 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+		}
+		username.setText(spannable);
 		return v;
 	}
 }
\ No newline at end of file
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java	Sun Sep 16 17:42:48 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/frontlib/Frontlib.java	Sun Sep 16 22:31:34 2012 +0200
@@ -950,6 +950,10 @@
 		void callback(Pointer context, String arg1, String arg2);
 	}
 	
+	public static interface StrStrBoolCallback extends Callback {
+		void callback(Pointer context, String arg1, String arg2, boolean arg3);
+	}
+	
 	public static interface RoomCallback extends Callback {
 		void callback(Pointer context, RoomPtr arg1);
 	}
@@ -1089,6 +1093,7 @@
 	int flib_netconn_send_getServerVars(NetconnPtr conn);
 	
 	void flib_netconn_onMessage(NetconnPtr conn, IntStrCallback callback, Pointer context);
+	void flib_netconn_onClientFlags(NetconnPtr conn, StrStrBoolCallback callback, Pointer context);
 	void flib_netconn_onChat(NetconnPtr conn, StrStrCallback callback, Pointer context);
 	void flib_netconn_onConnected(NetconnPtr conn, VoidCallback callback, Pointer context);
 	void flib_netconn_onDisconnected(NetconnPtr conn, IntStrCallback callback, Pointer context);
@@ -1101,8 +1106,6 @@
 	void flib_netconn_onNickTaken(NetconnPtr conn, StrCallback callback, Pointer context);
 	void flib_netconn_onPasswordRequest(NetconnPtr conn, StrCallback callback, Pointer context);
 	void flib_netconn_onEnterRoom(NetconnPtr conn, BoolCallback callback, Pointer context);
-	void flib_netconn_onRoomChiefStatus(NetconnPtr conn, BoolCallback callback, Pointer context);
-	void flib_netconn_onReadyState(NetconnPtr conn, StrBoolCallback callback, Pointer context);
 	void flib_netconn_onLeaveRoom(NetconnPtr conn, IntStrCallback callback, Pointer context);
 	void flib_netconn_onTeamAdd(NetconnPtr conn, TeamCallback callback, Pointer context);
 	void flib_netconn_onTeamDelete(NetconnPtr conn, StrCallback callback, Pointer context);
@@ -1117,7 +1120,6 @@
 	void flib_netconn_onMapChanged(NetconnPtr conn, MapIntCallback callback, Pointer context);
 	void flib_netconn_onScriptChanged(NetconnPtr conn, StrCallback callback, Pointer context);
 	void flib_netconn_onWeaponsetChanged(NetconnPtr conn, WeaponsetCallback callback, Pointer context);
-	void flib_netconn_onAdminAccess(NetconnPtr conn, VoidCallback callback, Pointer context);
 	void flib_netconn_onServerVar(NetconnPtr conn, StrStrCallback callback, Pointer context);
 
 	// ipc/gameconn.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ClientFlagsUpdate.java	Sun Sep 16 22:31:34 2012 +0200
@@ -0,0 +1,48 @@
+package org.hedgewars.hedgeroid.netplay;
+
+import org.hedgewars.hedgeroid.Datastructures.Player;
+import org.hedgewars.hedgeroid.Datastructures.PlayerInRoom;
+
+final class ClientFlagsUpdate {
+	public static final char FLAG_ADMIN = 'a';
+	public static final char FLAG_CHIEF = 'h';
+	public static final char FLAG_READY = 'r';
+	public static final char FLAG_REGISTERED = 'u';
+	
+	public final String nick, flags;
+	public final boolean newFlagState;
+	
+	public ClientFlagsUpdate(String nick, String flags, boolean newFlagState) {
+		this.nick = nick;
+		this.flags = flags;
+		this.newFlagState = newFlagState;
+	}
+
+	public Player applyTo(Player p) {
+		return new Player(
+				p.name,
+				updatedFlag(FLAG_REGISTERED, p.registered),
+				updatedFlag(FLAG_ADMIN, p.admin));
+	}
+	
+	public PlayerInRoom applyTo(PlayerInRoom p) {
+		return new PlayerInRoom(
+				this.applyTo(p.player),
+				updatedFlag(FLAG_READY, p.ready),
+				updatedFlag(FLAG_CHIEF, p.roomChief));
+	}
+	
+	public boolean appliesTo(char flag) {
+		return flags.indexOf(flag) != -1;
+	}
+	
+	private boolean updatedFlag(char flag, boolean oldValue) {
+		return appliesTo(flag) ? newFlagState : oldValue;
+	}
+	
+	@Override
+	public String toString() {
+		return "ClientFlagsUpdate [nick=" + nick + ", flags=" + flags
+				+ ", newFlagState=" + newFlagState + "]";
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java	Sun Sep 16 17:42:48 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/Netplay.java	Sun Sep 16 22:31:34 2012 +0200
@@ -279,6 +279,7 @@
 		MSG_LOBBY_LEAVE,
 		MSG_ROOM_JOIN,
 		MSG_ROOM_LEAVE,
+		MSG_CLIENT_FLAGS,
 		MSG_CHAT,
 		MSG_MESSAGE,
 		MSG_ROOM_ADD,
@@ -290,7 +291,6 @@
 		MSG_PASSWORD_REQUEST,
 		MSG_ENTER_ROOM_FROM_LOBBY,
 		MSG_LEAVE_ROOM,
-		MSG_READYSTATE,
 		MSG_TEAM_ADDED,
 		MSG_TEAM_DELETED,
 		MSG_TEAM_ACCEPTED,
@@ -300,7 +300,6 @@
 		MSG_RUN_GAME,
 		MSG_SCHEME_CHANGED,
 		MSG_MAP_CHANGED,
-		MSG_ROOM_CHIEF_STATUS_CHANGED,
 		MSG_SCRIPT_CHANGED,
 		MSG_WEAPONSET_CHANGED;
 		
@@ -339,7 +338,7 @@
 					Log.w("Netplay", "Unknown player joined room: "+name);
 					p = new Player(name, false, false);
 				}
-				roomPlayerlist.put(name, new PlayerInRoom(p, false));
+				roomPlayerlist.put(name, new PlayerInRoom(p, false, false));
 				roomChatlog.appendPlayerJoin(name);
 				break;
 			}
@@ -349,6 +348,23 @@
 				roomChatlog.appendPlayerLeave(args.first, args.second);
 				break;
 			}
+			case MSG_CLIENT_FLAGS: {
+				ClientFlagsUpdate upd = (ClientFlagsUpdate)msg.obj;
+				PlayerInRoom pir = roomPlayerlist.get(upd.nick);
+				if(pir != null) {
+					roomPlayerlist.put(upd.nick, upd.applyTo(pir));
+				}
+				Player p = lobbyPlayerlist.get(upd.nick);
+				if(p != null) {
+					lobbyPlayerlist.put(upd.nick, upd.applyTo(p));
+				} else {
+					Log.w("Netplay", "Received client flags for unknown player "+upd.nick);
+				}
+				if(playerName.equals(upd.nick) && upd.appliesTo(ClientFlagsUpdate.FLAG_CHIEF)) {
+					netRoomState.setChief(upd.newFlagState);
+				}
+				break;
+			}
 			case MSG_CHAT: {
 				Pair<String, String> args = (Pair<String, String>)msg.obj;
 				getCurrentLog().appendChat(args.first, args.second);
@@ -423,18 +439,6 @@
 				broadcastManager.sendBroadcastSync(intent);
 				break;
 			}
-			case MSG_READYSTATE: {
-				Pair<String, Boolean> args = (Pair<String, Boolean>)msg.obj;
-				String name = args.first;
-				Boolean newReadyState = args.second;
-				PlayerInRoom oldEntry = roomPlayerlist.get(name);
-				if(oldEntry==null) {
-					Log.e("Netplay", "Setting readystate for unknown player "+name);
-				} else {
-					roomPlayerlist.put(name, new PlayerInRoom(oldEntry.player, newReadyState));
-				}
-				break;
-			}
 			case MSG_TEAM_ADDED: {
 				TeamInGame newTeam = (TeamInGame)msg.obj;
 				if(isChief()) {
@@ -509,10 +513,6 @@
 				netRoomState.setMapRecipe((MapRecipe)msg.obj);
 				break;
 			}
-			case MSG_ROOM_CHIEF_STATUS_CHANGED: {
-				netRoomState.setChief((Boolean)msg.obj);
-				break;
-			}
 			case MSG_SCHEME_CHANGED: {
 				netRoomState.setScheme((Scheme)msg.obj);
 				break;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ThreadedNetConnection.java	Sun Sep 16 17:42:48 2012 +0200
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/netplay/ThreadedNetConnection.java	Sun Sep 16 22:31:34 2012 +0200
@@ -48,10 +48,10 @@
 import org.hedgewars.hedgeroid.frontlib.Frontlib.RoomPtr;
 import org.hedgewars.hedgeroid.frontlib.Frontlib.SchemeCallback;
 import org.hedgewars.hedgeroid.frontlib.Frontlib.SchemePtr;
-import org.hedgewars.hedgeroid.frontlib.Frontlib.StrBoolCallback;
 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrCallback;
 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrIntCallback;
 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrRoomCallback;
+import org.hedgewars.hedgeroid.frontlib.Frontlib.StrStrBoolCallback;
 import org.hedgewars.hedgeroid.frontlib.Frontlib.StrStrCallback;
 import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamCallback;
 import org.hedgewars.hedgeroid.frontlib.Frontlib.TeamPtr;
@@ -128,8 +128,8 @@
 					return;
 				}
 
-				// FLIB.flib_netconn_onAdminAccess(conn, adminAccessCb, null)
 				FLIB.flib_netconn_onSchemeChanged(conn, cfgSchemeCb, null);
+				FLIB.flib_netconn_onClientFlags(conn, clientFlagsCb, null);
 				FLIB.flib_netconn_onChat(conn, chatCb, null);
 				FLIB.flib_netconn_onConnected(conn, connectedCb, null);
 				FLIB.flib_netconn_onDisconnected(conn, disconnectCb, null);
@@ -142,9 +142,7 @@
 				FLIB.flib_netconn_onMapChanged(conn, mapChangedCb, null);
 				FLIB.flib_netconn_onMessage(conn, messageCb, null);
 				FLIB.flib_netconn_onPasswordRequest(conn, passwordRequestCb, null);
-				FLIB.flib_netconn_onReadyState(conn, readyStateCb, null);
 				FLIB.flib_netconn_onRoomAdd(conn, roomAddCb, null);
-				FLIB.flib_netconn_onRoomChiefStatus(conn, roomChiefStatusCb, null);
 				FLIB.flib_netconn_onRoomDelete(conn, roomDeleteCb, null);
 				FLIB.flib_netconn_onRoomJoin(conn, roomJoinCb, null);
 				FLIB.flib_netconn_onRoomLeave(conn, roomLeaveCb, null);
@@ -192,12 +190,6 @@
 		}
 	};
 	
-	private final BoolCallback roomChiefStatusCb = new BoolCallback() {
-		public void callback(Pointer context, boolean chief) {
-			sendFromNet(MSG_ROOM_CHIEF_STATUS_CHANGED, chief);
-		}
-	};
-	
 	private final StrCallback scriptChangedCb = new StrCallback() {
 		public void callback(Pointer context, String script) {
 			sendFromNet(MSG_SCRIPT_CHANGED, script);
@@ -227,11 +219,19 @@
 			sendFromNet(MSG_ROOM_JOIN, name);
 		}
 	};
+	
 	private final StrStrCallback roomLeaveCb = new StrStrCallback() {
 		public void callback(Pointer context, String name, String message) {
 			sendFromNet(MSG_ROOM_LEAVE, Pair.create(name, message));
 		}
 	};
+	
+	private final StrStrBoolCallback clientFlagsCb = new StrStrBoolCallback() {
+		public void callback(Pointer context, String nick, String flags, boolean newFlagsState) {
+			sendFromNet(MSG_CLIENT_FLAGS, new ClientFlagsUpdate(nick, flags, newFlagsState));
+		}
+	};
+	
 	private final StrStrCallback chatCb = new StrStrCallback() {
 		public void callback(Pointer context, String name, String msg) {
 			sendFromNet(MSG_CHAT, Pair.create(name, msg));
@@ -294,12 +294,6 @@
 		}
 	};
 	
-	private final StrBoolCallback readyStateCb = new StrBoolCallback() {
-		public void callback(Pointer context, String player, boolean ready) {
-			sendFromNet(MSG_READYSTATE, Pair.create(player, ready));
-		}
-	};
-	
 	private final TeamCallback teamAddedCb = new TeamCallback() {
 		public void callback(Pointer context, TeamPtr team) {
 			sendFromNet(MSG_TEAM_ADDED, team.deref());
--- a/project_files/frontlib/extra/jnacontrol.c	Sun Sep 16 17:42:48 2012 +0200
+++ b/project_files/frontlib/extra/jnacontrol.c	Sun Sep 16 22:31:34 2012 +0200
@@ -79,6 +79,7 @@
 typedef void (*IntStrCallback)(Pointer context, int arg1, String arg2);
 typedef void (*StrIntCallback)(Pointer context, String arg1, int arg2);
 typedef void (*StrStrCallback)(Pointer context, String arg1, String arg2);
+typedef void (*StrStrBoolCallback)(Pointer context, String arg1, String arg2, boolean arg3);
 typedef void (*RoomCallback)(Pointer context, RoomPtr arg1);
 typedef void (*RoomListCallback)(Pointer context, RoomArrayPtr arg1, int arg2);
 typedef void (*StrRoomCallback)(Pointer context, String arg1, RoomPtr arg2);
@@ -155,6 +156,7 @@
 	int flib_netconn_send_getServerVars(NetconnPtr conn);
 
 	void flib_netconn_onMessage(NetconnPtr conn, IntStrCallback callback, Pointer context);
+	void flib_netconn_onClientFlags(NetconnPtr conn, StrStrBoolCallback callback, Pointer context);
 	void flib_netconn_onChat(NetconnPtr conn, StrStrCallback callback, Pointer context);
 	void flib_netconn_onConnected(NetconnPtr conn, VoidCallback callback, Pointer context);
 	void flib_netconn_onDisconnected(NetconnPtr conn, IntStrCallback callback, Pointer context);
@@ -167,8 +169,6 @@
 	void flib_netconn_onNickTaken(NetconnPtr conn, StrCallback callback, Pointer context);
 	void flib_netconn_onPasswordRequest(NetconnPtr conn, StrCallback callback, Pointer context);
 	void flib_netconn_onEnterRoom(NetconnPtr conn, BoolCallback callback, Pointer context);
-	void flib_netconn_onRoomChiefStatus(NetconnPtr conn, BoolCallback callback, Pointer context);
-	void flib_netconn_onReadyState(NetconnPtr conn, StrBoolCallback callback, Pointer context);
 	void flib_netconn_onLeaveRoom(NetconnPtr conn, IntStrCallback callback, Pointer context);
 	void flib_netconn_onTeamAdd(NetconnPtr conn, TeamCallback callback, Pointer context);
 	void flib_netconn_onTeamDelete(NetconnPtr conn, StrCallback callback, Pointer context);
@@ -183,7 +183,6 @@
 	void flib_netconn_onMapChanged(NetconnPtr conn, MapIntCallback callback, Pointer context);
 	void flib_netconn_onScriptChanged(NetconnPtr conn, StrCallback callback, Pointer context);
 	void flib_netconn_onWeaponsetChanged(NetconnPtr conn, WeaponsetCallback callback, Pointer context);
-	void flib_netconn_onAdminAccess(NetconnPtr conn, VoidCallback callback, Pointer context);
 	void flib_netconn_onServerVar(NetconnPtr conn, StrStrCallback callback, Pointer context);
 
 	// ipc/gameconn.h
--- a/project_files/frontlib/net/netconn.c	Sun Sep 16 17:42:48 2012 +0200
+++ b/project_files/frontlib/net/netconn.c	Sun Sep 16 22:31:34 2012 +0200
@@ -41,7 +41,6 @@
 			newConn->dataDirPath = flib_strdupnull(dataDirPath);
 
 			newConn->netconnState = NETCONN_STATE_CONNECTING;
-			newConn->isAdmin = false;
 
 			newConn->isChief = false;
 			newConn->map = flib_map_create_named("", "NoSuchMap");
@@ -309,17 +308,12 @@
 				const char *flags = netmsg->parts[1];
 				bool setFlag = flags[0] == '+';
 
-				for(int i=1; flags[i]; i++) {
-					switch(flags[i]) {
-					case 'r':
-						for(int j = 2; j < netmsg->partCount; ++j) {
-							conn->onReadyStateCb(conn->onReadyStateCtx, netmsg->parts[j], setFlag);
-						}
-						break;
-					default:
-						flib_log_w("Net: Unknown flag %c in CLIENT_FLAGS message", flags[i]);
-						break;
+				for(int j = 2; j < netmsg->partCount; ++j) {
+					bool isSelf = !strcmp(conn->playerName, netmsg->parts[j]);
+					if(isSelf && strchr(flags, 'h')) {
+						conn->isChief = setFlag;
 					}
+					conn->onClientFlagsCb(conn->onClientFlagsCtx, netmsg->parts[j], flags+1, setFlag);
 				}
 	        }
 	    } else if (!strcmp(cmd, "ADD_TEAM")) {
@@ -589,15 +583,9 @@
 				exit = true;
 	        }
 	    } else if (!strcmp(cmd, "ADMIN_ACCESS")) {
-	    	conn->onAdminAccessCb(conn->onAdminAccessCtx);
-	    	conn->isAdmin = true;
+	    	// deprecated
 	    } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) {
-	        if (netmsg->partCount < 2) {
-	            flib_log_w("Net: Bad ROOM_CONTROL_ACCESS message");
-	        } else {
-	        	conn->isChief = strcmp("0", netmsg->parts[1]);
-	        	conn->onRoomChiefStatusCb(conn->onRoomChiefStatusCtx, conn->isChief);
-	        }
+	    	// deprecated
 	    } else {
 	    	flib_log_w("Unknown server command: %s", cmd);
 	    }
--- a/project_files/frontlib/net/netconn.h	Sun Sep 16 17:42:48 2012 +0200
+++ b/project_files/frontlib/net/netconn.h	Sun Sep 16 22:31:34 2012 +0200
@@ -188,7 +188,8 @@
 
 	/**
 	 * Join a room as guest (not chief). Only makes sense when in lobby state. If the action
-	 * succeeds, you will receive an onEnterRoom callback with chief=false.
+	 * succeeds, you will receive an onEnterRoom callback with chief=false followed by other
+	 * callbacks with current room information.
 	 */
 	int flib_netconn_send_joinRoom(flib_netconn *conn, const char *room);
 
@@ -236,14 +237,14 @@
 
 	/**
 	 * Leave the room for the lobby. Only makes sense in room state. msg can be NULL if you don't
-	 * want to send a message. The server always accepts a part message, so once you send it off,
+	 * want to send a message. The server always accepts a part command, so once you send it off,
 	 * you can just assume that you are back in the lobby.
 	 */
 	int flib_netconn_send_leaveRoom(flib_netconn *conn, const char *msg);
 
 	/**
 	 * Change your "ready" status in the room. Only makes sense when in room state. If the action
-	 * succeeds, you will receive an onReadyState callback containing the change.
+	 * succeeds, you will receive an onClientFlags callback containing the change.
 	 */
 	int flib_netconn_send_toggleReady(flib_netconn *conn);
 
@@ -479,6 +480,36 @@
 	void flib_netconn_onLobbyJoin(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context);
 	void flib_netconn_onLobbyLeave(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *partMessage), void* context);
 
+	/**
+	 * This is called when the server informs us that one or more flags associated with a
+	 * player/client have changed.
+	 *
+	 * nick is the name of the player, flags is a string containing one character for each modified
+	 * flag (see below), and newFlagState signals whether the flags should be set to true or false.
+	 *
+	 * Some of these flags are important for protocol purposes (especially if they are set for you)
+	 * while others are just informational. Also, some flags are only relevant for players who are
+	 * in the same room as you, and the server will not inform you if they change for others.
+	 *
+	 * These are the currently known/used flags:
+	 * a: Server admin. Always updated.
+	 * h: Room chief. Updated when in the same room.
+	 * r: Ready to play. Updated when in the same room.
+	 * u: Registered user. Always updated.
+	 *
+	 * The server tells us the 'a' and 'u' flags for all players when we first join the lobby, and
+	 * also tells us the 'r' and 'h' flags when we join or create a room. It assumes that all flags
+	 * are initially false, so it will typically only tell you to set certain flags to true when
+	 * transmitting the initial states. Reset the 'h' and 'r' flags to false when leaving a room,
+	 * or when entering room state, to arrive at the right state for each player.
+	 *
+	 * The room chief state of yourself is particularly important because it determines whether you
+	 * can modify settings of the current room. Generally, when you create a room you start out
+	 * being room chief, and when you join an existing room you are not. However, if the original
+	 * chief leaves a room, the server can choose a new chief, and if that happens the chief flag
+	 * will be transferred to someone else.
+	 */
+	void flib_netconn_onClientFlags(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *flags, bool newFlagState), void *context);
 
 // Callbacks that happen only in response to specific requests
 
@@ -520,23 +551,6 @@
 // Callbacks that are only relevant in a room
 
 	/**
-	 * This callback informs about changes to your room chief status, i.e. whether you are allowed
-	 * to modify the current room. Generally when you create a room you start out being room chief,
-	 * and when you join an existing room you are not. However, if the original chief leaves a room,
-	 * the server can choose a new chief, and if that happens this callback is called with the new
-	 * status.
-	 *
-	 * Note: This callback does not automatically fire when joining a room. You can always query the
-	 * current chief status using flib_netconn_is_chief().
-	 */
-	void flib_netconn_onRoomChiefStatus(flib_netconn *conn, void (*callback)(void *context, bool chief), void* context);
-
-	/**
-	 * One of the players in the room (possibly you) changed their ready state.
-	 */
-	void flib_netconn_onReadyState(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context);
-
-	/**
 	 * You just left a room and entered the lobby again.
 	 * reason is one of the NETCONN_ROOMLEAVE_ constants (usually a kick).
 	 * This will not be called when you actively leave a room using PART.
@@ -637,12 +651,4 @@
 	 */
 	void flib_netconn_onEngineMessage(flib_netconn *conn, void (*callback)(void *context, const uint8_t *message, size_t size), void *context);
 
-
-// Callbacks only needed for admin stuffs
-
-	/**
-	 * This callback is called if the server informs us that we have admin rights.
-	 */
-	void flib_netconn_onAdminAccess(flib_netconn *conn, void (*callback)(void *context), void *context);
-
 #endif
--- a/project_files/frontlib/net/netconn_callbacks.c	Sun Sep 16 17:42:48 2012 +0200
+++ b/project_files/frontlib/net/netconn_callbacks.c	Sun Sep 16 22:31:34 2012 +0200
@@ -68,6 +68,7 @@
 	flib_netconn_onRoomAdd(conn, NULL, NULL);
 	flib_netconn_onRoomDelete(conn, NULL, NULL);
 	flib_netconn_onRoomUpdate(conn, NULL, NULL);
+	flib_netconn_onClientFlags(conn, NULL, NULL);
 	flib_netconn_onChat(conn, NULL, NULL);
 	flib_netconn_onLobbyJoin(conn, NULL, NULL);
 	flib_netconn_onLobbyLeave(conn, NULL, NULL);
@@ -75,8 +76,6 @@
 	flib_netconn_onRoomLeave(conn, NULL, NULL);
 	flib_netconn_onNickTaken(conn, NULL, NULL);
 	flib_netconn_onPasswordRequest(conn, NULL, NULL);
-	flib_netconn_onRoomChiefStatus(conn, NULL, NULL);
-	flib_netconn_onReadyState(conn, NULL, NULL);
 	flib_netconn_onEnterRoom(conn, NULL, NULL);
 	flib_netconn_onLeaveRoom(conn, NULL, NULL);
 	flib_netconn_onTeamAdd(conn, NULL, NULL);
@@ -90,7 +89,6 @@
 	flib_netconn_onMapChanged(conn, NULL, NULL);
 	flib_netconn_onScriptChanged(conn, NULL, NULL);
 	flib_netconn_onWeaponsetChanged(conn, NULL, NULL);
-	flib_netconn_onAdminAccess(conn, NULL, NULL);
 	flib_netconn_onServerVar(conn, NULL, NULL);
 }
 
@@ -124,6 +122,7 @@
 GENERATE_CB_SETTER_AND_DEFAULT(onRoomAdd, (void *context, const flib_room *room));
 GENERATE_CB_SETTER_AND_DEFAULT(onRoomDelete, (void *context, const char *name));
 GENERATE_CB_SETTER_AND_DEFAULT(onRoomUpdate, (void *context, const char *oldName, const flib_room *room));
+GENERATE_CB_SETTER_AND_DEFAULT(onClientFlags, (void *context, const char *nick, const char *flags, bool newFlagState));
 GENERATE_CB_SETTER(onChat, (void *context, const char *nick, const char *msg), defaultCallback_onChat);
 GENERATE_CB_SETTER_AND_DEFAULT(onLobbyJoin, (void *context, const char *nick));
 GENERATE_CB_SETTER_AND_DEFAULT(onLobbyLeave, (void *context, const char *nick, const char *partMsg));
@@ -131,8 +130,6 @@
 GENERATE_CB_SETTER_AND_DEFAULT(onRoomLeave, (void *context, const char *nick, const char *partMessage));
 GENERATE_CB_SETTER(onNickTaken, (void *context, const char *nick), defaultCallback_onNickTaken);
 GENERATE_CB_SETTER(onPasswordRequest, (void *context, const char *nick), defaultCallback_onPasswordRequest);
-GENERATE_CB_SETTER_AND_DEFAULT(onRoomChiefStatus, (void *context, bool chief));
-GENERATE_CB_SETTER_AND_DEFAULT(onReadyState, (void *context, const char *nick, bool ready));
 GENERATE_CB_SETTER_AND_DEFAULT(onEnterRoom, (void *context, bool chief));
 GENERATE_CB_SETTER_AND_DEFAULT(onLeaveRoom, (void *context, int reason, const char *message));
 GENERATE_CB_SETTER_AND_DEFAULT(onTeamAdd, (void *context, const flib_team *team));
@@ -146,7 +143,6 @@
 GENERATE_CB_SETTER_AND_DEFAULT(onMapChanged, (void *context, const flib_map *map, int changetype));
 GENERATE_CB_SETTER_AND_DEFAULT(onScriptChanged, (void *context, const char *script));
 GENERATE_CB_SETTER_AND_DEFAULT(onWeaponsetChanged, (void *context, const flib_weaponset *weaponset));
-GENERATE_CB_SETTER_AND_DEFAULT(onAdminAccess, (void *context));
 GENERATE_CB_SETTER_AND_DEFAULT(onServerVar, (void *context, const char *name, const char *value));
 
 #undef GENERATE_CB_SETTER_AND_DEFAULT
--- a/project_files/frontlib/net/netconn_internal.h	Sun Sep 16 17:42:48 2012 +0200
+++ b/project_files/frontlib/net/netconn_internal.h	Sun Sep 16 22:31:34 2012 +0200
@@ -41,7 +41,6 @@
 	char *dataDirPath;
 
 	int netconnState;			// One of the NETCONN_STATE constants
-	bool isAdmin;				// Player is server administrator
 
 	bool isChief;				// Player can modify the current room
 	flib_map *map;
@@ -72,6 +71,9 @@
 	void (*onRoomUpdateCb)(void *context, const char *oldName, const flib_room *room);
 	void *onRoomUpdateCtx;
 
+	void (*onClientFlagsCb)(void *context, const char *nick, const char *flags, bool newFlagState);
+	void *onClientFlagsCtx;
+
 	void (*onChatCb)(void *context, const char *nick, const char *msg);
 	void *onChatCtx;
 
@@ -93,12 +95,6 @@
 	void (*onPasswordRequestCb)(void *context, const char *nick);
 	void *onPasswordRequestCtx;
 
-	void (*onRoomChiefStatusCb)(void *context, bool isChief);
-	void *onRoomChiefStatusCtx;
-
-	void (*onReadyStateCb)(void *context, const char *nick, bool ready);
-	void *onReadyStateCtx;
-
 	void (*onEnterRoomCb)(void *context, bool chief);
 	void *onEnterRoomCtx;
 
@@ -138,9 +134,6 @@
 	void (*onWeaponsetChangedCb)(void *context, const flib_weaponset *weaponset);
 	void *onWeaponsetChangedCtx;
 
-	void (*onAdminAccessCb)(void *context);
-	void *onAdminAccessCtx;
-
 	void (*onServerVarCb)(void *context, const char *name, const char *value);
 	void *onServerVarCtx;