Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-27 00:23:03 +01:00
Remove skull translation events and define custom blocks for custom skulls
Clean up skull block translation a bit
Dieser Commit ist enthalten in:
Ursprung
f553dee3a5
Commit
49f7f6d2f9
@ -38,6 +38,8 @@ import java.util.Map;
|
|||||||
public interface CustomBlockData {
|
public interface CustomBlockData {
|
||||||
@NonNull String name();
|
@NonNull String name();
|
||||||
|
|
||||||
|
@NonNull String identifier();
|
||||||
|
|
||||||
CustomBlockComponents components();
|
CustomBlockComponents components();
|
||||||
|
|
||||||
@NonNull Map<String, CustomBlockProperty<?>> properties();
|
@NonNull Map<String, CustomBlockProperty<?>> properties();
|
||||||
|
@ -25,5 +25,5 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.api.block.custom.component;
|
package org.geysermc.geyser.api.block.custom.component;
|
||||||
|
|
||||||
public record RotationComponent(float x, float y, float z) {
|
public record RotationComponent(int x, int y, int z) {
|
||||||
}
|
}
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @author GeyserMC
|
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.geysermc.geyser.api.event;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class GeyserConvertSkullInventoryEvent implements Event {
|
|
||||||
private final String skinHash;
|
|
||||||
|
|
||||||
private CustomBlockData replacementBlock;
|
|
||||||
|
|
||||||
public void replaceWithBlock(CustomBlockData customBlockData) {
|
|
||||||
this.replacementBlock = customBlockData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String skinHash() {
|
|
||||||
return skinHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CustomBlockData replacementBlock() {
|
|
||||||
return replacementBlock;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,118 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @author GeyserMC
|
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.geysermc.geyser.api.event.world;
|
|
||||||
|
|
||||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
|
||||||
import org.geysermc.geyser.api.event.Cancellable;
|
|
||||||
import org.geysermc.geyser.api.event.Event;
|
|
||||||
|
|
||||||
public class GeyserConvertSkullEvent implements Event, Cancellable {
|
|
||||||
private final int x;
|
|
||||||
private final int y;
|
|
||||||
private final int z;
|
|
||||||
|
|
||||||
private final boolean onFloor;
|
|
||||||
|
|
||||||
private final WallDirection wallDirection;
|
|
||||||
private final int floorDirection;
|
|
||||||
|
|
||||||
private final String skinHash;
|
|
||||||
|
|
||||||
private boolean cancelled;
|
|
||||||
|
|
||||||
private CustomBlockState newBlockState;
|
|
||||||
|
|
||||||
public GeyserConvertSkullEvent(int x, int y, int z, boolean onFloor, WallDirection wallDirection, int floorDirection, String skinHash) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
this.onFloor = onFloor;
|
|
||||||
this.wallDirection = wallDirection;
|
|
||||||
this.floorDirection = floorDirection;
|
|
||||||
|
|
||||||
if (onFloor && (wallDirection != WallDirection.INVALID || floorDirection == -1)) {
|
|
||||||
throw new IllegalArgumentException("Skull can't be on the floor and wall at the same time");
|
|
||||||
} else if (!onFloor && (wallDirection == WallDirection.INVALID || floorDirection != -1)) {
|
|
||||||
throw new IllegalArgumentException("Skull can't be on the floor and wall at the same time");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.skinHash = skinHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int x() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int y() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int z() {
|
|
||||||
return z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onFloor() {
|
|
||||||
return onFloor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WallDirection wallDirection() {
|
|
||||||
return wallDirection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int floorDirection() {
|
|
||||||
return floorDirection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String skinHash() {
|
|
||||||
return skinHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void replaceWithBlock(CustomBlockState blockState) {
|
|
||||||
this.newBlockState = blockState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CustomBlockState getNewBlockState() {
|
|
||||||
return newBlockState;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCancelled() {
|
|
||||||
return cancelled;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCancelled(boolean cancelled) {
|
|
||||||
this.cancelled = cancelled;
|
|
||||||
}
|
|
||||||
public enum WallDirection {
|
|
||||||
NORTH,
|
|
||||||
EAST,
|
|
||||||
SOUTH,
|
|
||||||
WEST,
|
|
||||||
INVALID
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -52,6 +52,11 @@ public class GeyserCustomBlockData implements CustomBlockData {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull String identifier() {
|
||||||
|
return "geyser:" + name;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CustomBlockComponents components() {
|
public CustomBlockComponents components() {
|
||||||
return components;
|
return components;
|
||||||
|
@ -34,6 +34,7 @@ import org.geysermc.geyser.registry.loader.RegistryLoaders;
|
|||||||
import org.geysermc.geyser.registry.populator.BlockRegistryPopulator;
|
import org.geysermc.geyser.registry.populator.BlockRegistryPopulator;
|
||||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||||
import org.geysermc.geyser.registry.type.BlockMappings;
|
import org.geysermc.geyser.registry.type.BlockMappings;
|
||||||
|
import org.geysermc.geyser.registry.type.CustomSkull;
|
||||||
import org.geysermc.geyser.util.collection.Object2IntBiMap;
|
import org.geysermc.geyser.util.collection.Object2IntBiMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,6 +79,12 @@ public class BlockRegistries {
|
|||||||
*/
|
*/
|
||||||
public static final ArrayRegistry<CustomBlockData> CUSTOM_BLOCKS = ArrayRegistry.create(RegistryLoaders.empty(() -> new CustomBlockData[] {}));
|
public static final ArrayRegistry<CustomBlockData> CUSTOM_BLOCKS = ArrayRegistry.create(RegistryLoaders.empty(() -> new CustomBlockData[] {}));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A registry which stores skin texture hashes to custom skull blocks.
|
||||||
|
* TODO add loader/populator
|
||||||
|
*/
|
||||||
|
public static final SimpleMappedRegistry<String, CustomSkull> CUSTOM_SKULLS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
|
||||||
|
|
||||||
static {
|
static {
|
||||||
BlockRegistryPopulator.populate();
|
BlockRegistryPopulator.populate();
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ import org.geysermc.geyser.level.physics.PistonBehavior;
|
|||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||||
import org.geysermc.geyser.registry.type.BlockMappings;
|
import org.geysermc.geyser.registry.type.BlockMappings;
|
||||||
|
import org.geysermc.geyser.registry.type.CustomSkull;
|
||||||
import org.geysermc.geyser.util.BlockUtils;
|
import org.geysermc.geyser.util.BlockUtils;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
@ -78,8 +79,6 @@ public class BlockRegistryPopulator {
|
|||||||
*/
|
*/
|
||||||
private static JsonNode BLOCKS_JSON;
|
private static JsonNode BLOCKS_JSON;
|
||||||
|
|
||||||
public static final String CUSTOM_PREFIX = "";
|
|
||||||
|
|
||||||
public static void populate() {
|
public static void populate() {
|
||||||
registerJavaBlocks();
|
registerJavaBlocks();
|
||||||
registerCustomBedrockBlocks();
|
registerCustomBedrockBlocks();
|
||||||
@ -96,15 +95,19 @@ public class BlockRegistryPopulator {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
for (CustomSkull customSkull : BlockRegistries.CUSTOM_SKULLS.get().values()) {
|
||||||
|
customBlocks.add(customSkull.getCustomBlockData());
|
||||||
|
}
|
||||||
|
|
||||||
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().debug("Registered " + customBlocks.size() + " custom blocks.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void generateCustomBlockStates(CustomBlockData customBlock, List<NbtMap> blockStates, List<CustomBlockState> customExtBlockStates, int stateVersion) {
|
private static void generateCustomBlockStates(CustomBlockData customBlock, List<NbtMap> blockStates, List<CustomBlockState> customExtBlockStates, int stateVersion) {
|
||||||
String name = CUSTOM_PREFIX + customBlock.name();
|
|
||||||
if (customBlock.properties().isEmpty()) {
|
if (customBlock.properties().isEmpty()) {
|
||||||
blockStates.add(NbtMap.builder()
|
blockStates.add(NbtMap.builder()
|
||||||
.putString("name", name)
|
.putString("name", customBlock.identifier())
|
||||||
.putInt("version", stateVersion)
|
.putInt("version", stateVersion)
|
||||||
.putCompound("states", NbtMap.EMPTY)
|
.putCompound("states", NbtMap.EMPTY)
|
||||||
.build());
|
.build());
|
||||||
@ -116,7 +119,6 @@ public class BlockRegistryPopulator {
|
|||||||
|
|
||||||
for (int i = 0; i < totalPermutations; i++) {
|
for (int i = 0; i < totalPermutations; i++) {
|
||||||
NbtMapBuilder statesBuilder = NbtMap.builder();
|
NbtMapBuilder statesBuilder = NbtMap.builder();
|
||||||
|
|
||||||
int permIndex = i;
|
int permIndex = i;
|
||||||
for (CustomBlockProperty<?> property : customBlock.properties().values()) {
|
for (CustomBlockProperty<?> property : customBlock.properties().values()) {
|
||||||
statesBuilder.put(property.name(), property.values().get(permIndex % property.values().size()));
|
statesBuilder.put(property.name(), property.values().get(permIndex % property.values().size()));
|
||||||
@ -125,11 +127,11 @@ public class BlockRegistryPopulator {
|
|||||||
NbtMap states = statesBuilder.build();
|
NbtMap states = statesBuilder.build();
|
||||||
|
|
||||||
blockStates.add(NbtMap.builder()
|
blockStates.add(NbtMap.builder()
|
||||||
.putString("name", name)
|
.putString("name", customBlock.identifier())
|
||||||
.putInt("version", stateVersion)
|
.putInt("version", stateVersion)
|
||||||
.putCompound("states", states)
|
.putCompound("states", states)
|
||||||
.build());
|
.build());
|
||||||
customExtBlockStates.add(new GeyserCustomBlockState(name, states));
|
customExtBlockStates.add(new GeyserCustomBlockState(customBlock.name(), states));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +170,7 @@ public class BlockRegistryPopulator {
|
|||||||
.putList("permutations", NbtType.COMPOUND, permutations)
|
.putList("permutations", NbtType.COMPOUND, permutations)
|
||||||
.putList("properties", NbtType.COMPOUND, properties)
|
.putList("properties", NbtType.COMPOUND, properties)
|
||||||
.build();
|
.build();
|
||||||
return new BlockPropertyData(CUSTOM_PREFIX + customBlock.name(), propertyTag);
|
return new BlockPropertyData(customBlock.identifier(), propertyTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerBedrockBlocks() {
|
private static void registerBedrockBlocks() {
|
||||||
|
@ -642,7 +642,7 @@ public class ItemRegistryPopulator {
|
|||||||
customBlockItemIds = new Object2IntOpenHashMap<>();
|
customBlockItemIds = new Object2IntOpenHashMap<>();
|
||||||
for (CustomBlockData customBlock : BlockRegistries.CUSTOM_BLOCKS.get()) {
|
for (CustomBlockData customBlock : BlockRegistries.CUSTOM_BLOCKS.get()) {
|
||||||
int customProtocolId = nextFreeBedrockId++;
|
int customProtocolId = nextFreeBedrockId++;
|
||||||
String identifier = BlockRegistryPopulator.CUSTOM_PREFIX + customBlock.name();
|
String identifier = customBlock.identifier();
|
||||||
|
|
||||||
entries.put(identifier, new StartGamePacket.ItemEntry(identifier, (short) customProtocolId));
|
entries.put(identifier, new StartGamePacket.ItemEntry(identifier, (short) customProtocolId));
|
||||||
customBlockItemIds.put(customBlock, customProtocolId);
|
customBlockItemIds.put(customBlock, customProtocolId);
|
||||||
|
171
core/src/main/java/org/geysermc/geyser/registry/type/CustomSkull.java
Normale Datei
171
core/src/main/java/org/geysermc/geyser/registry/type/CustomSkull.java
Normale Datei
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
package org.geysermc.geyser.registry.type;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockPermutation;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.BoxComponent;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.MaterialInstance;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.RotationComponent;
|
||||||
|
import org.geysermc.geyser.level.block.GeyserCustomBlockComponents;
|
||||||
|
import org.geysermc.geyser.level.block.GeyserCustomBlockData;
|
||||||
|
import org.geysermc.geyser.level.block.GeyserCustomBlockPermutation;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class CustomSkull {
|
||||||
|
private final String skinHash;
|
||||||
|
|
||||||
|
private CustomBlockData customBlockData;
|
||||||
|
|
||||||
|
private static final String DIRECTION_PROPERTY = "davchoo:direction";
|
||||||
|
private static final String TYPE_PROPERTY = "davchoo:type";
|
||||||
|
|
||||||
|
private static final int[] ROTATIONS = {0, -90, 180, 90};
|
||||||
|
|
||||||
|
public CustomSkull(String skinHash) {
|
||||||
|
this.skinHash = skinHash;
|
||||||
|
|
||||||
|
CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder()
|
||||||
|
.destroyTime(1.5f)
|
||||||
|
.materialInstances(Map.of("*", new MaterialInstance("davchoo." + skinHash + "_player_skin", "alpha_test", true, true)))
|
||||||
|
.lightFilter(0)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
List<CustomBlockPermutation> permutations = new ArrayList<>();
|
||||||
|
addDefaultPermutation(permutations);
|
||||||
|
addFloorPermutations(permutations);
|
||||||
|
addWallPermutations(permutations);
|
||||||
|
|
||||||
|
customBlockData = new GeyserCustomBlockData.CustomBlockDataBuilder()
|
||||||
|
.name("player_head_" + skinHash)
|
||||||
|
.components(components)
|
||||||
|
.intProperty(DIRECTION_PROPERTY, IntStream.rangeClosed(0, 15).boxed().toList())
|
||||||
|
.intProperty(TYPE_PROPERTY, IntStream.rangeClosed(0, 2).boxed().toList())
|
||||||
|
.permutations(permutations)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomBlockState getDefaultBlockState() {
|
||||||
|
return customBlockData.blockStateBuilder()
|
||||||
|
.intProperty(DIRECTION_PROPERTY, 0)
|
||||||
|
.intProperty(TYPE_PROPERTY, 0)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomBlockState getWallBlockState(int wallDirection) {
|
||||||
|
wallDirection = switch (wallDirection) {
|
||||||
|
case 0 -> 2; // South
|
||||||
|
case 90 -> 3; // West
|
||||||
|
case 180 -> 0; // North
|
||||||
|
case 270 -> 1; // East
|
||||||
|
default -> throw new IllegalArgumentException("Unknown skull wall direction: " + wallDirection);
|
||||||
|
};
|
||||||
|
|
||||||
|
return customBlockData.blockStateBuilder()
|
||||||
|
.intProperty(DIRECTION_PROPERTY, wallDirection)
|
||||||
|
.intProperty(TYPE_PROPERTY, 1)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomBlockState getFloorBlockState(int floorRotation) {
|
||||||
|
return customBlockData.blockStateBuilder()
|
||||||
|
.intProperty(DIRECTION_PROPERTY, floorRotation)
|
||||||
|
.intProperty(TYPE_PROPERTY, 2)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDefaultPermutation(List<CustomBlockPermutation> permutations) {
|
||||||
|
CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder()
|
||||||
|
.geometry("geometry.davchoo.player_head_hand")
|
||||||
|
.rotation(new RotationComponent(0, 180, 0))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String condition = String.format("query.block_property('%s') == 0 && query.block_property('%s') == 0", DIRECTION_PROPERTY, TYPE_PROPERTY);
|
||||||
|
permutations.add(new GeyserCustomBlockPermutation.CustomBlockPermutationBuilder()
|
||||||
|
.condition(condition)
|
||||||
|
.components(components)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addFloorPermutations(List<CustomBlockPermutation> permutations) {
|
||||||
|
BoxComponent box = new BoxComponent(
|
||||||
|
-4, 0, -4,
|
||||||
|
8, 8, 8
|
||||||
|
);
|
||||||
|
|
||||||
|
String[] quadrantNames = {"a", "b", "c", "d"};
|
||||||
|
|
||||||
|
for (int quadrant = 0; quadrant < 4; quadrant++) {
|
||||||
|
RotationComponent rotation = new RotationComponent(0, ROTATIONS[quadrant], 0);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
int floorDirection = 4 * quadrant + i;
|
||||||
|
CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder()
|
||||||
|
.aimCollision(box)
|
||||||
|
.entityCollision(box)
|
||||||
|
.geometry("geometry.davchoo.player_head_floor_" + quadrantNames[i])
|
||||||
|
.rotation(rotation)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String condition = String.format("query.block_property('%s') == %d && query.block_property('%s') == %d", DIRECTION_PROPERTY, floorDirection, TYPE_PROPERTY, 2);
|
||||||
|
permutations.add(new GeyserCustomBlockPermutation.CustomBlockPermutationBuilder()
|
||||||
|
.condition(condition)
|
||||||
|
.components(components)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addWallPermutations(List<CustomBlockPermutation> permutations) {
|
||||||
|
BoxComponent box = new BoxComponent(
|
||||||
|
-4, 4, 0,
|
||||||
|
8, 8, 8
|
||||||
|
);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
RotationComponent rotation = new RotationComponent(0, ROTATIONS[i], 0);
|
||||||
|
String condition = String.format("query.block_property('%s') == %d && query.block_property('%s') == %d", DIRECTION_PROPERTY, i, TYPE_PROPERTY, 1);
|
||||||
|
|
||||||
|
CustomBlockComponents components = new GeyserCustomBlockComponents.CustomBlockComponentsBuilder()
|
||||||
|
.aimCollision(box)
|
||||||
|
.entityCollision(box)
|
||||||
|
.geometry("geometry.davchoo.player_head_wall")
|
||||||
|
.rotation(rotation)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
permutations.add(new GeyserCustomBlockPermutation.CustomBlockPermutationBuilder()
|
||||||
|
.condition(condition)
|
||||||
|
.components(components)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1469,7 +1469,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
startGamePacket.setItemEntries(this.itemMappings.getItemEntries());
|
startGamePacket.setItemEntries(this.itemMappings.getItemEntries());
|
||||||
startGamePacket.getBlockProperties().addAll(this.blockMappings.getBlockProperties());
|
startGamePacket.getBlockProperties().addAll(this.blockMappings.getBlockProperties());
|
||||||
|
|
||||||
startGamePacket.getExperiments().add(new ExperimentData("vanilla_experiments", true));
|
|
||||||
startGamePacket.getExperiments().add(new ExperimentData("data_driven_items", true));
|
startGamePacket.getExperiments().add(new ExperimentData("data_driven_items", true));
|
||||||
startGamePacket.getExperiments().add(new ExperimentData("upcoming_creator_features", true));
|
startGamePacket.getExperiments().add(new ExperimentData("upcoming_creator_features", true));
|
||||||
startGamePacket.getExperiments().add(new ExperimentData("experimental_molang_features", true));
|
startGamePacket.getExperiments().add(new ExperimentData("experimental_molang_features", true));
|
||||||
|
@ -27,15 +27,20 @@ package org.geysermc.geyser.session.cache;
|
|||||||
|
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||||
import org.geysermc.geyser.entity.type.player.SkullPlayerEntity;
|
import org.geysermc.geyser.entity.type.player.SkullPlayerEntity;
|
||||||
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
|
import org.geysermc.geyser.registry.type.CustomSkull;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.level.block.entity.SkullBlockEntityTranslator;
|
import org.geysermc.geyser.skin.SkinManager;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class SkullCache {
|
public class SkullCache {
|
||||||
@ -73,20 +78,33 @@ public class SkullCache {
|
|||||||
this.skullRenderDistanceSquared = distance * distance;
|
this.skullRenderDistanceSquared = distance * distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void putSkull(Vector3i position, String texturesProperty, int blockState) {
|
public Skull putSkull(Vector3i position, String texturesProperty, int blockState) {
|
||||||
Skull skull = skulls.computeIfAbsent(position, Skull::new);
|
Skull skull = skulls.computeIfAbsent(position, Skull::new);
|
||||||
|
if (!texturesProperty.equals(skull.texturesProperty)) {
|
||||||
skull.texturesProperty = texturesProperty;
|
skull.texturesProperty = texturesProperty;
|
||||||
|
try {
|
||||||
|
SkinManager.GameProfileData gameProfileData = SkinManager.GameProfileData.loadFromJson(texturesProperty);
|
||||||
|
if (gameProfileData != null && gameProfileData.skinUrl() != null) {
|
||||||
|
String skinUrl = gameProfileData.skinUrl();
|
||||||
|
skull.skinHash = skinUrl.substring(skinUrl.lastIndexOf('/') + 1);
|
||||||
|
} else {
|
||||||
|
session.getGeyser().getLogger().debug("Player skull with invalid Skin tag: " + position + " Textures: " + texturesProperty);
|
||||||
|
skull.skinHash = null;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
session.getGeyser().getLogger().debug("Player skull with invalid Skin tag: " + position + " Textures: " + texturesProperty);
|
||||||
|
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
skull.skinHash = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
skull.blockState = blockState;
|
skull.blockState = blockState;
|
||||||
if (skull.customRuntimeId != -1) {
|
skull.customRuntimeId = translateCustomSkull(skull.skinHash, blockState);
|
||||||
skull.customRuntimeId = -1;
|
|
||||||
|
|
||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
if (skull.customRuntimeId != -1) {
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
reassignSkullEntity(skull);
|
||||||
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
return skull;
|
||||||
updateBlockPacket.setBlockPosition(position);
|
|
||||||
updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(blockState));
|
|
||||||
updateBlockPacket.setDataLayer(0);
|
|
||||||
session.sendUpstreamPacket(updateBlockPacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (skull.entity != null) {
|
if (skull.entity != null) {
|
||||||
@ -94,10 +112,10 @@ public class SkullCache {
|
|||||||
} else {
|
} else {
|
||||||
if (!cullingEnabled) {
|
if (!cullingEnabled) {
|
||||||
assignSkullEntity(skull);
|
assignSkullEntity(skull);
|
||||||
return;
|
return skull;
|
||||||
}
|
}
|
||||||
if (lastPlayerPosition == null) {
|
if (lastPlayerPosition == null) {
|
||||||
return;
|
return skull;
|
||||||
}
|
}
|
||||||
skull.distanceSquared = position.distanceSquared(lastPlayerPosition.getX(), lastPlayerPosition.getY(), lastPlayerPosition.getZ());
|
skull.distanceSquared = position.distanceSquared(lastPlayerPosition.getX(), lastPlayerPosition.getY(), lastPlayerPosition.getZ());
|
||||||
if (skull.distanceSquared < skullRenderDistanceSquared) {
|
if (skull.distanceSquared < skullRenderDistanceSquared) {
|
||||||
@ -117,54 +135,22 @@ public class SkullCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return skull;
|
||||||
|
|
||||||
public void putSkull(Vector3i position, String texturesProperty, int blockState, int customRuntimeId) {
|
|
||||||
Skull skull = skulls.computeIfAbsent(position, Skull::new);
|
|
||||||
skull.texturesProperty = texturesProperty;
|
|
||||||
skull.blockState = blockState;
|
|
||||||
skull.customRuntimeId = customRuntimeId;
|
|
||||||
|
|
||||||
boolean hadEntity = skull.entity != null;
|
|
||||||
freeSkullEntity(skull);
|
|
||||||
|
|
||||||
if (cullingEnabled) {
|
|
||||||
inRangeSkulls.remove(skull);
|
|
||||||
if (hadEntity && inRangeSkulls.size() >= maxVisibleSkulls) {
|
|
||||||
// Reassign entity to the closest skull without an entity
|
|
||||||
assignSkullEntity(inRangeSkulls.get(maxVisibleSkulls - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeSkull(Vector3i position) {
|
public void removeSkull(Vector3i position) {
|
||||||
Skull skull = skulls.remove(position);
|
Skull skull = skulls.remove(position);
|
||||||
if (skull != null) {
|
if (skull != null) {
|
||||||
boolean hadEntity = skull.entity != null;
|
reassignSkullEntity(skull);
|
||||||
freeSkullEntity(skull);
|
|
||||||
|
|
||||||
if (cullingEnabled) {
|
|
||||||
inRangeSkulls.remove(skull);
|
|
||||||
if (hadEntity && inRangeSkulls.size() >= maxVisibleSkulls) {
|
|
||||||
// Reassign entity to the closest skull without an entity
|
|
||||||
assignSkullEntity(inRangeSkulls.get(maxVisibleSkulls - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int updateSkull(Vector3i position, int blockState) {
|
public Skull updateSkull(Vector3i position, int blockState) {
|
||||||
Skull skull = skulls.remove(position);
|
Skull skull = skulls.get(position);
|
||||||
if (skull != null) {
|
if (skull != null) {
|
||||||
int customRuntimeId = SkullBlockEntityTranslator.translateCustomSkull(session, position, skull.texturesProperty, blockState);
|
|
||||||
if (customRuntimeId != -1) {
|
|
||||||
putSkull(position, skull.texturesProperty, blockState, customRuntimeId);
|
|
||||||
} else {
|
|
||||||
putSkull(position, skull.texturesProperty, blockState);
|
putSkull(position, skull.texturesProperty, blockState);
|
||||||
}
|
}
|
||||||
return customRuntimeId;
|
return skull;
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateVisibleSkulls() {
|
public void updateVisibleSkulls() {
|
||||||
@ -239,6 +225,19 @@ public class SkullCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void reassignSkullEntity(Skull skull) {
|
||||||
|
boolean hadEntity = skull.entity != null;
|
||||||
|
freeSkullEntity(skull);
|
||||||
|
|
||||||
|
if (cullingEnabled) {
|
||||||
|
inRangeSkulls.remove(skull);
|
||||||
|
if (hadEntity && inRangeSkulls.size() >= maxVisibleSkulls) {
|
||||||
|
// Reassign entity to the closest skull without an entity
|
||||||
|
assignSkullEntity(inRangeSkulls.get(maxVisibleSkulls - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
skulls.clear();
|
skulls.clear();
|
||||||
inRangeSkulls.clear();
|
inRangeSkulls.clear();
|
||||||
@ -247,10 +246,30 @@ public class SkullCache {
|
|||||||
lastPlayerPosition = null;
|
lastPlayerPosition = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int translateCustomSkull(String skinHash, int blockState) {
|
||||||
|
CustomSkull customSkull = BlockRegistries.CUSTOM_SKULLS.get(skinHash);
|
||||||
|
if (customSkull != null) {
|
||||||
|
byte floorRotation = BlockStateValues.getSkullRotation(blockState);
|
||||||
|
CustomBlockState customBlockState;
|
||||||
|
if (floorRotation == -1) {
|
||||||
|
// Wall skull
|
||||||
|
int wallDirection = BlockStateValues.getSkullWallDirections().get(blockState);
|
||||||
|
customBlockState = customSkull.getWallBlockState(wallDirection);
|
||||||
|
} else {
|
||||||
|
customBlockState = customSkull.getFloorBlockState(floorRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return session.getBlockMappings().getCustomBlockStateIds().getOrDefault(customBlockState, -1);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@Data
|
@Data
|
||||||
public static class Skull {
|
public static class Skull {
|
||||||
private String texturesProperty;
|
private String texturesProperty;
|
||||||
|
private String skinHash;
|
||||||
|
|
||||||
private int blockState;
|
private int blockState;
|
||||||
private int customRuntimeId = -1;
|
private int customRuntimeId = -1;
|
||||||
private SkullPlayerEntity entity;
|
private SkullPlayerEntity entity;
|
||||||
|
@ -38,12 +38,11 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
|||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
|
||||||
import org.geysermc.geyser.api.event.GeyserConvertSkullInventoryEvent;
|
|
||||||
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||||
import org.geysermc.geyser.api.util.TriState;
|
import org.geysermc.geyser.api.util.TriState;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
|
import org.geysermc.geyser.registry.type.CustomSkull;
|
||||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
@ -176,12 +175,12 @@ public abstract class ItemTranslator {
|
|||||||
builder.blockRuntimeId(bedrockItem.getBedrockBlockId());
|
builder.blockRuntimeId(bedrockItem.getBedrockBlockId());
|
||||||
}
|
}
|
||||||
|
|
||||||
translateCustomItem(nbt, builder, bedrockItem);
|
|
||||||
|
|
||||||
if (bedrockItem == session.getItemMappings().getStoredItems().playerHead()) {
|
if (bedrockItem == session.getItemMappings().getStoredItems().playerHead()) {
|
||||||
translatePlayerHead(session, nbt, builder);
|
translatePlayerHead(session, nbt, builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
translateCustomItem(nbt, builder, bedrockItem);
|
||||||
|
|
||||||
if (nbt != null) {
|
if (nbt != null) {
|
||||||
// Translate the canDestroy and canPlaceOn Java NBT
|
// Translate the canDestroy and canPlaceOn Java NBT
|
||||||
ListTag canDestroy = nbt.get("CanDestroy");
|
ListTag canDestroy = nbt.get("CanDestroy");
|
||||||
@ -547,6 +546,7 @@ public abstract class ItemTranslator {
|
|||||||
int bedrockId = getCustomItem(nbt, mapping);
|
int bedrockId = getCustomItem(nbt, mapping);
|
||||||
if (bedrockId != -1) {
|
if (bedrockId != -1) {
|
||||||
builder.id(bedrockId);
|
builder.id(bedrockId);
|
||||||
|
builder.blockRuntimeId(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,13 +563,14 @@ public abstract class ItemTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String skinHash = data.skinUrl().substring(data.skinUrl().lastIndexOf('/') + 1);
|
String skinHash = data.skinUrl().substring(data.skinUrl().lastIndexOf('/') + 1);
|
||||||
GeyserConvertSkullInventoryEvent skullInventoryEvent = new GeyserConvertSkullInventoryEvent(skinHash);
|
|
||||||
GeyserImpl.getInstance().getEventBus().fire(skullInventoryEvent);
|
|
||||||
|
|
||||||
CustomBlockData replacementBlock = skullInventoryEvent.replacementBlock();
|
CustomSkull customSkull = BlockRegistries.CUSTOM_SKULLS.get(skinHash);
|
||||||
if (replacementBlock != null) {
|
if (customSkull != null) {
|
||||||
int bedrockId = session.getItemMappings().getCustomBlockItemIds().getInt(replacementBlock);
|
int itemId = session.getItemMappings().getCustomBlockItemIds().getInt(customSkull.getCustomBlockData());
|
||||||
builder.id(bedrockId);
|
int blockRuntimeId = session.getBlockMappings().getCustomBlockStateIds().getInt(customSkull.getDefaultBlockState());
|
||||||
|
|
||||||
|
builder.id(itemId);
|
||||||
|
builder.blockRuntimeId(blockRuntimeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,14 +31,13 @@ import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
|||||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.nbt.NbtMapBuilder;
|
import com.nukkitx.nbt.NbtMapBuilder;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.event.world.GeyserConvertSkullEvent;
|
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.skin.SkinManager;
|
import org.geysermc.geyser.session.cache.SkullCache;
|
||||||
import org.geysermc.geyser.skin.SkinProvider;
|
import org.geysermc.geyser.skin.SkinProvider;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
@ -75,63 +74,46 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int translateSkull(GeyserSession session, CompoundTag tag, Vector3i blockPosition, int blockState) {
|
public static int translateSkull(GeyserSession session, CompoundTag tag, Vector3i blockPosition, int blockState) {
|
||||||
|
CompletableFuture<String> texturesFuture = getTextures(tag);
|
||||||
|
if (texturesFuture.isDone()) {
|
||||||
try {
|
try {
|
||||||
String texturesProperty = getTextures(tag).get();
|
SkullCache.Skull skull = session.getSkullCache().putSkull(blockPosition, texturesFuture.get(), blockState);
|
||||||
|
return skull.getCustomRuntimeId();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
session.getGeyser().getLogger().debug("Failed to acquire textures for custom skull: " + blockPosition + " " + tag);
|
||||||
|
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SkullOwner contained a username, so we have to wait for it to be retrieved
|
||||||
|
texturesFuture.whenComplete((texturesProperty, throwable) -> {
|
||||||
if (texturesProperty == null) {
|
if (texturesProperty == null) {
|
||||||
session.getGeyser().getLogger().debug("Custom skull with invalid SkullOwner tag: " + blockPosition + " " + tag);
|
session.getGeyser().getLogger().debug("Custom skull with invalid SkullOwner tag: " + blockPosition + " " + tag);
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
int runtimeId = translateCustomSkull(session, blockPosition, texturesProperty, blockState);
|
if (session.getEventLoop().inEventLoop()) {
|
||||||
if (runtimeId == -1) {
|
putSkull(session, blockPosition, texturesProperty, blockState);
|
||||||
session.getSkullCache().putSkull(blockPosition, texturesProperty, blockState);
|
|
||||||
} else {
|
} else {
|
||||||
session.getSkullCache().putSkull(blockPosition, texturesProperty, blockState, runtimeId);
|
session.executeInEventLoop(() -> putSkull(session, blockPosition, texturesProperty, blockState));
|
||||||
}
|
}
|
||||||
return runtimeId;
|
});
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
// We don't have the textures yet, so we can't determine if a custom block was defined for this skull
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int translateCustomSkull(GeyserSession session, Vector3i blockPosition, String texturesProperty, int blockState) {
|
|
||||||
try {
|
|
||||||
SkinManager.GameProfileData gameProfileData = SkinManager.GameProfileData.loadFromJson(texturesProperty);
|
|
||||||
if (gameProfileData == null || gameProfileData.skinUrl() == null) {
|
|
||||||
session.getGeyser().getLogger().debug("Player skull with invalid Skin tag: " + blockPosition + " Textures: " + texturesProperty);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
String skinUrl = gameProfileData.skinUrl();
|
private static void putSkull(GeyserSession session, Vector3i blockPosition, String texturesProperty, int blockState) {
|
||||||
String skinHash = skinUrl.substring(skinUrl.lastIndexOf('/') + 1);
|
SkullCache.Skull skull = session.getSkullCache().putSkull(blockPosition, texturesProperty, blockState);
|
||||||
|
if (skull.getCustomRuntimeId() != -1) {
|
||||||
byte floorRotation = BlockStateValues.getSkullRotation(blockState);
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
GeyserConvertSkullEvent.WallDirection wallDirection = GeyserConvertSkullEvent.WallDirection.INVALID;
|
updateBlockPacket.setDataLayer(0);
|
||||||
boolean onFloor = true;
|
updateBlockPacket.setBlockPosition(blockPosition);
|
||||||
if (floorRotation == -1) {
|
updateBlockPacket.setRuntimeId(skull.getCustomRuntimeId());
|
||||||
// Wall skull
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
||||||
onFloor = false;
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||||
int wallRotation = BlockStateValues.getSkullWallDirections().get(blockState);
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
wallDirection = switch (wallRotation) {
|
|
||||||
case 0 -> GeyserConvertSkullEvent.WallDirection.SOUTH;
|
|
||||||
case 90 -> GeyserConvertSkullEvent.WallDirection.WEST;
|
|
||||||
case 180 -> GeyserConvertSkullEvent.WallDirection.NORTH;
|
|
||||||
case 270 -> GeyserConvertSkullEvent.WallDirection.EAST;
|
|
||||||
default -> GeyserConvertSkullEvent.WallDirection.INVALID;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
GeyserConvertSkullEvent event = new GeyserConvertSkullEvent(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), onFloor, wallDirection, floorRotation, skinHash);
|
|
||||||
GeyserImpl.getInstance().getEventBus().fire(event);
|
|
||||||
|
|
||||||
if (event.getNewBlockState() != null) {
|
|
||||||
return session.getBlockMappings().getCustomBlockStateIds().getOrDefault(event.getNewBlockState(), -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.isCancelled()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
session.getGeyser().getLogger().debug("Player skull with invalid Skin tag: " + blockPosition + " Textures: " + texturesProperty + " Message: " + e.getMessage());
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,11 +64,11 @@ public class JavaBlockEntityDataTranslator extends PacketTranslator<ClientboundB
|
|||||||
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(type, position.getX(), position.getY(), position.getZ(),
|
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(type, position.getX(), position.getY(), position.getZ(),
|
||||||
packet.getNbt(), blockState), packet.getPosition());
|
packet.getNbt(), blockState), packet.getPosition());
|
||||||
// Check for custom skulls.
|
// Check for custom skulls.
|
||||||
boolean customSkull = false;
|
boolean hasCustomHeadBlock = false;
|
||||||
if (session.getPreferencesCache().showCustomSkulls() && packet.getNbt() != null && packet.getNbt().contains("SkullOwner")) {
|
if (session.getPreferencesCache().showCustomSkulls() && packet.getNbt() != null && packet.getNbt().contains("SkullOwner")) {
|
||||||
int runtimeId = SkullBlockEntityTranslator.translateSkull(session, packet.getNbt(), position, blockState);
|
int runtimeId = SkullBlockEntityTranslator.translateSkull(session, packet.getNbt(), position, blockState);
|
||||||
if (runtimeId != -1) {
|
if (runtimeId != -1) {
|
||||||
customSkull = true;
|
hasCustomHeadBlock = true;
|
||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
updateBlockPacket.setDataLayer(0);
|
updateBlockPacket.setDataLayer(0);
|
||||||
updateBlockPacket.setBlockPosition(position);
|
updateBlockPacket.setBlockPosition(position);
|
||||||
@ -78,7 +78,7 @@ public class JavaBlockEntityDataTranslator extends PacketTranslator<ClientboundB
|
|||||||
session.sendUpstreamPacket(updateBlockPacket);
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!customSkull) {
|
if (!hasCustomHeadBlock) {
|
||||||
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(type, position.getX(), position.getY(), position.getZ(),
|
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(type, position.getX(), position.getY(), position.getZ(),
|
||||||
packet.getNbt(), blockState), packet.getPosition());
|
packet.getNbt(), blockState), packet.getPosition());
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ import org.geysermc.geyser.level.chunk.GeyserChunkSection;
|
|||||||
import org.geysermc.geyser.level.chunk.bitarray.SingletonBitArray;
|
import org.geysermc.geyser.level.chunk.bitarray.SingletonBitArray;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.session.cache.SkullCache;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.translator.level.block.entity.BedrockOnlyBlockEntity;
|
import org.geysermc.geyser.translator.level.block.entity.BedrockOnlyBlockEntity;
|
||||||
|
|
||||||
@ -141,9 +142,10 @@ public class ChunkUtils {
|
|||||||
// Skull is gone
|
// Skull is gone
|
||||||
session.getSkullCache().removeSkull(position);
|
session.getSkullCache().removeSkull(position);
|
||||||
} else if (skullVariant == 3) {
|
} else if (skullVariant == 3) {
|
||||||
int customRuntimeId = session.getSkullCache().updateSkull(position, blockState);
|
// The changed block was a player skull so check if a custom block was defined for this skull
|
||||||
if (customRuntimeId != -1) {
|
SkullCache.Skull skull = session.getSkullCache().updateSkull(position, blockState);
|
||||||
blockId = customRuntimeId;
|
if (skull != null && skull.getCustomRuntimeId() != -1) {
|
||||||
|
blockId = skull.getCustomRuntimeId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren