Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-11-19 22:40:18 +01:00
Minor cleanup and javadocs
Dieser Commit ist enthalten in:
Ursprung
bfdebadacc
Commit
cc2e409f05
@ -41,7 +41,7 @@ public final class Constants {
|
|||||||
|
|
||||||
static final String SAVED_REFRESH_TOKEN_FILE = "saved-refresh-tokens.json";
|
static final String SAVED_REFRESH_TOKEN_FILE = "saved-refresh-tokens.json";
|
||||||
|
|
||||||
public static final String GEYSER_NAMESPACE = "geyser:";
|
public static final String GEYSER_NAMESPACE = "geyser_custom";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
URI wsUri = null;
|
URI wsUri = null;
|
||||||
|
@ -94,7 +94,7 @@ public class GeyserCustomBlockData implements CustomBlockData {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull String identifier() {
|
public @NonNull String identifier() {
|
||||||
return Constants.GEYSER_NAMESPACE + name;
|
return Constants.GEYSER_NAMESPACE + ":" + name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
import com.google.common.base.CharMatcher;
|
import com.google.common.base.CharMatcher;
|
||||||
|
|
||||||
|
import org.geysermc.geyser.Constants;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||||
import org.geysermc.geyser.api.block.custom.CustomBlockPermutation;
|
import org.geysermc.geyser.api.block.custom.CustomBlockPermutation;
|
||||||
@ -178,6 +179,13 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
return customItemData.build();
|
return customItemData.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a block mapping entry from a JSON node and java identifier
|
||||||
|
* @param identifier The java identifier of the block
|
||||||
|
* @param node The {@link JsonNode} containing the block mapping entry
|
||||||
|
* @return The {@link CustomBlockMapping} record to be read by {@link org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator#registerCustomBedrockBlocks}
|
||||||
|
* @throws InvalidCustomMappingsFileException If the JSON node is invalid
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CustomBlockMapping readBlockMappingEntry(String identifier, JsonNode node) throws InvalidCustomMappingsFileException {
|
public CustomBlockMapping readBlockMappingEntry(String identifier, JsonNode node) throws InvalidCustomMappingsFileException {
|
||||||
if (node == null || !node.isObject()) {
|
if (node == null || !node.isObject()) {
|
||||||
@ -189,6 +197,7 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
throw new InvalidCustomMappingsFileException("A block entry has no name");
|
throw new InvalidCustomMappingsFileException("A block entry has no name");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is true, we will only register the states the user has specified rather than all of the blocks possible states
|
||||||
boolean onlyOverrideStates = node.has("only_override_states") && node.get("only_override_states").asBoolean();
|
boolean onlyOverrideStates = node.has("only_override_states") && node.get("only_override_states").asBoolean();
|
||||||
JsonNode stateOverrides = node.get("state_overrides");
|
JsonNode stateOverrides = node.get("state_overrides");
|
||||||
|
|
||||||
@ -199,6 +208,7 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
|
|
||||||
List<String> stateKeys = new ArrayList<>();
|
List<String> stateKeys = new ArrayList<>();
|
||||||
|
|
||||||
|
// Add the block's identifier to the object so we can use the resulting string to search the block mappings
|
||||||
if (stateOverrides != null && stateOverrides.isObject()) {
|
if (stateOverrides != null && stateOverrides.isObject()) {
|
||||||
Iterator<Map.Entry<String, JsonNode>> fields = stateOverrides.fields();
|
Iterator<Map.Entry<String, JsonNode>> fields = stateOverrides.fields();
|
||||||
while (fields.hasNext()) {
|
while (fields.hasNext()) {
|
||||||
@ -206,54 +216,92 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find all the default states for the block we wish to override
|
||||||
List<String> defaultStates = List.copyOf(BlockRegistries.JAVA_IDENTIFIERS.get().keySet())
|
List<String> defaultStates = List.copyOf(BlockRegistries.JAVA_IDENTIFIERS.get().keySet())
|
||||||
.stream()
|
.stream()
|
||||||
.filter(s -> s.startsWith(identifier + "["))
|
.filter(s -> s.startsWith(identifier + "["))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
// If no states were found, the block must only have one state, so we add its plain identifier
|
||||||
if (defaultStates.isEmpty()) defaultStates.add(identifier);
|
if (defaultStates.isEmpty()) defaultStates.add(identifier);
|
||||||
|
|
||||||
|
// Create the data for the overall block
|
||||||
CustomBlockDataBuilder customBlockDataBuilder = new CustomBlockDataBuilder();
|
CustomBlockDataBuilder customBlockDataBuilder = new CustomBlockDataBuilder();
|
||||||
customBlockDataBuilder.name(name)
|
customBlockDataBuilder.name(name)
|
||||||
|
// We pass in the first state and just use the hitbox from that as the default
|
||||||
|
// Each state will have its own so this is fine
|
||||||
.components(createCustomBlockComponents(node, defaultStates.get(0), name))
|
.components(createCustomBlockComponents(node, defaultStates.get(0), name))
|
||||||
|
// We must create permutation for every state override
|
||||||
.permutations(createCustomBlockPermutations(stateOverrides, identifier, name))
|
.permutations(createCustomBlockPermutations(stateOverrides, identifier, name))
|
||||||
.booleanProperty("geyser_custom:default");
|
// This property is use to display the default state when onlyOverrideStates is false
|
||||||
|
.booleanProperty(String.format("%s:default", Constants.GEYSER_NAMESPACE));
|
||||||
|
|
||||||
|
// We need to have three property type maps, one for each type of block property
|
||||||
|
// Each contains the properties this block has for that type and its possible values, except for boolean since the values must be true/false
|
||||||
|
// If we are only overriding states, we pass in only the state keys supplied in the mapping
|
||||||
|
// Otherwise, we pass in all possible states for the block
|
||||||
BlockPropertyTypeMaps blockPropertyTypeMaps = createBlockPropertyTypeMaps(onlyOverrideStates ? stateKeys : defaultStates);
|
BlockPropertyTypeMaps blockPropertyTypeMaps = createBlockPropertyTypeMaps(onlyOverrideStates ? stateKeys : defaultStates);
|
||||||
blockPropertyTypeMaps.stringValuesMap().forEach((key, value) -> customBlockDataBuilder.stringProperty(key, new ArrayList<String>(value)));
|
blockPropertyTypeMaps.stringValuesMap().forEach((key, value) -> customBlockDataBuilder.stringProperty(key, new ArrayList<String>(value)));
|
||||||
blockPropertyTypeMaps.intValuesMap().forEach((key, value) -> customBlockDataBuilder.intProperty(key, new ArrayList<Integer>(value)));
|
blockPropertyTypeMaps.intValuesMap().forEach((key, value) -> customBlockDataBuilder.intProperty(key, new ArrayList<Integer>(value)));
|
||||||
blockPropertyTypeMaps.booleanValuesSet().forEach((value) -> customBlockDataBuilder.booleanProperty(value));
|
blockPropertyTypeMaps.booleanValuesSet().forEach((value) -> customBlockDataBuilder.booleanProperty(value));
|
||||||
|
|
||||||
|
// Finally, build the custom block data
|
||||||
CustomBlockData customBlockData = customBlockDataBuilder.build();
|
CustomBlockData customBlockData = customBlockDataBuilder.build();
|
||||||
|
|
||||||
Map<String, CustomBlockState> states = createCustomBlockStatesMap(identifier, stateKeys, defaultStates, onlyOverrideStates, customBlockData,
|
// Create a map of the custom block states for this block, which contains the full state identifier mapped to the custom block state data
|
||||||
|
Map<String, CustomBlockState> states = createCustomBlockStatesMap(stateKeys, defaultStates, onlyOverrideStates, customBlockData,
|
||||||
blockPropertyTypeMaps.stateKeyStrings(), blockPropertyTypeMaps.stateKeyInts(), blockPropertyTypeMaps.stateKeyBools());
|
blockPropertyTypeMaps.stateKeyStrings(), blockPropertyTypeMaps.stateKeyInts(), blockPropertyTypeMaps.stateKeyBools());
|
||||||
|
|
||||||
|
// Create the custom block mapping record to be passed into the custom block registry populator
|
||||||
return new CustomBlockMapping(customBlockData, states, identifier, !onlyOverrideStates);
|
return new CustomBlockMapping(customBlockData, states, identifier, !onlyOverrideStates);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a list of {@link CustomBlockPermutation} from the given mappings node containing permutations, java identifier, and custom block name
|
||||||
|
* @param node an {@link JsonNode} from the mappings file containing the permutations
|
||||||
|
* @param identifier the java identifier of the block
|
||||||
|
* @param name the name of the custom block
|
||||||
|
* @return the list of custom block permutations
|
||||||
|
*/
|
||||||
private List<CustomBlockPermutation> createCustomBlockPermutations(JsonNode node, String identifier, String name) {
|
private List<CustomBlockPermutation> createCustomBlockPermutations(JsonNode node, String identifier, String name) {
|
||||||
List<CustomBlockPermutation> permutations = new ArrayList<>();
|
List<CustomBlockPermutation> permutations = new ArrayList<>();
|
||||||
|
|
||||||
|
// Create a custom block permutation record for each permutation passed into the mappings for the given block
|
||||||
if (node != null && node.isObject()) {
|
if (node != null && node.isObject()) {
|
||||||
node.fields().forEachRemaining(entry -> {
|
node.fields().forEachRemaining(entry -> {
|
||||||
String key = entry.getKey();
|
String key = entry.getKey();
|
||||||
JsonNode value = entry.getValue();
|
JsonNode value = entry.getValue();
|
||||||
if (value.isObject()) {
|
if (value.isObject()) {
|
||||||
|
// Each permutation has its own components, which override the base components when explicitly set
|
||||||
|
// Based on the input states, we construct a molang query that will evaluate to true when the block properties corresponding to the state are active
|
||||||
permutations.add(new CustomBlockPermutation(createCustomBlockComponents(value, (identifier + key), name), createCustomBlockPropertyQuery(key)));
|
permutations.add(new CustomBlockPermutation(createCustomBlockComponents(value, (identifier + key), name), createCustomBlockPropertyQuery(key)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
permutations.add(new CustomBlockPermutation(new GeyserCustomBlockComponents.CustomBlockComponentsBuilder().build(), "q.block_property('geyser_custom:default') == 1"));
|
// We also need to create a permutation for the default state of the block with no components
|
||||||
|
// Functionally, this means the default components will be used
|
||||||
|
permutations.add(new CustomBlockPermutation(new GeyserCustomBlockComponents.CustomBlockComponentsBuilder().build(), String.format("q.block_property('%s:default') == 1", Constants.GEYSER_NAMESPACE)));
|
||||||
|
|
||||||
return permutations;
|
return permutations;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, CustomBlockState> createCustomBlockStatesMap(String identifier, List<String> stateKeys,List<String> defaultStates, boolean onlyOverrideStates, CustomBlockData customBlockData,
|
/**
|
||||||
|
* Create a map of java block state identifiers to {@link CustomBlockState} so that {@link #readBlockMappingEntry} can include it in the {@link CustomBlockMapping} record
|
||||||
|
* @param stateKeys the list of java block state identifiers explicitly passed in the mappings
|
||||||
|
* @param defaultStates the list of all possible java block state identifiers for the block
|
||||||
|
* @param onlyOverrideStates whether or not we are only overriding the states passed in the mappings
|
||||||
|
* @param customBlockData the {@link CustomBlockData} for the block
|
||||||
|
* @param stateKeyStrings the map of java block state identifiers to their string properties
|
||||||
|
* @param stateKeyInts the map of java block state identifiers to their int properties
|
||||||
|
* @param stateKeyBools the map of java block state identifiers to their boolean properties
|
||||||
|
* @return the custom block states maps
|
||||||
|
*/
|
||||||
|
private Map<String, CustomBlockState> createCustomBlockStatesMap(List<String> stateKeys,List<String> defaultStates, boolean onlyOverrideStates, CustomBlockData customBlockData,
|
||||||
Map<String, Map<String, String>> stateKeyStrings, Map<String, Map<String, Integer>> stateKeyInts, Map<String, Map<String, Boolean>> stateKeyBools) {
|
Map<String, Map<String, String>> stateKeyStrings, Map<String, Map<String, Integer>> stateKeyInts, Map<String, Map<String, Boolean>> stateKeyBools) {
|
||||||
|
|
||||||
Map<String, CustomBlockState> states = new HashMap<>();
|
Map<String, CustomBlockState> states = new HashMap<>();
|
||||||
|
|
||||||
|
// If not only overriding specified states, we must include the default states in the custom block states map
|
||||||
if (!onlyOverrideStates) {
|
if (!onlyOverrideStates) {
|
||||||
defaultStates.removeAll(stateKeys);
|
defaultStates.removeAll(stateKeys);
|
||||||
createCustomBlockStates(defaultStates, true, customBlockData, stateKeyStrings, stateKeyInts, stateKeyBools, states);
|
createCustomBlockStates(defaultStates, true, customBlockData, stateKeyStrings, stateKeyInts, stateKeyBools, states);
|
||||||
@ -263,13 +311,25 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
return states;
|
return states;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the custom block states for the given state keys and append them to the passed states map
|
||||||
|
* @param stateKeys the list of java block state identifiers
|
||||||
|
* @param defaultState whether or not this is the default state
|
||||||
|
* @param customBlockData the {@link CustomBlockData} for the block
|
||||||
|
* @param stateKeyStrings the map of java block state identifiers to their string properties
|
||||||
|
* @param stateKeyInts the map of java block state identifiers to their int properties
|
||||||
|
* @param stateKeyBools the map of java block state identifiers to their boolean properties
|
||||||
|
* @param states the map of java block state identifiers to their {@link CustomBlockState} to append
|
||||||
|
*/
|
||||||
private void createCustomBlockStates(List<String> stateKeys, boolean defaultState, CustomBlockData customBlockData,
|
private void createCustomBlockStates(List<String> stateKeys, boolean defaultState, CustomBlockData customBlockData,
|
||||||
Map<String, Map<String, String>> stateKeyStrings, Map<String, Map<String, Integer>> stateKeyInts,
|
Map<String, Map<String, String>> stateKeyStrings, Map<String, Map<String, Integer>> stateKeyInts,
|
||||||
Map<String, Map<String, Boolean>> stateKeyBools, Map<String, CustomBlockState> states) {
|
Map<String, Map<String, Boolean>> stateKeyBools, Map<String, CustomBlockState> states) {
|
||||||
stateKeys.forEach((key) -> {
|
stateKeys.forEach((key) -> {
|
||||||
CustomBlockState.Builder builder = customBlockData.blockStateBuilder();
|
CustomBlockState.Builder builder = customBlockData.blockStateBuilder();
|
||||||
builder.booleanProperty("geyser_custom:default", defaultState);
|
// We always include the default property, which is used to set the default state when onlyOverrideStates is false
|
||||||
|
builder.booleanProperty(String.format("%s:default", Constants.GEYSER_NAMESPACE), defaultState);
|
||||||
|
|
||||||
|
// The properties must be added to the builder seperately for each type
|
||||||
stateKeyStrings.getOrDefault(key, Collections.emptyMap()).forEach((property, stringValue) -> builder.stringProperty(property, stringValue));
|
stateKeyStrings.getOrDefault(key, Collections.emptyMap()).forEach((property, stringValue) -> builder.stringProperty(property, stringValue));
|
||||||
stateKeyInts.getOrDefault(key, Collections.emptyMap()).forEach((property, intValue) -> builder.intProperty(property, intValue));
|
stateKeyInts.getOrDefault(key, Collections.emptyMap()).forEach((property, intValue) -> builder.intProperty(property, intValue));
|
||||||
stateKeyBools.getOrDefault(key, Collections.emptyMap()).forEach((property, boolValue) -> builder.booleanProperty(property, boolValue));
|
stateKeyBools.getOrDefault(key, Collections.emptyMap()).forEach((property, boolValue) -> builder.booleanProperty(property, boolValue));
|
||||||
@ -280,7 +340,15 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a record of {@link BlockPropertyTypeMaps} for the given list of java block state identifiers that are being actively used by the custom block
|
||||||
|
* @param usedStateKeys the list of java block state identifiers that are being actively used by the custom block
|
||||||
|
* @return the {@link BlockPropertyTypeMaps} record
|
||||||
|
*/
|
||||||
private BlockPropertyTypeMaps createBlockPropertyTypeMaps(List<String> usedStateKeys) {
|
private BlockPropertyTypeMaps createBlockPropertyTypeMaps(List<String> usedStateKeys) {
|
||||||
|
// Each of the three property type has two maps
|
||||||
|
// The first map is used to store the possible values for each property
|
||||||
|
// The second map is used to store the value for each property for each state
|
||||||
Map<String, LinkedHashSet<String>> stringValuesMap = new HashMap<>();
|
Map<String, LinkedHashSet<String>> stringValuesMap = new HashMap<>();
|
||||||
Map<String, Map<String, String>> stateKeyStrings = new HashMap<>();
|
Map<String, Map<String, String>> stateKeyStrings = new HashMap<>();
|
||||||
|
|
||||||
@ -292,14 +360,19 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
|
|
||||||
|
|
||||||
for (String state : usedStateKeys) {
|
for (String state : usedStateKeys) {
|
||||||
|
// No bracket means that there is only one state, so the maps should be empty
|
||||||
if (!state.contains("[")) continue;
|
if (!state.contains("[")) continue;
|
||||||
|
|
||||||
|
// Split the state string into an array containing each property=value pair
|
||||||
String[] pairs = splitStateString(state);
|
String[] pairs = splitStateString(state);
|
||||||
|
|
||||||
for (String pair : pairs) {
|
for (String pair : pairs) {
|
||||||
|
// Get the property and value individually
|
||||||
String[] parts = pair.split("=");
|
String[] parts = pair.split("=");
|
||||||
String property = parts[0];
|
String property = parts[0];
|
||||||
String value = parts[1];
|
String value = parts[1];
|
||||||
|
|
||||||
|
// Figure out what property type we are dealing with
|
||||||
if (value.equals("true") || value.equals("false")) {
|
if (value.equals("true") || value.equals("false")) {
|
||||||
booleanValuesSet.add(property);
|
booleanValuesSet.add(property);
|
||||||
Map<String, Boolean> propertyMap = stateKeyBools.getOrDefault(state, new HashMap<>());
|
Map<String, Boolean> propertyMap = stateKeyBools.getOrDefault(state, new HashMap<>());
|
||||||
@ -308,6 +381,7 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
} else if (CharMatcher.inRange('0', '9').matchesAllOf(value)) {
|
} else if (CharMatcher.inRange('0', '9').matchesAllOf(value)) {
|
||||||
int intValue = Integer.parseInt(value);
|
int intValue = Integer.parseInt(value);
|
||||||
LinkedHashSet<Integer> values = intValuesMap.get(property);
|
LinkedHashSet<Integer> values = intValuesMap.get(property);
|
||||||
|
// Initialize the property to values map if it doesn't exist
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
values = new LinkedHashSet<>();
|
values = new LinkedHashSet<>();
|
||||||
intValuesMap.put(property, values);
|
intValuesMap.put(property, values);
|
||||||
@ -317,7 +391,9 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
propertyMap.put(property, intValue);
|
propertyMap.put(property, intValue);
|
||||||
stateKeyInts.put(state, propertyMap);
|
stateKeyInts.put(state, propertyMap);
|
||||||
} else {
|
} else {
|
||||||
|
// If it's n not a boolean or int it must be a string
|
||||||
LinkedHashSet<String> values = stringValuesMap.get(property);
|
LinkedHashSet<String> values = stringValuesMap.get(property);
|
||||||
|
// Initialize the property to values map if it doesn't exist
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
values = new LinkedHashSet<>();
|
values = new LinkedHashSet<>();
|
||||||
stringValuesMap.put(property, values);
|
stringValuesMap.put(property, values);
|
||||||
@ -329,14 +405,22 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// We should now have all of the maps
|
||||||
return new BlockPropertyTypeMaps(stringValuesMap, stateKeyStrings, intValuesMap, stateKeyInts, booleanValuesSet, stateKeyBools);
|
return new BlockPropertyTypeMaps(stringValuesMap, stateKeyStrings, intValuesMap, stateKeyInts, booleanValuesSet, stateKeyBools);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link CustomBlockComponents} object for the passed permutation or base block node, java block state identifier, and custom block name
|
||||||
|
* @param node the permutation or base block {@link JsonNode}
|
||||||
|
* @param stateKey the java block state identifier
|
||||||
|
* @param name the custom block name
|
||||||
|
* @return the {@link CustomBlockComponents} object
|
||||||
|
*/
|
||||||
private CustomBlockComponents createCustomBlockComponents(JsonNode node, String stateKey, String name) {
|
private CustomBlockComponents createCustomBlockComponents(JsonNode node, String stateKey, String name) {
|
||||||
|
// This is needed to find the correct selection box for the given block
|
||||||
int id = BlockRegistries.JAVA_IDENTIFIERS.getOrDefault(stateKey, -1);
|
int id = BlockRegistries.JAVA_IDENTIFIERS.getOrDefault(stateKey, -1);
|
||||||
|
|
||||||
CustomBlockComponentsBuilder builder = new CustomBlockComponentsBuilder();
|
CustomBlockComponentsBuilder builder = new CustomBlockComponentsBuilder();
|
||||||
// .friction()
|
|
||||||
BoxComponent boxComponent = createBoxComponent(id);
|
BoxComponent boxComponent = createBoxComponent(id);
|
||||||
builder.selectionBox(boxComponent).collisionBox(boxComponent);
|
builder.selectionBox(boxComponent).collisionBox(boxComponent);
|
||||||
|
|
||||||
@ -358,6 +442,10 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
}
|
}
|
||||||
builder.displayName(displayName);
|
builder.displayName(displayName);
|
||||||
|
|
||||||
|
if (node.has("friction")) {
|
||||||
|
builder.friction(node.get("friction").floatValue());
|
||||||
|
}
|
||||||
|
|
||||||
if (node.has("light_emission")) {
|
if (node.has("light_emission")) {
|
||||||
builder.lightEmission(node.get("light_emission").asInt());
|
builder.lightEmission(node.get("light_emission").asInt());
|
||||||
}
|
}
|
||||||
@ -411,6 +499,11 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tags can be applied so that blocks will match return true when queried for the tag
|
||||||
|
// Potentially useful for resource pack creators
|
||||||
|
// Ideally we could programatically extract the tags here https://wiki.bedrock.dev/blocks/block-tags.html
|
||||||
|
// This would let us automatically apply the correct valilla tags to blocks
|
||||||
|
// However, its worth noting that vanilla tools do not currently honor these tags anyways
|
||||||
if (node.has("tags")) {
|
if (node.has("tags")) {
|
||||||
JsonNode tags = node.get("tags");
|
JsonNode tags = node.get("tags");
|
||||||
if (tags.isArray()) {
|
if (tags.isArray()) {
|
||||||
@ -426,6 +519,11 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
return components;
|
return components;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the {@link BoxComponent} for the passed collision box index
|
||||||
|
* @param id the collision box index
|
||||||
|
* @return the {@link BoxComponent}
|
||||||
|
*/
|
||||||
private BoxComponent createBoxComponent(int id) {
|
private BoxComponent createBoxComponent(int id) {
|
||||||
BoundingBox boundingBox = BlockUtils.getCollision(id).getBoundingBoxes()[0];
|
BoundingBox boundingBox = BlockUtils.getCollision(id).getBoundingBoxes()[0];
|
||||||
|
|
||||||
@ -433,6 +531,11 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
float offsetY = (float) boundingBox.getSizeY() * 8;
|
float offsetY = (float) boundingBox.getSizeY() * 8;
|
||||||
float offsetZ = (float) boundingBox.getSizeZ() * 8;
|
float offsetZ = (float) boundingBox.getSizeZ() * 8;
|
||||||
|
|
||||||
|
// Unfortunately we need to clamp the values here to a an effective size of one block
|
||||||
|
// This is quite a pain for anything like fences, as the player can just jump over them
|
||||||
|
// One possible solution would be to create invisible blocks that we use only for collision box
|
||||||
|
// These could be placed above the block when a custom block exceeds this limit
|
||||||
|
// I am hopeful this will be extended slightly since the geometry of blocks can be 1.875^3
|
||||||
float cornerX = clamp((float) boundingBox.getMiddleX() * 16 - 8 - offsetX, -8, 8);
|
float cornerX = clamp((float) boundingBox.getMiddleX() * 16 - 8 - offsetX, -8, 8);
|
||||||
float cornerY = clamp((float) boundingBox.getMiddleY() * 16 - offsetY, 0, 16);
|
float cornerY = clamp((float) boundingBox.getMiddleY() * 16 - offsetY, 0, 16);
|
||||||
float cornerZ = clamp((float) boundingBox.getMiddleZ() * 16 - 8 - offsetZ, -8, 8);
|
float cornerZ = clamp((float) boundingBox.getMiddleZ() * 16 - 8 - offsetZ, -8, 8);
|
||||||
@ -446,7 +549,15 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
return boxComponent;
|
return boxComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the {@link MaterialInstance} for the passed material instance node and custom block name
|
||||||
|
* The name is used as a fallback if no texture is provided by the node
|
||||||
|
* @param node the material instance node
|
||||||
|
* @param name the custom block name
|
||||||
|
* @return the {@link MaterialInstance}
|
||||||
|
*/
|
||||||
private MaterialInstance createMaterialInstanceComponent(JsonNode node, String name) {
|
private MaterialInstance createMaterialInstanceComponent(JsonNode node, String name) {
|
||||||
|
// Set default values, and use what the user provides if they have provided something
|
||||||
String texture = name;
|
String texture = name;
|
||||||
if (node.has("texture")) {
|
if (node.has("texture")) {
|
||||||
texture = node.get("texture").asText();
|
texture = node.get("texture").asText();
|
||||||
@ -470,9 +581,16 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
return new MaterialInstance(texture, renderMethod, faceDimming, ambientOcclusion);
|
return new MaterialInstance(texture, renderMethod, faceDimming, ambientOcclusion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the {@link PlacementFilter} for the passed conditions node
|
||||||
|
* @param node the conditions node
|
||||||
|
* @return the {@link PlacementFilter}
|
||||||
|
*/
|
||||||
private PlacementFilter createPlacementFilterComponent(JsonNode node) {
|
private PlacementFilter createPlacementFilterComponent(JsonNode node) {
|
||||||
List<Conditions> conditions = new ArrayList<>();
|
List<Conditions> conditions = new ArrayList<>();
|
||||||
|
|
||||||
|
// The structure of the placement filter component is the most complex of the current components
|
||||||
|
// Each condition effectively seperated into an two arrays: one of allowed faces, and one of blocks/block molang queries
|
||||||
node.forEach(condition -> {
|
node.forEach(condition -> {
|
||||||
Set<Face> faces = new HashSet<>();
|
Set<Face> faces = new HashSet<>();
|
||||||
if (condition.has("allowed_faces")) {
|
if (condition.has("allowed_faces")) {
|
||||||
@ -507,7 +625,13 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
return new PlacementFilter(conditions);
|
return new PlacementFilter(conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a molang query that returns true when the given java state identifier is the active state
|
||||||
|
* @param state the java state identifier
|
||||||
|
* @return the molang query
|
||||||
|
*/
|
||||||
private String createCustomBlockPropertyQuery(String state) {
|
private String createCustomBlockPropertyQuery(String state) {
|
||||||
|
// This creates a molang query from the given input blockstate string
|
||||||
String[] conditions = splitStateString(state);
|
String[] conditions = splitStateString(state);
|
||||||
String[] queries = new String[conditions.length];
|
String[] queries = new String[conditions.length];
|
||||||
|
|
||||||
@ -527,10 +651,17 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
|
|
||||||
String query = String.join(" && ", queries);
|
String query = String.join(" && ", queries);
|
||||||
|
|
||||||
return String.format("q.block_property('geyser_custom:default') == 0 && %s", query);
|
// Appends the default property to ensure it can be disabled when a state without specific overrides is active
|
||||||
|
return String.format("q.block_property('%1$s:default') == 0 && %2$s", Constants.GEYSER_NAMESPACE, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits the given java state identifier into an array of property=value pairs
|
||||||
|
* @param state the java state identifier
|
||||||
|
* @return the array of property=value pairs
|
||||||
|
*/
|
||||||
private String[] splitStateString(String state) {
|
private String[] splitStateString(String state) {
|
||||||
|
// Split the given state string into an array of property=value pairs
|
||||||
int openBracketIndex = state.indexOf("[");
|
int openBracketIndex = state.indexOf("[");
|
||||||
int closeBracketIndex = state.indexOf("]");
|
int closeBracketIndex = state.indexOf("]");
|
||||||
|
|
||||||
@ -541,6 +672,13 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
return pairs;
|
return pairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clamps the given value between the given min and max
|
||||||
|
* @param value the value to clamp
|
||||||
|
* @param min the minimum value
|
||||||
|
* @param max the maximum value
|
||||||
|
* @return the clamped value
|
||||||
|
*/
|
||||||
private float clamp(float value, float min, float max) {
|
private float clamp(float value, float min, float max) {
|
||||||
return Math.max(min, Math.min(max, value));
|
return Math.max(min, Math.min(max, value));
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,9 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|||||||
|
|
||||||
public class CustomBlockRegistryPopulator {
|
public class CustomBlockRegistryPopulator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers all custom blocks defined by extensions and user supplied mappings
|
||||||
|
*/
|
||||||
public static void registerCustomBedrockBlocks() {
|
public static void registerCustomBedrockBlocks() {
|
||||||
if (!GeyserImpl.getInstance().getConfig().isAddCustomBlocks()) {
|
if (!GeyserImpl.getInstance().getConfig().isAddCustomBlocks()) {
|
||||||
return;
|
return;
|
||||||
@ -69,7 +72,6 @@ public class CustomBlockRegistryPopulator {
|
|||||||
}
|
}
|
||||||
CustomBlockState oldBlockState = blockStateOverrides.put(id, customBlockState);
|
CustomBlockState oldBlockState = blockStateOverrides.put(id, customBlockState);
|
||||||
if (oldBlockState != null) {
|
if (oldBlockState != null) {
|
||||||
// TODO should this be an error? Allow extensions to query block state overrides?
|
|
||||||
GeyserImpl.getInstance().getLogger().debug("Duplicate block state override for Java Identifier: " +
|
GeyserImpl.getInstance().getLogger().debug("Duplicate block state override for Java Identifier: " +
|
||||||
javaIdentifier + " Old override: " + oldBlockState.name() + " New override: " + customBlockState.name());
|
javaIdentifier + " Old override: " + oldBlockState.name() + " New override: " + customBlockState.name());
|
||||||
}
|
}
|
||||||
@ -101,15 +103,23 @@ public class CustomBlockRegistryPopulator {
|
|||||||
});
|
});
|
||||||
|
|
||||||
BlockRegistries.CUSTOM_BLOCKS.set(customBlocks.toArray(new CustomBlockData[0]));
|
BlockRegistries.CUSTOM_BLOCKS.set(customBlocks.toArray(new CustomBlockData[0]));
|
||||||
GeyserImpl.getInstance().getLogger().debug("Registered " + customBlocks.size() + " custom blocks.");
|
GeyserImpl.getInstance().getLogger().info("Registered " + customBlocks.size() + " custom blocks.");
|
||||||
|
|
||||||
BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.set(blockStateOverrides);
|
BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.set(blockStateOverrides);
|
||||||
GeyserImpl.getInstance().getLogger().debug("Registered " + blockStateOverrides.size() + " custom block overrides.");
|
GeyserImpl.getInstance().getLogger().info("Registered " + blockStateOverrides.size() + " custom block overrides.");
|
||||||
|
|
||||||
BlockRegistries.CUSTOM_BLOCK_ITEM_OVERRIDES.set(customBlockItemOverrides);
|
BlockRegistries.CUSTOM_BLOCK_ITEM_OVERRIDES.set(customBlockItemOverrides);
|
||||||
GeyserImpl.getInstance().getLogger().debug("Registered " + customBlockItemOverrides.size() + " custom block item overrides.");
|
GeyserImpl.getInstance().getLogger().info("Registered " + customBlockItemOverrides.size() + " custom block item overrides.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates and appends all custom block states to the provided list of custom block states
|
||||||
|
* Appends the custom block states to the provided list of NBT maps
|
||||||
|
* @param customBlock the custom block data to generate states for
|
||||||
|
* @param blockStates the list of NBT maps to append the custom block states to
|
||||||
|
* @param customExtBlockStates the list of custom block states to append the custom block states to
|
||||||
|
* @param stateVersion the state version to use for the custom block states
|
||||||
|
*/
|
||||||
static void generateCustomBlockStates(CustomBlockData customBlock, List<NbtMap> blockStates, List<CustomBlockState> customExtBlockStates, int stateVersion) {
|
static void generateCustomBlockStates(CustomBlockData customBlock, List<NbtMap> blockStates, List<CustomBlockState> customExtBlockStates, int stateVersion) {
|
||||||
int totalPermutations = 1;
|
int totalPermutations = 1;
|
||||||
for (CustomBlockProperty<?> property : customBlock.properties().values()) {
|
for (CustomBlockProperty<?> property : customBlock.properties().values()) {
|
||||||
@ -134,6 +144,12 @@ public class CustomBlockRegistryPopulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates and returns the block property data for the provided custom block
|
||||||
|
* @param customBlock the custom block to generate block property data for
|
||||||
|
* @param protocolVersion the protocol version to use for the block property data
|
||||||
|
* @return the block property data for the provided custom block
|
||||||
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
static BlockPropertyData generateBlockPropertyData(CustomBlockData customBlock, int protocolVersion) {
|
static BlockPropertyData generateBlockPropertyData(CustomBlockData customBlock, int protocolVersion) {
|
||||||
List<NbtMap> permutations = new ArrayList<>();
|
List<NbtMap> permutations = new ArrayList<>();
|
||||||
@ -161,11 +177,15 @@ public class CustomBlockRegistryPopulator {
|
|||||||
|
|
||||||
NbtMap propertyTag = NbtMap.builder()
|
NbtMap propertyTag = NbtMap.builder()
|
||||||
.putCompound("components", CustomBlockRegistryPopulator.convertComponents(customBlock.components(), protocolVersion))
|
.putCompound("components", CustomBlockRegistryPopulator.convertComponents(customBlock.components(), protocolVersion))
|
||||||
|
// this is required or the client will crash
|
||||||
|
// in the future, this can be used to replace items in the creative inventory
|
||||||
|
// this would require us to map https://wiki.bedrock.dev/documentation/creative-categories.html#for-blocks programatically
|
||||||
.putCompound("menu_category", NbtMap.builder()
|
.putCompound("menu_category", NbtMap.builder()
|
||||||
.putString("category", "none")
|
.putString("category", "none")
|
||||||
.putString("group", "")
|
.putString("group", "")
|
||||||
.putBoolean("is_hidden_in_commands", false)
|
.putBoolean("is_hidden_in_commands", false)
|
||||||
.build())
|
.build())
|
||||||
|
// meaning of this version is unknown, but it's required for tags to work and should probably be checked periodically
|
||||||
.putInt("molangVersion", 1)
|
.putInt("molangVersion", 1)
|
||||||
.putList("permutations", NbtType.COMPOUND, permutations)
|
.putList("permutations", NbtType.COMPOUND, permutations)
|
||||||
.putList("properties", NbtType.COMPOUND, properties)
|
.putList("properties", NbtType.COMPOUND, properties)
|
||||||
@ -173,6 +193,12 @@ public class CustomBlockRegistryPopulator {
|
|||||||
return new BlockPropertyData(customBlock.identifier(), propertyTag);
|
return new BlockPropertyData(customBlock.identifier(), propertyTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the provided custom block components to an {@link NbtMap} to be sent to the client in the StartGame packet
|
||||||
|
* @param components the custom block components to convert
|
||||||
|
* @param protocolVersion the protocol version to use for the conversion
|
||||||
|
* @return the NBT representation of the provided custom block components
|
||||||
|
*/
|
||||||
static NbtMap convertComponents(CustomBlockComponents components, int protocolVersion) {
|
static NbtMap convertComponents(CustomBlockComponents components, int protocolVersion) {
|
||||||
if (components == null) {
|
if (components == null) {
|
||||||
return NbtMap.EMPTY;
|
return NbtMap.EMPTY;
|
||||||
@ -206,6 +232,9 @@ public class CustomBlockRegistryPopulator {
|
|||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
builder.putCompound("minecraft:material_instances", NbtMap.builder()
|
builder.putCompound("minecraft:material_instances", NbtMap.builder()
|
||||||
|
// we could read these, but there is no functional reason to use them at the moment
|
||||||
|
// they only allow you to make aliases for material instances
|
||||||
|
// but you could already just define the same instance twice if this was really needed
|
||||||
.putCompound("mappings", NbtMap.EMPTY)
|
.putCompound("mappings", NbtMap.EMPTY)
|
||||||
.putCompound("materials", materialsBuilder.build())
|
.putCompound("materials", materialsBuilder.build())
|
||||||
.build());
|
.build());
|
||||||
@ -230,6 +259,9 @@ public class CustomBlockRegistryPopulator {
|
|||||||
.putInt("value", components.lightEmission())
|
.putInt("value", components.lightEmission())
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
// This is supposed to be sent as "light_dampening" since "block_light_filter" is the old value
|
||||||
|
// However, it seems they forgot to actually update it on the network despite all the documentation changing
|
||||||
|
// So we'll send this for now
|
||||||
if (components.lightDampening() != null) {
|
if (components.lightDampening() != null) {
|
||||||
builder.putCompound("minecraft:block_light_filter", NbtMap.builder()
|
builder.putCompound("minecraft:block_light_filter", NbtMap.builder()
|
||||||
.putByte("value", components.lightDampening().byteValue())
|
.putByte("value", components.lightDampening().byteValue())
|
||||||
@ -245,6 +277,9 @@ public class CustomBlockRegistryPopulator {
|
|||||||
if (components.unitCube()) {
|
if (components.unitCube()) {
|
||||||
builder.putCompound("minecraft:unit_cube", NbtMap.EMPTY);
|
builder.putCompound("minecraft:unit_cube", NbtMap.EMPTY);
|
||||||
}
|
}
|
||||||
|
// place_air is not an actual component
|
||||||
|
// We just apply a dummy event to prevent the client from trying to place a block
|
||||||
|
// This mitigates the issue with the client sometimes double placing blocks
|
||||||
if (components.placeAir()) {
|
if (components.placeAir()) {
|
||||||
builder.putCompound("minecraft:on_player_placing", NbtMap.builder()
|
builder.putCompound("minecraft:on_player_placing", NbtMap.builder()
|
||||||
.putString("triggerType", "geyser:place_event")
|
.putString("triggerType", "geyser:place_event")
|
||||||
@ -256,6 +291,11 @@ public class CustomBlockRegistryPopulator {
|
|||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the provided box component to an {@link NbtMap}
|
||||||
|
* @param boxComponent the box component to convert
|
||||||
|
* @return the NBT representation of the provided box component
|
||||||
|
*/
|
||||||
private static NbtMap convertBox(BoxComponent boxComponent) {
|
private static NbtMap convertBox(BoxComponent boxComponent) {
|
||||||
return NbtMap.builder()
|
return NbtMap.builder()
|
||||||
.putBoolean("enabled", !boxComponent.isEmpty())
|
.putBoolean("enabled", !boxComponent.isEmpty())
|
||||||
@ -264,20 +304,30 @@ public class CustomBlockRegistryPopulator {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the provided placement filter to a list of {@link NbtMap}
|
||||||
|
* @param placementFilter the placement filter to convert
|
||||||
|
* @return the NBT representation of the provided placement filter
|
||||||
|
*/
|
||||||
private static List<NbtMap> convertPlacementFilter(PlacementFilter placementFilter) {
|
private static List<NbtMap> convertPlacementFilter(PlacementFilter placementFilter) {
|
||||||
List<NbtMap> conditions = new ArrayList<>();
|
List<NbtMap> conditions = new ArrayList<>();
|
||||||
placementFilter.conditions().forEach((condition) -> {
|
placementFilter.conditions().forEach((condition) -> {
|
||||||
NbtMapBuilder conditionBuilder = NbtMap.builder();
|
NbtMapBuilder conditionBuilder = NbtMap.builder();
|
||||||
|
|
||||||
|
// allowed_faces on the network is represented by 6 bits for the 6 possible faces
|
||||||
|
// the enum has the proper values for that face only, so we just bitwise OR them together
|
||||||
byte allowedFaces = 0;
|
byte allowedFaces = 0;
|
||||||
for (Face face : condition.allowedFaces()) { allowedFaces |= face.getValue(); }
|
for (Face face : condition.allowedFaces()) { allowedFaces |= face.getValue(); }
|
||||||
conditionBuilder.putByte("allowed_faces", allowedFaces);
|
conditionBuilder.putByte("allowed_faces", allowedFaces);
|
||||||
|
|
||||||
|
// block_filters is a list of either blocks or queries for block tags
|
||||||
|
// if these match the block the player is trying to place on, the placement is allowed by the client
|
||||||
List <NbtMap> blockFilters = new ArrayList<>();
|
List <NbtMap> blockFilters = new ArrayList<>();
|
||||||
condition.blockFilters().forEach((value, type) -> {
|
condition.blockFilters().forEach((value, type) -> {
|
||||||
NbtMapBuilder blockFilterBuilder = NbtMap.builder();
|
NbtMapBuilder blockFilterBuilder = NbtMap.builder();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case BLOCK -> blockFilterBuilder.putString("name", value);
|
case BLOCK -> blockFilterBuilder.putString("name", value);
|
||||||
|
// meaning of this version is unknown, but it's required for tags to work and should probably be checked periodically
|
||||||
case TAG -> blockFilterBuilder.putString("tags", value).putInt("tags_version", 6);
|
case TAG -> blockFilterBuilder.putString("tags", value).putInt("tags_version", 6);
|
||||||
}
|
}
|
||||||
blockFilters.add(blockFilterBuilder.build());
|
blockFilters.add(blockFilterBuilder.build());
|
||||||
|
@ -38,6 +38,7 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.Constants;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||||
@ -375,6 +376,9 @@ public class ItemRegistryPopulator {
|
|||||||
// and the last, if relevant. We then iterate over all those values and get their Bedrock equivalents
|
// and the last, if relevant. We then iterate over all those values and get their Bedrock equivalents
|
||||||
Integer lastBlockRuntimeId = entry.getValue().getLastBlockRuntimeId() == null ? firstBlockRuntimeId : entry.getValue().getLastBlockRuntimeId();
|
Integer lastBlockRuntimeId = entry.getValue().getLastBlockRuntimeId() == null ? firstBlockRuntimeId : entry.getValue().getLastBlockRuntimeId();
|
||||||
for (int i = firstBlockRuntimeId; i <= lastBlockRuntimeId; i++) {
|
for (int i = firstBlockRuntimeId; i <= lastBlockRuntimeId; i++) {
|
||||||
|
// For now we opt to preserve the pre custom block phase and just use the vanilla blocks in the creative inventory
|
||||||
|
// In the future if we get the mappings for categories it we could put the custom blocks in the creative inventory
|
||||||
|
// This would likely also require changes to recipe handling
|
||||||
int bedrockBlockRuntimeId = blockMappings.getVanillaBedrockBlockId(i);
|
int bedrockBlockRuntimeId = blockMappings.getVanillaBedrockBlockId(i);
|
||||||
NbtMap blockTag = blockMappings.getBedrockBlockStates().get(bedrockBlockRuntimeId);
|
NbtMap blockTag = blockMappings.getBedrockBlockStates().get(bedrockBlockRuntimeId);
|
||||||
String bedrockName = blockTag.getString("name");
|
String bedrockName = blockTag.getString("name");
|
||||||
@ -504,7 +508,7 @@ public class ItemRegistryPopulator {
|
|||||||
for (CustomItemData customItem : customItemsToLoad) {
|
for (CustomItemData customItem : customItemsToLoad) {
|
||||||
int customProtocolId = nextFreeBedrockId++;
|
int customProtocolId = nextFreeBedrockId++;
|
||||||
|
|
||||||
String customItemName = "geyser_custom:" + customItem.name();
|
String customItemName = Constants.GEYSER_NAMESPACE + ":" + customItem.name();
|
||||||
if (!registeredItemNames.add(customItemName)) {
|
if (!registeredItemNames.add(customItemName)) {
|
||||||
if (firstMappingsPass) {
|
if (firstMappingsPass) {
|
||||||
GeyserImpl.getInstance().getLogger().error("Custom item name '" + customItem.name() + "' already exists and was registered again! Skipping...");
|
GeyserImpl.getInstance().getLogger().error("Custom item name '" + customItem.name() + "' already exists and was registered again! Skipping...");
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren