純規の暇人趣味ブログ

首を突っ込んで足を洗う

[Bukkitプラグイン制作講座-其之七]config.ymlを扱う

      2016/12/13    HimaJyun

最近の蚊は寒いのに元気ですねぇ……

蚊除けのために扇風機を付け、扇風機が寒いから布団に包まる、@HimaJyunです。

さて、「普通のプラグイン」では起動するとconfig.ymlなどが作成されて自分好みに設定や挙動を変えられるようになっています。

Bukkitプラグインではconfig.yml(yamlファイル)の使い方はほぼ必須でしょうし、今回はこれを解説してみましょう。

前回の記事は「[Bukkitプラグイン制作講座-其之六]特定の出来事(イベント)に反応する」です。

また、実際のコードはGitHubにおいてあります。

まずは基本的な使い方から

兎にも角にも基本的なロード、セーブの使い方が分からない事には始まらないでしょう。

とりあえず基本的な読み書きをやってみましょう。

設定ファイルのロード

設定ファイルをメモリ上にロードするのは大半が起動した段階、すなわちonEnableの時だと思われます。

まずは以下の様に、config.ymlをロードしましょう。

@Override
public void onEnable() {
	// config.ymlが存在しない場合はファイルに出力します。
	saveDefaultConfig();
	// config.ymlを読み込みます。
	FileConfiguration config = getConfig();
	// この2つは殆ど定型文の様な物なので覚えておきましょう。
}

値のロード

次に、ロードした設定ファイルからJavaの値(型)として設定値を取り出してみましょう。

config.ymlは以下の様な状態だと仮定しておきましょうか。

Message: "string"
Message2:
  Message3: "string2"
Value1: 1
Value2: 1.1

この時、一番上のMessageを取り出すには以下の様にします。

config.getString("Message");

Message2の中にあるMessage3を取り出すには以下の様にします。

config.getString("Message2.Message3");

Value1をintで取り出す場合には以下の様にします。

config.getInt("Value1");

Value2をdoubleとして取り出す場合には以下の様に

config.getDouble("Value2");

この様に「get型名」と言うメソッドが用意されているので、それらを使用して適切な値を取得する訳です。

もしこれが、Aの中にあるBの中にあるCを取り出す場合には「A.B.C」みたいに、「.」で繋げていくわけですね。

また、文字の大文字小文字は区別される(はず)なので間違えない様に指定しましょう(もしくは、全て小文字で統一する、など。)

値のリロード

良く出来たプラグインだと起動中に設定をリロードする事が出来たりします。

config.ymlの内容はロードした時点でメモリ上に載るので、リロードするからと言って単にもう一度ロードしただけでは更新が反映されません。

きちんともう一度ディスクからロードするように指示する必要があります。

reloadConfig();

リロード出来たら、もう一度getConfig();して、設定をロードするだけです、簡単!!

値のセーブ

何らかの値を設定ファイルに保存する事も可能です。

例えば、上の例で言う「Message2.Message3」を別に値に置き換える時には以下の様にします。

config.set("Message2.Message3", "新しい値");

getの時と違って、setは型を問いません。

Stringでもintでもdoubleでも同じsetが利用出来ます。

また、これでは値を「セットした」だけであって「セーブした」訳ではありません、セットし終わったらセーブする必要があります。

saveConfig();

知ってると便利な少し高度な使い方

次は、知っていると少しだけ便利にconfig.ymlを扱えるいくつかの方法をご紹介しましょう。

取得できなかった場合のデフォルト値

値が設定されていなかった場合など、正常に取得出来なかった場合のデフォルト値を指定する事が可能です。

と言っても簡単、getの第二引数にデフォルト値を指定するだけです。

config.getString("NotExists","デフォルト値");

この場合、当然ながらNotExistsは設定されていないので「デフォルト値」が帰ってきます。

あまり使いまくると設定ミスに気付けなくなったりするので、ほどほどで利用しましょう。

設定があるか確認する

では、設定があるかどうかを確認するにはどうするか、簡単です。containsメソッドを使えば良いのです。

config.contains("NotExists");

この場合、NotExistsは存在していないのでfalseが帰ってきます。

これが、存在しているときにはtrueが帰って来るので、早い話if文で分岐が出来る訳ですね。

設定が特定の型か確認する

「"string"」が設定された設定に対してgetIntは出来ません。

当然です、String(文字列)はint(数値)ではないのですから。

では、取得する前にintかどうか確認したい場合にはどうすれば良いのでしょうか?

簡単です、「is型名(isInt)」を使えば良いのです。

config.isInt("Message");
config.isInt("Value1");

この場合、isInt("Message")はStringでしたのでfalseが、isInt("Value1")はintでしたのでtrueが帰ってきます。

コメントヘッダーを付ける

設定ファイルにコメントヘッダーを付ける事が可能です。

イマイチ使う場面が浮かびませんが、こう言う事が出来る、程度の感覚で知っておくと良いでしょう。

config.options().header("Comment1\nComment2");

コードを見れば分かる通り、改行は\nです。

MapをListっぽく扱う

例えば、以下の様な設定があるとしましょう。

MapKeys:
  Key1: "A"
  Key2: "B"
  Key3: "C"

このKey1,2,3……と言うのは増えるかも知れませんし、もしかすると減るかも知れません。

もしかするとHoge、なんてのが増えるかも知れません、とにかくMapKeysに設定されている値を取得したいとしましょう。

この場合、以下の様にすることで可能です。

for (String key : config.getConfigurationSection("MapKeys").getKeys(false)) {
	getLogger().info(key + config.getString("MapKeys." + key));
}

「getConfigurationSection("MapKeys").getKeys(false)」でKey1,Key2,Key3……が含まれるSet(Listの親戚みたいなの)が取得可能です。

それをforループでぐるぐるまわって、getStringしているだけです。

この手法を使いこなす事が出来ると、かなり複雑なデータ構造を(割と見やすい)yamlだけで取り扱う事が出来ます。

この手法、割と他所のサイトでは載っていない感じなのでぜひ覚えておいてください。

設定をまとめたクラスで使おう

configはイチイチ直接getStringなどするのではなく、あらかじめロードして値をまとめたクラス、C言語風に言えば構造体を用意して、それらを通じて利用するようにすると速度や使いやすさの点でもベストです。

つまり、ざっと説明するとこう言う訳です。

/**
 * 設定をまとめて取り扱う構造体
 * @author HimaJyun
 *
 */
public class Config {

	private final Plugin plugin;
	private FileConfiguration config = null;

	public Config(Plugin plugin) {
		this.plugin = plugin;
		// ロードする
		load();
	}

	/**
	 * 設定をロードします
	 */
	public void load() {
		// 設定ファイルを保存
		plugin.saveDefaultConfig();
		if (config != null) { // configが非null == リロードで呼び出された
			plugin.reloadConfig();
		}
		config = plugin.getConfig();

		// TODO:ここに設定をロードする処理
	}

}

ゲッター/セッターの利用

個人的にはメンバ変数をpublicで持てばいいと思ってるんですけど、一応紹介しましょう。

例えば、以下の様なprivateの変数を用意します。

private String message;

そして、この変数の値を取り出すpublicなgetXXメソッドを定義、値を返します。

public String getMessage() {
	return message;
}

これがゲッターで、こうする事で値の取得しか出来ない(書き換えが出来ない)、リードオンリーな感じにすることが出来ます。(書き換えが発生しないならpublic finalでも構わない)

値の書き換えを許す場合は、publicなsetXXメソッド、セッターを用意します。

public boolean setMessage(String message) {
	// 例えばnullチェックをしたり
	if (message == null) {
		return false;
	}

	// 値をセットした後に自動で保存したり
	this.message = message;
	config.set("Message", message);
	plugin.saveConfig();

	return true;
}

こうする事で、渡された値が正しいかどうかをチェックしたり、今回の場合であれば、設定が変更された後に自動でセーブしたりする事も可能です。

C#だとプロパティと言う機能があって、メソッドを用意しなくても同じ事が出来るんですけどね、今の所Javaにはそれはないみたいで……

個人的な意見ですが、以下の様な場合はゲッター/セッターなんか用意せずにそのまま変数をpublicにすれば良いと思います。

// 以下の様にゲッター/セッターが単純に値の受け渡しをしているだけなら
// 普通にhoge変数をpublicにしてしまえば良いと思う。
private String hoge = "hoge";
public String getHoge() {
	return hoge;
}
public boolean setHoge(String hoge) {
	this.hoge = hoge;
	return true;
}

最後に

最初にも言った通り、Bukkitでのymalの取り扱いは絶対必須の技術でしょう。

とは言え、何でもかんでもyamlで保存すれば良いのかと言うとそう言う訳でもなく、時にはデータベースの方が適している事もあります。

まぁ、データベースは難しいので必要になってからでも構わないでしょう。

今回のコードは、例に依ってGitHubにあります、参考までにどうぞ。

 - プログラミング , ,