[1.15.2] Forge Modding: BlockStateとJsonの相互変換

アバター
kegare
ID: 4Z9DAOEKR8
記事: 11
登録日時: 2020年5月27日(水) 19:48
いいねされた回数: 11回
連絡する:

[1.15.2] Forge Modding: BlockStateとJsonの相互変換

投稿記事 by kegare » 2020年6月04日(木) 15:58

Minecraft 1.15.2ではBlockに数値としてのメタデータが存在しない。
通常であれば特段困らないが、コンフィグで管理したいとき、非常に困る。
というわけで、Gsonを使ってBlockStateのPropertyからJsonへと相互変換するSerializerを作った。

コード: 全て選択

import java.lang.reflect.Type;
import java.util.Map;

import org.apache.commons.lang3.tuple.Pair;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import net.minecraft.block.AirBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.state.IProperty;
import net.minecraft.state.IStateHolder;
import net.minecraft.state.StateContainer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Util;
import net.minecraftforge.registries.ForgeRegistries;

public class BlockStateSerializer implements JsonSerializer<BlockState>, JsonDeserializer<BlockState>
{
	@Override
	public JsonElement serialize(BlockState src, Type typeOfSrc, JsonSerializationContext context)
	{
		JsonObject object = new JsonObject();

		object.addProperty("name", src.getBlock().getRegistryName().toString());

		JsonObject propsObject = new JsonObject();

		for (Map.Entry<IProperty<?>, Comparable<?>> entry : src.getValues().entrySet())
		{
			Pair<String, String> value = getPropertyString(entry);

			propsObject.addProperty(value.getLeft(), value.getRight());
		}

		object.add("properties", propsObject);

		return object;
	}

	@Override
	public BlockState deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
	{
		JsonObject object = json.getAsJsonObject();

		String name = object.get("name").getAsString();
		Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(name));

		if (block == null || block instanceof AirBlock)
		{
			return Blocks.AIR.getDefaultState();
		}

		JsonObject propsObject = object.get("properties").getAsJsonObject();

		BlockState state = block.getDefaultState();
		StateContainer<Block, BlockState> stateContainer = block.getStateContainer();

		for (Map.Entry<String, JsonElement> entry : propsObject.entrySet())
		{
			String key = entry.getKey();
			IProperty<?> prop = stateContainer.getProperty(key);

			if (prop != null)
			{
				String value = entry.getValue().getAsString();

				state = IStateHolder.withString(state, prop, key, value, value);
			}
		}

		return state;
	}

	private Pair<String, String> getPropertyString(Map.Entry<IProperty<?>, Comparable<?>> entry)
	{
		IProperty<?> key = entry.getKey();
		Comparable<?> value = entry.getValue();

		return Pair.of(key.getName(), Util.getValueName(key, value));
	}
}
あとは、Gsonのインスタンスにアダプターとして登録してアレコレするだけ。

コード: 全て選択

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;

import net.minecraft.block.BlockState;

public class BlockStateHelper
{
	private static final Gson GSON = new GsonBuilder().setPrettyPrinting().registerTypeAdapter(BlockState.class, new BlockStateSerializer()).create();

	public static String toJson(BlockState state)
	{
		return GSON.toJson(state);
	}

	public static JsonElement toJsonTree(BlockState state)
	{
		return GSON.toJsonTree(state);
	}

	public static BlockState fromJson(String value)
	{
		return GSON.fromJson(value, BlockState.class);
	}

	public static BlockState fromJson(JsonElement value)
	{
		return GSON.fromJson(value, BlockState.class);
	}
}
1.15.2でしか試していないので、前バージョンでも使えるかは不明。
0



Kotori316
ID: 5SKOIWMOVL
記事: 25
登録日時: 2019年10月28日(月) 15:21
いいね: 1回
いいねされた回数: 5回
連絡する:

Re: [1.15.2] Forge Modding: BlockStateとJsonの相互変換

投稿記事 by Kotori316 » 2020年7月07日(火) 21:47

1.14.4, 1.15.2ではこのような方法もあります。

コード: 全て選択

BlockState.serialize(JsonOps.INSTANCE, state).getValue
JsonOps.INSTANCEをNBTDynamicOps.INSTANCEに変えるとNBTタグが得られます。
Json->BlockStateでは

コード: 全て選択

BlockState.deserialize(new Dynamic<>(JsonOps.INSTANCE, json))
のようなコードを使えます。

1.16.1では

コード: 全て選択

BlockState.field_235877_b_.encodeStart(JsonOps.INSTANCE, state).result().get()
でJsonが得られます。Optionalをチェックなしにgetしているのが気になりますが...
フィールド名はマッピングによって変わるかもしれません。Codec<BlockState>のインスタンスです。
Deserializeは

コード: 全て選択

BlockState.field_235877_b_.decode(JsonOps.INSTANCE, json).result().get().getFirst()
でできます。
0

返信する