func (l *pktHandler) handle() {
	var err, clientErr error

	defer func() {
		if err != nil {
			log.Print("Connection closed ", err.Error())
			if clientErr == nil {
				clientErr = clientErrGeneral
			}
			proto.WriteDisconnect(l.conn, clientErr.Error())
			l.conn.Close()
		}
	}()

	err = proto.ServerReadPacketExpect(l.conn, l, []byte{
		proto.PacketIdHandshake,
		proto.PacketIdServerListPing,
	})
	if err != nil {
		clientErr = clientErrLoginGeneral
		return
	}

	switch l.connType {
	case connTypeLogin:
		err, clientErr = l.handleLogin(l.conn)
	case connTypeServerQuery:
		err, clientErr = l.handleServerQuery(l.conn)
	default:
		err = loginErrorConnType
	}
}
func (l *pktHandler) handleLogin(conn net.Conn) (err, clientErr error) {
	if !validPlayerUsername.MatchString(l.username) {
		err = clientErrUsername
		clientErr = err
		return
	}

	log.Print("Client ", conn.RemoteAddr(), " connected as ", l.username)

	// TODO Allow admins to connect.
	if l.gameInfo.maintenanceMsg != "" {
		err = loginErrorMaintenance
		clientErr = errors.New(l.gameInfo.maintenanceMsg)
		return
	}

	// Load player permissions.
	permissions := gamerules.Permissions.UserPermissions(l.username)
	if !permissions.Has("login") {
		err = fmt.Errorf("Player %q does not have login permission", l.username)
		clientErr = clientErrLoginDenied
		return
	}

	if err = proto.ServerWriteHandshake(conn, l.gameInfo.serverId); err != nil {
		clientErr = clientErrHandshake
		return
	}

	if l.gameInfo.serverId != "-" {
		var authenticated bool
		authenticated, err = l.gameInfo.authserver.Authenticate(l.gameInfo.serverId, l.username)
		if !authenticated || err != nil {
			var reason string
			if err != nil {
				reason = "Authentication check failed: " + err.Error()
			} else {
				reason = "Failed authentication"
			}
			err = fmt.Errorf("Client %v: %s", conn.RemoteAddr(), reason)
			clientErr = clientErrAuthFailed
			return
		}
		log.Print("Client ", conn.RemoteAddr(), " passed minecraft.net authentication")
	}

	err = proto.ServerReadPacketExpect(conn, l, []byte{
		proto.PacketIdLogin,
	})
	if err != nil {
		clientErr = clientErrLoginGeneral
		return
	}

	entityId := l.gameInfo.entityManager.NewEntity()

	var playerData *nbt.Compound
	if playerData, err = l.gameInfo.game.worldStore.PlayerData(l.username); err != nil {
		clientErr = clientErrUserData
		return
	}

	player := player.NewPlayer(entityId, l.gameInfo.shardManager, conn, l.username, l.gameInfo.worldStore.SpawnPosition, l.gameInfo.game.playerDisconnect, l.gameInfo.game)
	if playerData != nil {
		if err = player.UnmarshalNbt(playerData); err != nil {
			// Don't let the player log in, as they will only have default inventory
			// etc., which could lose items from them. Better for an administrator to
			// sort this out.
			err = fmt.Errorf("Error parsing player data for %q: %v", l.username, err)
			clientErr = clientErrUserData
			return
		}
	}

	l.gameInfo.game.playerConnect <- player
	player.Run()

	return
}