All Downloads are FREE. Search and download functionalities are using the official Maven repository.

team.unnamed.creative.serialize.minecraft.blockstate.BlockStateSerializer Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of creative, licensed under the MIT license
 *
 * Copyright (c) 2021-2023 Unnamed Team
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package team.unnamed.creative.serialize.minecraft.blockstate;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.stream.JsonWriter;
import net.kyori.adventure.key.Key;
import team.unnamed.creative.blockstate.BlockState;
import team.unnamed.creative.blockstate.Condition;
import team.unnamed.creative.blockstate.MultiVariant;
import team.unnamed.creative.blockstate.Selector;
import team.unnamed.creative.blockstate.Variant;
import team.unnamed.creative.overlay.ResourceContainer;
import team.unnamed.creative.serialize.minecraft.GsonUtil;
import team.unnamed.creative.serialize.minecraft.ResourceCategory;
import team.unnamed.creative.serialize.minecraft.base.KeySerializer;
import team.unnamed.creative.serialize.minecraft.io.JsonResourceDeserializer;
import team.unnamed.creative.serialize.minecraft.io.JsonResourceSerializer;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public final class BlockStateSerializer implements JsonResourceSerializer, JsonResourceDeserializer {

    public static final BlockStateSerializer INSTANCE;
    public static final ResourceCategory CATEGORY;

    static {
        INSTANCE = new BlockStateSerializer();
        CATEGORY = new ResourceCategory<>(
                "blockstates",
                ".json",
                ResourceContainer::blockState,
                ResourceContainer::blockStates,
                INSTANCE
        );
    }

    @Override
    public void serializeToJson(BlockState state, JsonWriter writer) throws IOException {
        writer.beginObject();

        // write "variants" part if not empty
        Map variants = state.variants();
        if (!variants.isEmpty()) {
            writer.name("variants").beginObject();
            for (Map.Entry entry : variants.entrySet()) {
                writer.name(entry.getKey());
                writeMultiVariant(writer, entry.getValue());
            }
            writer.endObject();
        }

        // write "multipart" part if not empty
        List multipart = state.multipart();
        if (!multipart.isEmpty()) {
            writer.name("multipart").beginArray();
            for (Selector selector : multipart) {
                writeSelector(writer, selector);
            }
            writer.endArray();
        }

        writer.endObject();
    }

    @Override
    public BlockState deserializeFromJson(JsonElement node, Key key) {

        JsonObject objectNode = node.getAsJsonObject();

        Map variants = new LinkedHashMap<>();
        List multipart = new ArrayList<>();

        // read variants
        if (objectNode.has("variants")) {
            JsonObject variantsNode = objectNode.getAsJsonObject("variants");
            for (Map.Entry variantEntry : variantsNode.entrySet()) {
                String variantKey = variantEntry.getKey();
                variants.put(variantKey, readMultiVariant(variantEntry.getValue()));
            }
        }

        // read multipart
        if (objectNode.has("multipart")) {
            for (JsonElement selectorNode : objectNode.getAsJsonArray("multipart")) {
                multipart.add(readSelector(selectorNode));
            }
        }

        return BlockState.of(key, variants, multipart);
    }

    private static void writeMultiVariant(JsonWriter writer, MultiVariant multiVariant) throws IOException {
        List variants = multiVariant.variants();
        if (variants.size() == 1) {
            // single variant, write as an object
            writeVariant(writer, variants.get(0));
        } else {
            // multiple variants, write as an array
            writer.beginArray();
            for (Variant variant : variants) {
                writeVariant(writer, variant);
            }
            writer.endArray();
        }
    }

    private static MultiVariant readMultiVariant(JsonElement node) {
        if (node.isJsonObject()) {
            // single variant
            return MultiVariant.of(readVariant(node.getAsJsonObject()));
        } else {
            // multiple variants
            List variants = new ArrayList<>();
            for (JsonElement variantNode : node.getAsJsonArray()) {
                variants.add(readVariant(variantNode.getAsJsonObject()));
            }
            return MultiVariant.of(variants);
        }
    }

    private static void writeVariant(JsonWriter writer, Variant variant) throws IOException {
        writer
                .beginObject()
                .name("model").value(KeySerializer.toString(variant.model()));
        int x = variant.x();
        if (x != Variant.DEFAULT_X_ROTATION) {
            writer.name("x").value(x);
        }
        int y = variant.y();
        if (y != Variant.DEFAULT_Y_ROTATION) {
            writer.name("y").value(y);
        }
        boolean uvLock = variant.uvLock();
        if (uvLock != Variant.DEFAULT_UV_LOCK) {
            writer.name("uvlock").value(uvLock);
        }
        int weight = variant.weight();
        if (weight != Variant.DEFAULT_WEIGHT) {
            writer.name("weight").value(weight);
        }
        writer.endObject();
    }

    private static Variant readVariant(JsonObject node) {
        return Variant.builder()
                .model(Key.key(node.get("model").getAsString()))
                .x(GsonUtil.getInt(node, "x", Variant.DEFAULT_X_ROTATION))
                .y(GsonUtil.getInt(node, "y", Variant.DEFAULT_Y_ROTATION))
                .uvLock(GsonUtil.getBoolean(node, "uvlock", Variant.DEFAULT_UV_LOCK))
                .weight(GsonUtil.getInt(node, "weight", Variant.DEFAULT_WEIGHT))
                .build();
    }

    private static void writeSelector(JsonWriter writer, Selector selector) throws IOException {
        writer.beginObject();

        Condition condition = selector.condition();
        if (condition != Condition.NONE) {
            writer.name("when").beginObject();
            writeCondition(writer, condition, true);
            writer.endObject();
        }

        writer.name("apply");
        writeMultiVariant(writer, selector.variant());
        writer.endObject();
    }

    private static Selector readSelector(JsonElement node) {
        JsonObject objectNode = node.getAsJsonObject();
        Condition condition = Condition.NONE;
        if (objectNode.has("when")) {
            JsonObject conditionNode = objectNode.getAsJsonObject("when");
            condition = readCondition(conditionNode);
        }
        MultiVariant multiVariant = readMultiVariant(objectNode.get("apply"));
        return Selector.of(condition, multiVariant);
    }

    private static void writeCondition(JsonWriter writer, Condition condition) throws IOException {
        writeCondition(writer, condition, false);
    }

    private static void writeCondition(JsonWriter writer, Condition condition, boolean topLevel) throws IOException {
        if (condition instanceof Condition.Match) {
            Condition.Match match = (Condition.Match) condition;
            writer.name(match.key()).value(match.value().toString());// TODO: don't toString()
        } else if (condition instanceof Condition.Or) {
            List conditions = ((Condition.Or) condition).conditions();
            if (conditions.size() == 1) {
                // single condition, just write it
                writeCondition(writer, conditions.get(0));
                return;
            }

            writer.name("OR").beginArray();
            for (Condition orCondition : conditions) {
                writer.beginObject();
                writeCondition(writer, orCondition);
                writer.endObject();
            }
            writer.endArray();
        } else if (condition instanceof Condition.And) {
            List conditions = ((Condition.And) condition).conditions();
            if (topLevel || conditions.size() == 1) {
                // top level or single condition, direct write
                for (Condition child : conditions) {
                    writeCondition(writer, child);
                }
                return;
            }

            writer.name("AND").beginArray();
            for (Condition child : conditions) {
                writer.beginObject();
                writeCondition(writer, child);
                writer.endObject();
            }
            writer.endArray();
        }
    }

    private static Condition readCondition(JsonObject node) {
        Set> entrySet = node.entrySet();
        if (entrySet.isEmpty()) {
            throw new IllegalStateException("No conditions");
        } else if (entrySet.size() == 1) {
            Map.Entry entry = entrySet.iterator().next();
            JsonElement contentNode = entry.getValue();

            switch (entry.getKey()) {
                case "AND": {
                    List conditions = new ArrayList<>();
                    for (JsonElement element : contentNode.getAsJsonArray()) {
                        // recursive
                        conditions.add(readCondition(element.getAsJsonObject()));
                    }
                    return Condition.and(conditions);
                }
                case "OR": {
                    List conditions = new ArrayList<>();
                    for (JsonElement element : contentNode.getAsJsonArray()) {
                        // recursive
                        conditions.add(readCondition(element.getAsJsonObject()));
                    }
                    return Condition.or(conditions);
                }
                default: {
                    // match (key -> value)
                    return Condition.match(
                            entry.getKey(),
                            contentNode.getAsString()
                    );
                }
            }
        } else {
            // AND everything by match (key -> value)
            List conditions = new ArrayList<>();
            for (Map.Entry entry : entrySet) {
                conditions.add(Condition.match(
                        entry.getKey(),
                        entry.getValue().getAsString()
                ));
            }
            return Condition.and(conditions);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy