refactor: mappings loading

docs: add how to use in README.md
This commit is contained in:
Dmitry Luk committed 2025-03-06 15:22:04 +04:00
1 parent 14a07602ee
commit 03124cb008
2 files changed
+163 -66

No files matched your search

+18
View File
@@ -1,5 +1,23 @@
# Allay platform # Allay platform
## Use
```yaml
# world-settings.yml
worlds:
world:
enable: true
runtime-only: false
storage-type: LEVELDB
overworld:
generator-type: TERRA
generator-preset: pack=overworld;seed=<a number>
nether: null
the-end: null
default-world: world
```
## Resource files ## Resource files
Current mapping version: je 1.21.4 to be 1.21.50 Current mapping version: je 1.21.4 to be 1.21.50
@@ -1,52 +1,61 @@
package com.dfsek.terra.allay; package com.dfsek.terra.allay;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.allaymc.api.block.type.BlockState; import org.allaymc.api.block.type.BlockState;
import org.allaymc.api.block.type.BlockStateSafeGetter; import org.allaymc.api.block.type.BlockStateSafeGetter;
import org.allaymc.api.block.type.BlockStateSafeGetter.Getter;
import org.allaymc.api.block.type.BlockTypes; import org.allaymc.api.block.type.BlockTypes;
import org.allaymc.api.item.type.ItemType; import org.allaymc.api.item.type.ItemType;
import org.allaymc.api.item.type.ItemTypeSafeGetter; import org.allaymc.api.item.type.ItemTypeSafeGetter;
import org.allaymc.api.utils.JSONUtils;
import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Objects;
import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
/** /**
* @author daoge_cmd * @author daoge_cmd
*/ */
public final class Mapping { public final class Mapping {
private static final Gson GSON = new GsonBuilder()
.registerTypeAdapterFactory(new IgnoreFailureTypeAdapterFactory())
.create();
private static final Map<String, Map<String, String>> JE_BLOCK_DEFAULT_PROPERTIES = new Object2ObjectOpenHashMap<>(); private static final Map<String, Map<String, String>> JE_BLOCK_DEFAULT_PROPERTIES = new Object2ObjectOpenHashMap<>();
private static final Map<BlockState, JeBlockState> BLOCK_STATE_BE_TO_JE = new Object2ObjectOpenHashMap<>(); private static final Map<BlockState, JeBlockState> BE_BLOCK_STATE_TO_JE = new Object2ObjectOpenHashMap<>();
private static final Map<Integer, BlockState> BLOCK_STATE_JE_HASH_TO_BE = new Int2ObjectOpenHashMap<>(); private static final Map<Integer, BlockState> JE_BLOCK_STATE_HASH_TO_BE = new Int2ObjectOpenHashMap<>();
private static final Map<String, ItemType<?>> ITEM_ID_JE_TO_BE = new Object2ObjectOpenHashMap<>(); private static final Map<String, ItemType<?>> JE_ITEM_ID_TO_BE = new Object2ObjectOpenHashMap<>();
private static final Map<String, Integer> BIOME_ID_JE_TO_BE = new Object2IntOpenHashMap<>(); private static final Map<String, Integer> JE_BIOME_ID_TO_BE = new Object2IntOpenHashMap<>();
private static final BlockState BE_AIR_STATE = BlockTypes.AIR.getDefaultState(); private static final BlockState BE_AIR_STATE = BlockTypes.AIR.getDefaultState();
public static void init() { public static void init() {
if(!initBlockStateMapping()) error(); if (!initBlockStateMapping()) error();
if(!initJeBlockDefaultProperties()) error(); if (!initJeBlockDefaultProperties()) error();
if(!initItemMapping()) error(); if (!initItemMapping()) error();
if(!initBiomeMapping()) error(); if (!initBiomeMapping()) error();
} }
public static JeBlockState blockStateBeToJe(BlockState beBlockState) { public static JeBlockState blockStateBeToJe(BlockState beBlockState) {
return BLOCK_STATE_BE_TO_JE.get(beBlockState); return BE_BLOCK_STATE_TO_JE.get(beBlockState);
} }
public static BlockState blockStateJeToBe(JeBlockState jeBlockState) { public static BlockState blockStateJeToBe(JeBlockState jeBlockState) {
BlockState result = BLOCK_STATE_JE_HASH_TO_BE.get(jeBlockState.getHash()); BlockState result = JE_BLOCK_STATE_HASH_TO_BE.get(jeBlockState.getHash());
if(result == null) { if (result == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().warn("Failed to find be block state for {}", jeBlockState); TerraAllayPlugin.INSTANCE.getPluginLogger().warn("Failed to find be block state for {}", jeBlockState);
return BE_AIR_STATE; return BE_AIR_STATE;
} }
@@ -54,7 +63,7 @@ public final class Mapping {
} }
public static ItemType<?> itemIdJeToBe(String jeItemId) { public static ItemType<?> itemIdJeToBe(String jeItemId) {
return ITEM_ID_JE_TO_BE.get(jeItemId); return JE_ITEM_ID_TO_BE.get(jeItemId);
} }
// Enchantment identifiers are same in both versions // Enchantment identifiers are same in both versions
@@ -68,15 +77,16 @@ public final class Mapping {
} }
public static int biomeIdJeToBe(String jeBiomeId) { public static int biomeIdJeToBe(String jeBiomeId) {
return BIOME_ID_JE_TO_BE.get(jeBiomeId); return JE_BIOME_ID_TO_BE.get(jeBiomeId);
} }
public static Map<String, String> getJeBlockDefaultProperties(String jeBlockIdentifier) { public static Map<String, String> getJeBlockDefaultProperties(String jeBlockIdentifier) {
Map<String, String> defaultProperties = JE_BLOCK_DEFAULT_PROPERTIES.get(jeBlockIdentifier); var defaultProperties = JE_BLOCK_DEFAULT_PROPERTIES.get(jeBlockIdentifier);
if(defaultProperties == null) { if (defaultProperties == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().warn("Failed to find default properties for {}", jeBlockIdentifier); TerraAllayPlugin.INSTANCE.getPluginLogger().warn("Failed to find default properties for {}", jeBlockIdentifier);
return Map.of(); return Map.of();
} }
return defaultProperties; return defaultProperties;
} }
@@ -85,14 +95,15 @@ public final class Mapping {
} }
private static boolean initBiomeMapping() { private static boolean initBiomeMapping() {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("mapping/biomes.json")) { try (var stream = Mapping.class.getClassLoader().getResourceAsStream("mapping/biomes.json")) {
if(stream == null) { if (stream == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("biomes mapping not found"); TerraAllayPlugin.INSTANCE.getPluginLogger().error("biomes mapping not found");
return false; return false;
} }
Set<Entry<String, Map<String, Integer>>> mappings = JSONUtils.from(stream, new TypeToken<Map<String, Map<String, Integer>>>(){}).entrySet();
mappings.forEach(mapping -> BIOME_ID_JE_TO_BE.put(mapping.getKey(), mapping.getValue().get("bedrock_id"))); Map<String, BiomeMapping> mappings = from(stream, new TypeToken<>() {});
} catch(IOException e) { mappings.forEach((javaId, mapping) -> JE_BIOME_ID_TO_BE.put(javaId, mapping.bedrockId()));
} catch (IOException e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Failed to load biomes mapping", e); TerraAllayPlugin.INSTANCE.getPluginLogger().error("Failed to load biomes mapping", e);
return false; return false;
} }
@@ -100,89 +111,157 @@ public final class Mapping {
} }
private static boolean initItemMapping() { private static boolean initItemMapping() {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("mapping/items.json")) { try (var stream = Mapping.class.getClassLoader().getResourceAsStream("mapping/items.json")) {
if(stream == null) { if (stream == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("items mapping not found"); TerraAllayPlugin.INSTANCE.getPluginLogger().error("items mapping not found");
return false; return false;
} }
Set<Entry<String, Map<String, Object>>> mappings = JSONUtils.from(stream, new TypeToken<Map<String, Map<String, Object>>>() {}).entrySet();
mappings.forEach(mapping -> { Map<String, ItemMapping> mappings = from(stream, new TypeToken<>() {});
ItemType<?> item = ItemTypeSafeGetter mappings.forEach((javaId, mapping) -> {
.name((String) mapping.getValue().get("bedrock_identifier")) var itemType = ItemTypeSafeGetter
// NOTICE: should be cast to double .name(mapping.bedrockId())
.meta(((Double) mapping.getValue().get("bedrock_data")).intValue()) .meta(mapping.bedrockData())
.itemType(); .itemType();
ITEM_ID_JE_TO_BE.put(mapping.getKey(), item); JE_ITEM_ID_TO_BE.put(javaId, itemType);
}); });
} catch(IOException e) { } catch (IOException e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Failed to load items mapping", e); TerraAllayPlugin.INSTANCE.getPluginLogger().error("Failed to load items mapping", e);
return false;
} }
return true; return true;
} }
private static boolean initBlockStateMapping() { private static boolean initBlockStateMapping() {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("mapping/blocks.json")) { try (var stream = Mapping.class.getClassLoader().getResourceAsStream("mapping/blocks.json")) {
if(stream == null) { if (stream == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("blocks mapping not found"); TerraAllayPlugin.INSTANCE.getPluginLogger().error("blocks mapping not found");
return false; return false;
} }
// noinspection unchecked
List<Map<String, Map<String, Object>>> mappings = (List<Map<String, Map<String, Object>>>) JSONUtils.from(stream, new TypeToken<Map<String, Object>>() {}).get("mappings"); Map<String, List<BlockMapping>> root = from(stream, new TypeToken<>() {});
List<BlockMapping> mappings = root.get("mappings");
mappings.forEach(mapping -> { mappings.forEach(mapping -> {
JeBlockState jeState = createJeBlockState(mapping.get("java_state")); var jeState = createJeBlockState(mapping.javaState());
BlockState beState = createBeBlockState(mapping.get("bedrock_state")); var beState = createBeBlockState(mapping.bedrockState());
BLOCK_STATE_BE_TO_JE.put(beState, jeState); BE_BLOCK_STATE_TO_JE.put(beState, jeState);
BLOCK_STATE_JE_HASH_TO_BE.put(jeState.getHash(), beState); JE_BLOCK_STATE_HASH_TO_BE.put(jeState.getHash(), beState);
}); });
} catch(IOException e) { } catch (IOException e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Failed to load blocks mapping", e); TerraAllayPlugin.INSTANCE.getPluginLogger().error("Failed to load blocks mapping", e);
return false;
} }
return true; return true;
} }
private static boolean initJeBlockDefaultProperties() { private static boolean initJeBlockDefaultProperties() {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("je_block_default_states.json")) { try (var stream = Mapping.class.getClassLoader().getResourceAsStream("je_block_default_states.json")) {
if(stream == null) { if (stream == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("je_block_default_states.json not found"); TerraAllayPlugin.INSTANCE.getPluginLogger().error("je_block_default_states.json not found");
return false; return false;
} }
Map<String, Map<String, String>> states = JSONUtils.from(stream, new TypeToken<>() {});
for(Entry<String, Map<String, String>> entry : states.entrySet()) { Map<String, Map<String, String>> states = from(stream, new TypeToken<>() {});
String identifier = entry.getKey(); JE_BLOCK_DEFAULT_PROPERTIES.putAll(states);
Map<String, String> properties = entry.getValue(); } catch (IOException e) {
JE_BLOCK_DEFAULT_PROPERTIES.put(identifier, properties);
}
} catch(IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
return true; return true;
} }
private static BlockState createBeBlockState(Map<String, Object> data) { private static JeBlockState createJeBlockState(BlockMapping.JavaState state) {
Getter getter = BlockStateSafeGetter Map<String, String> properties = state.properties() == null ? Map.of() : state.properties();
.name("minecraft:" + data.get("bedrock_identifier")); return JeBlockState.create(state.name(), new TreeMap<>(properties));
if(data.containsKey("state")) { }
// noinspection unchecked
convertValueType((Map<String, Object>) data.get("state")).forEach(getter::property); private static BlockState createBeBlockState(BlockMapping.BedrockState state) {
var getter = BlockStateSafeGetter.name("minecraft:" + state.bedrockId());
if (state.state() != null) {
convertValueType(state.state()).forEach(getter::property);
} }
return getter.blockState(); return getter.blockState();
} }
private static Map<String, Object> convertValueType(Map<String, Object> data) { private static Map<String, Object> convertValueType(Map<String, Object> data) {
TreeMap<String, Object> result = new TreeMap<>(); Map<String, Object> result = new TreeMap<>();
for(Entry<String, Object> entry : data.entrySet()) { for (var entry : data.entrySet()) {
if(entry.getValue() instanceof Number number) { if (entry.getValue() instanceof Number number) {
// Convert double to int because the number in json is double // Convert double to int because the number in json is double
result.put(entry.getKey(), number.intValue()); result.put(entry.getKey(), number.intValue());
} else { } else {
result.put(entry.getKey(), entry.getValue()); result.put(entry.getKey(), entry.getValue());
} }
} }
return result; return result;
} }
private static JeBlockState createJeBlockState(Map<String, Object> data) { public static <V> V from(InputStream inputStream, TypeToken<V> typeToken) {
// noinspection unchecked var reader = new JsonReader(new InputStreamReader(Objects.requireNonNull(inputStream)));
return JeBlockState.create((String) data.get("Name"), new TreeMap<>((Map<String, String>) data.getOrDefault("Properties", Map.of()))); return GSON.fromJson(reader, typeToken.getType());
}
public record BiomeMapping(
@SerializedName("bedrock_id")
int bedrockId
) {
}
public record ItemMapping(
@SerializedName("bedrock_identifier")
String bedrockId,
@SerializedName("bedrock_data")
int bedrockData
) {
}
public record BlockMapping(
@SerializedName("java_state")
BlockMapping.JavaState javaState,
@SerializedName("bedrock_state")
BlockMapping.BedrockState bedrockState
) {
public record JavaState(
@SerializedName("Name")
String name,
@Nullable
@SerializedName("Properties")
Map<String, String> properties
) {
}
public record BedrockState(
@SerializedName("bedrock_identifier")
String bedrockId,
@Nullable
Map<String, Object> state
) {
}
}
/**
* @see <a href="https://stackoverflow.com/questions/59655279/is-there-an-easy-way-to-make-gson-skip-a-field-if-theres-an-error-deserializing">IgnoreFailureTypeAdapterFactory</a>
*/
public static class IgnoreFailureTypeAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
var delegate = gson.getDelegateAdapter(this, typeToken);
return new TypeAdapter<>() {
@Override
public void write(JsonWriter writer, T value) throws IOException {
delegate.write(writer, value);
}
@Override
public T read(JsonReader reader) throws IOException {
try {
return delegate.read(reader);
} catch (Exception e) {
reader.skipValue();
return null;
}
}
};
}
} }
} }