mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Super cursed custom skulls custom block
This commit is contained in:
parent
f9fd7cb831
commit
0eb7218582
33 changed files with 1681 additions and 33 deletions
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* 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.block.custom;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
|
||||||
|
import org.geysermc.geyser.api.block.custom.property.CustomBlockProperty;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to store data for a custom block.
|
||||||
|
*/
|
||||||
|
public interface CustomBlockData {
|
||||||
|
@NonNull String name();
|
||||||
|
|
||||||
|
CustomBlockComponents components();
|
||||||
|
|
||||||
|
@NonNull Map<String, CustomBlockProperty<?>> properties();
|
||||||
|
|
||||||
|
@NonNull List<CustomBlockPermutation> permutations();
|
||||||
|
|
||||||
|
CustomBlockState.Builder blockStateBuilder();
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
Builder name(@NonNull String name);
|
||||||
|
|
||||||
|
Builder components(@NonNull CustomBlockComponents components);
|
||||||
|
|
||||||
|
Builder booleanProperty(@NonNull String propertyName, List<Boolean> values);
|
||||||
|
|
||||||
|
Builder intProperty(@NonNull String propertyName, List<Integer> values);
|
||||||
|
|
||||||
|
Builder stringProperty(@NonNull String propertyName, List<String> values);
|
||||||
|
|
||||||
|
|
||||||
|
Builder permutations(@NonNull List<CustomBlockPermutation> permutations);
|
||||||
|
|
||||||
|
CustomBlockData build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* 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.block.custom;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
|
||||||
|
|
||||||
|
public interface CustomBlockPermutation {
|
||||||
|
@NonNull String condition();
|
||||||
|
|
||||||
|
CustomBlockComponents components();
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
Builder condition(@NonNull String condition);
|
||||||
|
|
||||||
|
Builder components(CustomBlockComponents components);
|
||||||
|
|
||||||
|
CustomBlockPermutation build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* 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.block.custom;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface CustomBlockState {
|
||||||
|
@NonNull String name();
|
||||||
|
|
||||||
|
@NonNull <T> T property(String propertyName);
|
||||||
|
|
||||||
|
@NonNull Map<String, Object> properties();
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
Builder booleanProperty(@NonNull String propertyName, boolean value);
|
||||||
|
|
||||||
|
Builder intProperty(@NonNull String propertyName, int value);
|
||||||
|
|
||||||
|
Builder stringProperty(@NonNull String propertyName, @NonNull String value);
|
||||||
|
|
||||||
|
CustomBlockState build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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.block.custom.component;
|
||||||
|
|
||||||
|
public record CollisionComponent(float originX, float originY, float originZ,
|
||||||
|
float sizeX, float sizeY, float sizeZ) {
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* 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.block.custom.component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface CustomBlockComponents {
|
||||||
|
CollisionComponent aimCollision();
|
||||||
|
|
||||||
|
CollisionComponent entityCollision();
|
||||||
|
|
||||||
|
String geometry();
|
||||||
|
|
||||||
|
Map<String, MaterialInstance> materialInstances();
|
||||||
|
|
||||||
|
Float destroyTime();
|
||||||
|
|
||||||
|
Float friction();
|
||||||
|
|
||||||
|
Float lightEmission();
|
||||||
|
|
||||||
|
Integer lightFilter();
|
||||||
|
|
||||||
|
RotationComponent rotation();
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
Builder aimCollision(CollisionComponent aimCollision);
|
||||||
|
|
||||||
|
Builder entityCollision(CollisionComponent entityCollision);
|
||||||
|
|
||||||
|
Builder geometry(String geometry);
|
||||||
|
|
||||||
|
Builder materialInstances(Map<String, MaterialInstance> materialInstances);
|
||||||
|
|
||||||
|
Builder destroyTime(Float destroyTime);
|
||||||
|
|
||||||
|
Builder friction(Float friction);
|
||||||
|
|
||||||
|
Builder lightEmission(Float lightEmission);
|
||||||
|
|
||||||
|
Builder lightFilter(Integer lightFilter);
|
||||||
|
|
||||||
|
Builder rotation(RotationComponent rotation);
|
||||||
|
|
||||||
|
CustomBlockComponents build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 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.block.custom.component;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
public record MaterialInstance(@NonNull String texture, @NonNull RenderMethod renderMethod, boolean faceDimming, boolean ambientOcclusion) {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 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.block.custom.component;
|
||||||
|
|
||||||
|
public enum RenderMethod {
|
||||||
|
BLEND,
|
||||||
|
OPAQUE,
|
||||||
|
ALPHA_TEST
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* 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.block.custom.component;
|
||||||
|
|
||||||
|
public record RotationComponent(float x, float y, float z) {
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* 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.block.custom.property;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface CustomBlockProperty<T> {
|
||||||
|
@NonNull String name();
|
||||||
|
|
||||||
|
@NonNull List<T> values();
|
||||||
|
|
||||||
|
@NonNull PropertyType<T> type();
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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.block.custom.property;
|
||||||
|
|
||||||
|
public class PropertyType<T> {
|
||||||
|
public static final PropertyType<Boolean> BOOLEAN = new PropertyType<>(Boolean.class);
|
||||||
|
public static final PropertyType<Integer> INTEGER = new PropertyType<>(Integer.class);
|
||||||
|
public static final PropertyType<String> STRING = new PropertyType<>(String.class);
|
||||||
|
|
||||||
|
private final Class<?> typeClass;
|
||||||
|
|
||||||
|
private PropertyType(Class<?> typeClass) {
|
||||||
|
this.typeClass = typeClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getTypeClass() {
|
||||||
|
return typeClass;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.lifecycle;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||||
|
import org.geysermc.geyser.api.event.Event;
|
||||||
|
|
||||||
|
public abstract class GeyserDefineCustomBlocksEvent implements Event {
|
||||||
|
|
||||||
|
public abstract boolean registerCustomBlock(@NonNull CustomBlockData customBlockData);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* 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.level.block;
|
||||||
|
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.CollisionComponent;
|
||||||
|
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 java.util.Map;
|
||||||
|
|
||||||
|
public class GeyserCustomBlockComponents implements CustomBlockComponents {
|
||||||
|
private final CollisionComponent aimCollision;
|
||||||
|
private final CollisionComponent entityCollision;
|
||||||
|
private final String geometry;
|
||||||
|
private final Map<String, MaterialInstance> materialInstances;
|
||||||
|
private final Float destroyTime;
|
||||||
|
private final Float friction;
|
||||||
|
private final Float lightEmission;
|
||||||
|
private final Integer lightFilter;
|
||||||
|
private final RotationComponent rotation;
|
||||||
|
|
||||||
|
public GeyserCustomBlockComponents(BuilderImpl builder) {
|
||||||
|
this.aimCollision = builder.aimCollision;
|
||||||
|
this.entityCollision = builder.entityCollision;
|
||||||
|
this.geometry = builder.geometry;
|
||||||
|
this.materialInstances = builder.materialInstances;
|
||||||
|
this.destroyTime = builder.destroyTime;
|
||||||
|
this.friction = builder.friction;
|
||||||
|
this.lightEmission = builder.lightEmission;
|
||||||
|
this.lightFilter = builder.lightFilter;
|
||||||
|
this.rotation = builder.rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CollisionComponent aimCollision() {
|
||||||
|
return aimCollision;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CollisionComponent entityCollision() {
|
||||||
|
return entityCollision;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String geometry() {
|
||||||
|
return geometry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, MaterialInstance> materialInstances() {
|
||||||
|
return materialInstances;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Float destroyTime() {
|
||||||
|
return destroyTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Float friction() {
|
||||||
|
return friction;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Float lightEmission() {
|
||||||
|
return lightEmission;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer lightFilter() {
|
||||||
|
return lightFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RotationComponent rotation() {
|
||||||
|
return rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BuilderImpl implements Builder {
|
||||||
|
protected CollisionComponent aimCollision;
|
||||||
|
protected CollisionComponent entityCollision;
|
||||||
|
protected String geometry;
|
||||||
|
protected Map<String, MaterialInstance> materialInstances;
|
||||||
|
protected Float destroyTime;
|
||||||
|
protected Float friction;
|
||||||
|
protected Float lightEmission;
|
||||||
|
protected Integer lightFilter;
|
||||||
|
protected RotationComponent rotation;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder aimCollision(CollisionComponent aimCollision) {
|
||||||
|
this.aimCollision = aimCollision;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder entityCollision(CollisionComponent entityCollision) {
|
||||||
|
this.entityCollision = entityCollision;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder geometry(String geometry) {
|
||||||
|
this.geometry = geometry;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder materialInstances(Map<String, MaterialInstance> materialInstances) {
|
||||||
|
this.materialInstances = materialInstances;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder destroyTime(Float destroyTime) {
|
||||||
|
this.destroyTime = destroyTime;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder friction(Float friction) {
|
||||||
|
this.friction = friction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder lightEmission(Float lightEmission) {
|
||||||
|
this.lightEmission = lightEmission;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder lightFilter(Integer lightFilter) {
|
||||||
|
this.lightFilter = lightFilter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder rotation(RotationComponent rotation) {
|
||||||
|
this.rotation = rotation;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomBlockComponents build() {
|
||||||
|
return new GeyserCustomBlockComponents(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
* 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.level.block;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectLists;
|
||||||
|
import lombok.Value;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
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.CustomBlockComponents;
|
||||||
|
import org.geysermc.geyser.api.block.custom.property.CustomBlockProperty;
|
||||||
|
import org.geysermc.geyser.api.block.custom.property.PropertyType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
public class GeyserCustomBlockData implements CustomBlockData {
|
||||||
|
String name;
|
||||||
|
CustomBlockComponents components;
|
||||||
|
Map<String, CustomBlockProperty<?>> properties;
|
||||||
|
List<CustomBlockPermutation> permutations;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomBlockComponents components() {
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Map<String, CustomBlockProperty<?>> properties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull List<CustomBlockPermutation> permutations() {
|
||||||
|
return permutations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomBlockState.Builder blockStateBuilder() {
|
||||||
|
return new GeyserCustomBlockState.BuilderImpl(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BuilderImpl implements Builder {
|
||||||
|
private String name;
|
||||||
|
private CustomBlockComponents components;
|
||||||
|
private Map<String, CustomBlockProperty<?>> properties = new Object2ObjectOpenHashMap<>();
|
||||||
|
private List<CustomBlockPermutation> permutations;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder name(@NonNull String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder components(@NonNull CustomBlockComponents components) {
|
||||||
|
this.components = components;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder booleanProperty(@NonNull String propertyName, List<Boolean> values) {
|
||||||
|
this.properties.put(propertyName, new GeyserCustomBlockProperty<>(propertyName, values, PropertyType.BOOLEAN));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder intProperty(@NonNull String propertyName, List<Integer> values) {
|
||||||
|
this.properties.put(propertyName, new GeyserCustomBlockProperty<>(propertyName, values, PropertyType.INTEGER));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder stringProperty(@NonNull String propertyName, List<String> values) {
|
||||||
|
this.properties.put(propertyName, new GeyserCustomBlockProperty<>(propertyName, values, PropertyType.STRING));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder permutations(@NonNull List<CustomBlockPermutation> permutations) {
|
||||||
|
this.permutations = permutations;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomBlockData build() {
|
||||||
|
if (name == null) {
|
||||||
|
throw new IllegalArgumentException("Name must be set");
|
||||||
|
}
|
||||||
|
if (properties == null) {
|
||||||
|
properties = Object2ObjectMaps.emptyMap();
|
||||||
|
} else {
|
||||||
|
for (CustomBlockProperty<?> property : properties.values()) {
|
||||||
|
if (property.values().isEmpty() || property.values().size() > 16) {
|
||||||
|
throw new IllegalArgumentException(property.name() + " must contain 1 to 16 values.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (permutations == null) {
|
||||||
|
permutations = ObjectLists.emptyList();
|
||||||
|
}
|
||||||
|
return new GeyserCustomBlockData(name, components, properties, permutations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* 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.level.block;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockPermutation;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
|
||||||
|
|
||||||
|
|
||||||
|
public class GeyserCustomBlockPermutation implements CustomBlockPermutation {
|
||||||
|
private final String condition;
|
||||||
|
private final CustomBlockComponents components;
|
||||||
|
|
||||||
|
public GeyserCustomBlockPermutation(String condition, CustomBlockComponents components) {
|
||||||
|
this.condition = condition;
|
||||||
|
this.components = components;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull String condition() {
|
||||||
|
return condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomBlockComponents components() {
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BuilderImpl implements Builder {
|
||||||
|
private String condition;
|
||||||
|
private CustomBlockComponents components;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder condition(@NonNull String condition) {
|
||||||
|
this.condition = condition;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder components(CustomBlockComponents components) {
|
||||||
|
this.components = components;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomBlockPermutation build() {
|
||||||
|
if (condition == null) {
|
||||||
|
throw new IllegalArgumentException("Condition must be set");
|
||||||
|
}
|
||||||
|
return new GeyserCustomBlockPermutation(condition, components);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* 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.level.block;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.block.custom.property.CustomBlockProperty;
|
||||||
|
import org.geysermc.geyser.api.block.custom.property.PropertyType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class GeyserCustomBlockProperty<T> implements CustomBlockProperty<T> {
|
||||||
|
private final String name;
|
||||||
|
private final List<T> values;
|
||||||
|
|
||||||
|
private final PropertyType<T> type;
|
||||||
|
|
||||||
|
public GeyserCustomBlockProperty(String name, List<T> values, PropertyType<T> type) {
|
||||||
|
this.name = name;
|
||||||
|
this.values = values;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull List<T> values(){
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull PropertyType<T> type() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* 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.level.block;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Value;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||||
|
import org.geysermc.geyser.api.block.custom.property.CustomBlockProperty;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Value
|
||||||
|
public class GeyserCustomBlockState implements CustomBlockState {
|
||||||
|
String name;
|
||||||
|
Map<String, Object> properties;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T> @NonNull T property(String propertyName) {
|
||||||
|
return (T) properties.get(propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NonNull Map<String, Object> properties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public static class BuilderImpl implements CustomBlockState.Builder {
|
||||||
|
private final CustomBlockData blockData;
|
||||||
|
private final Object2ObjectMap<String, Object> properties = new Object2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder booleanProperty(@NonNull String propertyName, boolean value) {
|
||||||
|
properties.put(propertyName, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder intProperty(@NonNull String propertyName, int value) {
|
||||||
|
properties.put(propertyName, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder stringProperty(@NonNull String propertyName, @NonNull String value) {
|
||||||
|
properties.put(propertyName, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomBlockState build() {
|
||||||
|
for (String propertyName : blockData.properties().keySet()) {
|
||||||
|
if (!properties.containsKey(propertyName)) {
|
||||||
|
throw new IllegalArgumentException("Missing property: " + propertyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, Object> entry : properties.entrySet()) {
|
||||||
|
String propertyName = entry.getKey();
|
||||||
|
Object propertyValue = entry.getValue();
|
||||||
|
|
||||||
|
CustomBlockProperty<?> property = blockData.properties().get(propertyName);
|
||||||
|
if (property == null) {
|
||||||
|
throw new IllegalArgumentException("Unknown property: " + propertyName);
|
||||||
|
} else if (!property.values().contains(propertyValue)) {
|
||||||
|
throw new IllegalArgumentException("Invalid value for property: " + propertyName + " Value: " + propertyValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GeyserCustomBlockState(blockData.name(), Object2ObjectMaps.unmodifiable(properties));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||||
import org.geysermc.geyser.registry.loader.RegistryLoaders;
|
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;
|
||||||
|
@ -72,6 +73,11 @@ public class BlockRegistries {
|
||||||
*/
|
*/
|
||||||
public static final SimpleRegistry<IntSet> WATERLOGGED = SimpleRegistry.create(RegistryLoaders.empty(IntOpenHashSet::new));
|
public static final SimpleRegistry<IntSet> WATERLOGGED = SimpleRegistry.create(RegistryLoaders.empty(IntOpenHashSet::new));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A registry containing all the custom blocks.
|
||||||
|
*/
|
||||||
|
public static final ArrayRegistry<CustomBlockData> CUSTOM_BLOCKS = ArrayRegistry.create(RegistryLoaders.empty(() -> new CustomBlockData[] {}));
|
||||||
|
|
||||||
static {
|
static {
|
||||||
BlockRegistryPopulator.populate();
|
BlockRegistryPopulator.populate();
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,15 +28,24 @@ package org.geysermc.geyser.registry.populator;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.nukkitx.nbt.*;
|
import com.nukkitx.nbt.*;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.BlockPropertyData;
|
||||||
import com.nukkitx.protocol.bedrock.v527.Bedrock_v527;
|
import com.nukkitx.protocol.bedrock.v527.Bedrock_v527;
|
||||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.*;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectIntPair;
|
|
||||||
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.CustomBlockPermutation;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.CollisionComponent;
|
||||||
|
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.property.CustomBlockProperty;
|
||||||
|
import org.geysermc.geyser.api.block.custom.property.PropertyType;
|
||||||
|
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomBlocksEvent;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
|
import org.geysermc.geyser.level.block.GeyserCustomBlockState;
|
||||||
import org.geysermc.geyser.level.physics.PistonBehavior;
|
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;
|
||||||
|
@ -45,10 +54,8 @@ import org.geysermc.geyser.util.BlockUtils;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayDeque;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Deque;
|
import java.util.*;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
@ -71,38 +78,172 @@ 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();
|
||||||
registerBedrockBlocks();
|
registerBedrockBlocks();
|
||||||
|
|
||||||
BLOCKS_JSON = null;
|
BLOCKS_JSON = null;
|
||||||
}
|
}
|
||||||
|
private static void registerCustomBedrockBlocks() {
|
||||||
|
List<CustomBlockData> customBlocks = new ArrayList<>();
|
||||||
|
GeyserImpl.getInstance().getEventBus().fire(new GeyserDefineCustomBlocksEvent() {
|
||||||
|
@Override
|
||||||
|
public boolean registerCustomBlock(@NonNull CustomBlockData customBlockData) {
|
||||||
|
customBlocks.add(customBlockData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
BlockRegistries.CUSTOM_BLOCKS.set(customBlocks.toArray(new CustomBlockData[0]));
|
||||||
|
GeyserImpl.getInstance().getLogger().debug("Registered " + customBlocks.size() + " custom blocks.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void generateCustomBlockStates(List<NbtMap> blockStates, List<CustomBlockState> customExtBlockStates, CustomBlockData customBlock, int stateVersion) {
|
||||||
|
if (customBlock.properties().isEmpty()) {
|
||||||
|
blockStates.add(NbtMap.builder()
|
||||||
|
.putString("name", CUSTOM_PREFIX + customBlock.name())
|
||||||
|
.putInt("version", stateVersion)
|
||||||
|
.putCompound("states", NbtMap.EMPTY)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
int totalPermutations = 1;
|
||||||
|
for (CustomBlockProperty<?> property : customBlock.properties().values()) {
|
||||||
|
totalPermutations *= property.values().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < totalPermutations; i++) {
|
||||||
|
NbtMapBuilder statesBuilder = NbtMap.builder();
|
||||||
|
|
||||||
|
int permIndex = i;
|
||||||
|
for (CustomBlockProperty<?> property : customBlock.properties().values()) {
|
||||||
|
statesBuilder.put(property.name(), property.values().get(permIndex % property.values().size()));
|
||||||
|
permIndex /= property.values().size();
|
||||||
|
}
|
||||||
|
String name = CUSTOM_PREFIX + customBlock.name();
|
||||||
|
NbtMap states = statesBuilder.build();
|
||||||
|
|
||||||
|
blockStates.add(NbtMap.builder()
|
||||||
|
.putString("name", name)
|
||||||
|
.putInt("version", stateVersion)
|
||||||
|
.putCompound("states", states)
|
||||||
|
.build());
|
||||||
|
customExtBlockStates.add(new GeyserCustomBlockState(name, states));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static BlockPropertyData generateBlockPropertyData(CustomBlockData customBlock) {
|
||||||
|
List<NbtMap> permutations = new ArrayList<>();
|
||||||
|
for (CustomBlockPermutation permutation : customBlock.permutations()) {
|
||||||
|
permutations.add(NbtMap.builder()
|
||||||
|
.putCompound("components", convertComponents(permutation.components()))
|
||||||
|
.putString("condition", permutation.condition())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The order that properties are defined influences the order that block states are generated
|
||||||
|
List<NbtMap> properties = new ArrayList<>();
|
||||||
|
for (CustomBlockProperty<?> property : customBlock.properties().values()) {
|
||||||
|
NbtMapBuilder propertyBuilder = NbtMap.builder()
|
||||||
|
.putString("name", property.name());
|
||||||
|
if (property.type() == PropertyType.BOOLEAN) {
|
||||||
|
List<Byte> values = new ArrayList<>();
|
||||||
|
for (boolean value : (List<Boolean>) property.values()) {
|
||||||
|
values.add((byte) (value ? 1 : 0));
|
||||||
|
}
|
||||||
|
propertyBuilder.putList("enum", NbtType.BYTE, values);
|
||||||
|
} else if (property.type() == PropertyType.INTEGER) {
|
||||||
|
propertyBuilder.putList("enum", NbtType.INT, (List<Integer>) property.values());
|
||||||
|
} else if (property.type() == PropertyType.STRING) {
|
||||||
|
propertyBuilder.putList("enum", NbtType.STRING, (List<String>) property.values());
|
||||||
|
}
|
||||||
|
properties.add(propertyBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
NbtMap propertyTag = NbtMap.builder()
|
||||||
|
.putCompound("components", convertComponents(customBlock.components()))
|
||||||
|
.putInt("molangVersion", 0)
|
||||||
|
.putList("permutations", NbtType.COMPOUND, permutations)
|
||||||
|
.putList("properties", NbtType.COMPOUND, properties)
|
||||||
|
.build();
|
||||||
|
return new BlockPropertyData(CUSTOM_PREFIX + customBlock.name(), propertyTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final long FNV1_64_OFFSET_BASIS = 0xcbf29ce484222325L;
|
||||||
|
private static final long FNV1_64_PRIME = 1099511628211L;
|
||||||
|
|
||||||
|
private static long fnv164(String str) {
|
||||||
|
long hash = FNV1_64_OFFSET_BASIS;
|
||||||
|
for (byte b : str.getBytes(StandardCharsets.UTF_8)) {
|
||||||
|
hash *= FNV1_64_PRIME;
|
||||||
|
hash ^= b;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
private static void registerBedrockBlocks() {
|
private static void registerBedrockBlocks() {
|
||||||
for (Map.Entry<ObjectIntPair<String>, BiFunction<String, NbtMapBuilder, String>> palette : BLOCK_MAPPERS.entrySet()) {
|
for (Map.Entry<ObjectIntPair<String>, BiFunction<String, NbtMapBuilder, String>> palette : BLOCK_MAPPERS.entrySet()) {
|
||||||
NbtList<NbtMap> blocksTag;
|
NbtList<NbtMap> blocksTag;
|
||||||
|
List<NbtMap> blockStates;
|
||||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResource(String.format("bedrock/block_palette.%s.nbt", palette.getKey().key()));
|
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResource(String.format("bedrock/block_palette.%s.nbt", palette.getKey().key()));
|
||||||
NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(new GZIPInputStream(stream)), true, true)) {
|
NBTInputStream nbtInputStream = new NBTInputStream(new DataInputStream(new GZIPInputStream(stream)), true, true)) {
|
||||||
NbtMap blockPalette = (NbtMap) nbtInputStream.readTag();
|
NbtMap blockPalette = (NbtMap) nbtInputStream.readTag();
|
||||||
blocksTag = (NbtList<NbtMap>) blockPalette.getList("blocks", NbtType.COMPOUND);
|
blocksTag = (NbtList<NbtMap>) blockPalette.getList("blocks", NbtType.COMPOUND);
|
||||||
|
blockStates = new ArrayList<>(blocksTag);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError("Unable to get blocks from runtime block states", e);
|
throw new AssertionError("Unable to get blocks from runtime block states", e);
|
||||||
}
|
}
|
||||||
|
int stateVersion = blocksTag.get(0).getInt("version");
|
||||||
|
|
||||||
|
List<BlockPropertyData> customBlockProperties = new ArrayList<>();
|
||||||
|
List<NbtMap> customBlockStates = new ArrayList<>();
|
||||||
|
List<CustomBlockState> customExtBlockStates = new ArrayList<>();
|
||||||
|
int[] remappedVanillaIds = new int[0];
|
||||||
|
if (BlockRegistries.CUSTOM_BLOCKS.get().length != 0) {
|
||||||
|
for (CustomBlockData customBlock : BlockRegistries.CUSTOM_BLOCKS.get()) {
|
||||||
|
customBlockProperties.add(generateBlockPropertyData(customBlock));
|
||||||
|
generateCustomBlockStates(customBlockStates, customExtBlockStates, customBlock, stateVersion);
|
||||||
|
}
|
||||||
|
blockStates.addAll(customBlockStates);
|
||||||
|
GeyserImpl.getInstance().getLogger().debug("Added " + customBlockStates.size() + " custom block states to v" + palette.getKey().valueInt() + " palette.");
|
||||||
|
|
||||||
|
if (palette.getKey().valueInt() > 486) {
|
||||||
|
// Version 1.18.30 and above sort the palette by the FNV1 64 bit hash of the name
|
||||||
|
blockStates.sort((a, b) -> Long.compareUnsigned(fnv164(a.getString("name")), fnv164(b.getString("name"))));
|
||||||
|
} else {
|
||||||
|
// Version below 1.18.30 sort the palette alphabetically by name
|
||||||
|
blockStates.sort(Comparator.comparing(state -> state.getString("name")));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// New since 1.16.100 - find the block runtime ID by the order given to us in the block palette,
|
// New since 1.16.100 - find the block runtime ID by the order given to us in the block palette,
|
||||||
// as we no longer send a block palette
|
// as we no longer send a block palette
|
||||||
Object2IntMap<NbtMap> blockStateOrderedMap = new Object2IntOpenHashMap<>(blocksTag.size());
|
Object2IntMap<NbtMap> blockStateOrderedMap = new Object2IntOpenHashMap<>(blockStates.size());
|
||||||
|
for (int i = 0; i < blockStates.size(); i++) {
|
||||||
int stateVersion = -1;
|
NbtMap tag = blockStates.get(i);
|
||||||
for (int i = 0; i < blocksTag.size(); i++) {
|
|
||||||
NbtMap tag = blocksTag.get(i);
|
|
||||||
if (blockStateOrderedMap.containsKey(tag)) {
|
if (blockStateOrderedMap.containsKey(tag)) {
|
||||||
throw new AssertionError("Duplicate block states in Bedrock palette: " + tag);
|
throw new AssertionError("Duplicate block states in Bedrock palette: " + tag);
|
||||||
}
|
}
|
||||||
blockStateOrderedMap.put(tag, i);
|
blockStateOrderedMap.put(tag, i);
|
||||||
if (stateVersion == -1) {
|
}
|
||||||
stateVersion = tag.getInt("version");
|
|
||||||
|
Object2IntMap<CustomBlockState> customBlockStateIds = Object2IntMaps.emptyMap();
|
||||||
|
if (BlockRegistries.CUSTOM_BLOCKS.get().length != 0) {
|
||||||
|
customBlockStateIds = new Object2IntOpenHashMap<>(customExtBlockStates.size());
|
||||||
|
for (int i = 0; i < customExtBlockStates.size(); i++) {
|
||||||
|
NbtMap tag = customBlockStates.get(i);
|
||||||
|
CustomBlockState blockState = customExtBlockStates.get(i);
|
||||||
|
customBlockStateIds.put(blockState, blockStateOrderedMap.getInt(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
remappedVanillaIds = new int[blocksTag.size()];
|
||||||
|
for (int i = 0; i < blocksTag.size(); i++) {
|
||||||
|
remappedVanillaIds[i] = blockStateOrderedMap.getInt(blocksTag.get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int airRuntimeId = -1;
|
int airRuntimeId = -1;
|
||||||
int commandBlockRuntimeId = -1;
|
int commandBlockRuntimeId = -1;
|
||||||
int javaRuntimeId = -1;
|
int javaRuntimeId = -1;
|
||||||
|
@ -154,7 +295,7 @@ public class BlockRegistryPopulator {
|
||||||
|
|
||||||
// Get the tag needed for non-empty flower pots
|
// Get the tag needed for non-empty flower pots
|
||||||
if (entry.getValue().get("pottable") != null) {
|
if (entry.getValue().get("pottable") != null) {
|
||||||
flowerPotBlocks.put(cleanJavaIdentifier.intern(), blocksTag.get(bedrockRuntimeId));
|
flowerPotBlocks.put(cleanJavaIdentifier.intern(), blockStates.get(bedrockRuntimeId));
|
||||||
}
|
}
|
||||||
|
|
||||||
javaToBedrockBlocks[javaRuntimeId] = bedrockRuntimeId;
|
javaToBedrockBlocks[javaRuntimeId] = bedrockRuntimeId;
|
||||||
|
@ -187,17 +328,92 @@ public class BlockRegistryPopulator {
|
||||||
itemFrames.put(entry.getKey(), entry.getIntValue());
|
itemFrames.put(entry.getKey(), entry.getIntValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.bedrockBlockStates(blocksTag);
|
builder.bedrockBlockStates(new NbtList<>(NbtType.COMPOUND, blockStates));
|
||||||
|
|
||||||
BlockRegistries.BLOCKS.register(palette.getKey().valueInt(), builder.blockStateVersion(stateVersion)
|
BlockRegistries.BLOCKS.register(palette.getKey().valueInt(), builder.blockStateVersion(stateVersion)
|
||||||
.javaToBedrockBlocks(javaToBedrockBlocks)
|
.javaToBedrockBlocks(javaToBedrockBlocks)
|
||||||
.itemFrames(itemFrames)
|
.itemFrames(itemFrames)
|
||||||
.flowerPotBlocks(flowerPotBlocks)
|
.flowerPotBlocks(flowerPotBlocks)
|
||||||
.jigsawStateIds(jigsawStateIds)
|
.jigsawStateIds(jigsawStateIds)
|
||||||
|
.remappedVanillaIds(remappedVanillaIds)
|
||||||
|
.blockProperties(customBlockProperties)
|
||||||
|
.customBlockStateIds(customBlockStateIds)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static NbtMap convertComponents(CustomBlockComponents components) {
|
||||||
|
if (components == null) {
|
||||||
|
return NbtMap.EMPTY;
|
||||||
|
}
|
||||||
|
NbtMapBuilder builder = NbtMap.builder();
|
||||||
|
if (components.aimCollision() != null) {
|
||||||
|
CollisionComponent collisionComponent = components.aimCollision();
|
||||||
|
builder.putCompound("minecraft:aim_collision", NbtMap.builder()
|
||||||
|
.putBoolean("enabled", true)
|
||||||
|
.putList("origin", NbtType.FLOAT, collisionComponent.originX(), collisionComponent.originY(), collisionComponent.originZ())
|
||||||
|
.putList("size", NbtType.FLOAT, collisionComponent.sizeX(), collisionComponent.sizeY(), collisionComponent.sizeZ())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
if (components.entityCollision() != null) {
|
||||||
|
CollisionComponent collisionComponent = components.entityCollision();
|
||||||
|
builder.putCompound("minecraft:block_collision", NbtMap.builder()
|
||||||
|
.putBoolean("enabled", true)
|
||||||
|
.putList("origin", NbtType.FLOAT, collisionComponent.originX(), collisionComponent.originY(), collisionComponent.originZ())
|
||||||
|
.putList("size", NbtType.FLOAT, collisionComponent.sizeX(), collisionComponent.sizeY(), collisionComponent.sizeZ())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
if (components.geometry() != null) {
|
||||||
|
builder.putCompound("minecraft:geometry", NbtMap.builder()
|
||||||
|
.putString("value", components.geometry())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
if (components.materialInstances() != null) {
|
||||||
|
NbtMapBuilder materialsBuilder = NbtMap.builder();
|
||||||
|
for (Map.Entry<String, MaterialInstance> entry : components.materialInstances().entrySet()) {
|
||||||
|
MaterialInstance materialInstance = entry.getValue();
|
||||||
|
materialsBuilder.putCompound(entry.getKey(), NbtMap.builder()
|
||||||
|
.putString("texture", materialInstance.texture())
|
||||||
|
.putString("render_method", materialInstance.renderMethod().toString().toLowerCase(Locale.ROOT))
|
||||||
|
.putBoolean("face_dimming", materialInstance.faceDimming())
|
||||||
|
.putBoolean("ambient_occlusion", materialInstance.faceDimming())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
builder.putCompound("minecraft:material_instances", NbtMap.builder()
|
||||||
|
.putCompound("mappings", NbtMap.EMPTY)
|
||||||
|
.putCompound("materials", materialsBuilder.build())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
if (components.destroyTime() != null) {
|
||||||
|
builder.putCompound("minecraft:destroy_time", NbtMap.builder()
|
||||||
|
.putFloat("value", components.destroyTime())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
if (components.friction() != null) {
|
||||||
|
builder.putCompound("minecraft:friction", NbtMap.builder()
|
||||||
|
.putFloat("value", components.friction())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
if (components.lightEmission() != null) {
|
||||||
|
builder.putCompound("minecraft:block_light_emission", NbtMap.builder()
|
||||||
|
.putFloat("value", components.lightEmission())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
if (components.lightFilter() != null) {
|
||||||
|
builder.putCompound("minecraft:block_light_filter", NbtMap.builder()
|
||||||
|
.putByte("value", components.lightFilter().byteValue())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
if (components.rotation() != null) {
|
||||||
|
builder.putCompound("minecraft:rotation", NbtMap.builder()
|
||||||
|
.putFloat("x", components.rotation().x())
|
||||||
|
.putFloat("y", components.rotation().y())
|
||||||
|
.putFloat("z", components.rotation().z())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
private static void registerJavaBlocks() {
|
private static void registerJavaBlocks() {
|
||||||
JsonNode blocksJson;
|
JsonNode blocksJson;
|
||||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResource("mappings/blocks.json")) {
|
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResource("mappings/blocks.json")) {
|
||||||
|
|
|
@ -46,6 +46,7 @@ import it.unimi.dsi.fastutil.objects.*;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
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.event.lifecycle.GeyserDefineCustomItemsEvent;
|
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomItemsEvent;
|
||||||
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||||
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||||
|
@ -198,6 +199,8 @@ public class ItemRegistryPopulator {
|
||||||
// Temporary mapping to create stored items
|
// Temporary mapping to create stored items
|
||||||
Map<String, ItemMapping> identifierToMapping = new Object2ObjectOpenHashMap<>();
|
Map<String, ItemMapping> identifierToMapping = new Object2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(palette.getValue().protocolVersion());
|
||||||
|
|
||||||
int netId = 1;
|
int netId = 1;
|
||||||
List<ItemData> creativeItems = new ArrayList<>();
|
List<ItemData> creativeItems = new ArrayList<>();
|
||||||
for (JsonNode itemNode : creativeItemEntries) {
|
for (JsonNode itemNode : creativeItemEntries) {
|
||||||
|
@ -216,6 +219,10 @@ public class ItemRegistryPopulator {
|
||||||
JsonNode blockRuntimeIdNode = itemNode.get("blockRuntimeId");
|
JsonNode blockRuntimeIdNode = itemNode.get("blockRuntimeId");
|
||||||
if (blockRuntimeIdNode != null) {
|
if (blockRuntimeIdNode != null) {
|
||||||
blockRuntimeId = blockRuntimeIdNode.asInt();
|
blockRuntimeId = blockRuntimeIdNode.asInt();
|
||||||
|
|
||||||
|
if (blockMappings.getRemappedVanillaIds().length != 0) {
|
||||||
|
blockRuntimeId = blockMappings.getRemappedVanillaIds()[blockRuntimeId];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
JsonNode nbtNode = itemNode.get("nbt_b64");
|
JsonNode nbtNode = itemNode.get("nbt_b64");
|
||||||
if (nbtNode != null) {
|
if (nbtNode != null) {
|
||||||
|
@ -270,8 +277,6 @@ public class ItemRegistryPopulator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(palette.getValue().protocolVersion());
|
|
||||||
|
|
||||||
int itemIndex = 0;
|
int itemIndex = 0;
|
||||||
int javaFurnaceMinecartId = 0;
|
int javaFurnaceMinecartId = 0;
|
||||||
|
|
||||||
|
@ -631,6 +636,19 @@ public class ItemRegistryPopulator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register the item forms of custom blocks
|
||||||
|
Object2IntMap<CustomBlockData> customBlockItemIds = Object2IntMaps.emptyMap();
|
||||||
|
if (BlockRegistries.CUSTOM_BLOCKS.get().length != 0) {
|
||||||
|
customBlockItemIds = new Object2IntOpenHashMap<>();
|
||||||
|
for (CustomBlockData customBlock : BlockRegistries.CUSTOM_BLOCKS.get()) {
|
||||||
|
int customProtocolId = nextFreeBedrockId++;
|
||||||
|
String identifier = BlockRegistryPopulator.CUSTOM_PREFIX + customBlock.name();
|
||||||
|
|
||||||
|
entries.put(identifier, new StartGamePacket.ItemEntry(identifier, (short) customProtocolId));
|
||||||
|
customBlockItemIds.put(customBlock, customProtocolId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ItemMappings itemMappings = ItemMappings.builder()
|
ItemMappings itemMappings = ItemMappings.builder()
|
||||||
.items(mappings.toArray(new ItemMapping[0]))
|
.items(mappings.toArray(new ItemMapping[0]))
|
||||||
.creativeItems(creativeItems.toArray(new ItemData[0]))
|
.creativeItems(creativeItems.toArray(new ItemData[0]))
|
||||||
|
@ -645,6 +663,7 @@ public class ItemRegistryPopulator {
|
||||||
.componentItemData(componentItemData)
|
.componentItemData(componentItemData)
|
||||||
.lodestoneCompass(lodestoneEntry)
|
.lodestoneCompass(lodestoneEntry)
|
||||||
.customIdMappings(customIdMappings)
|
.customIdMappings(customIdMappings)
|
||||||
|
.customBlockItemIds(customBlockItemIds)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Registries.ITEMS.register(palette.getValue().protocolVersion(), itemMappings);
|
Registries.ITEMS.register(palette.getValue().protocolVersion(), itemMappings);
|
||||||
|
|
|
@ -27,6 +27,9 @@ package org.geysermc.geyser.registry.provider;
|
||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockPermutation;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
|
||||||
import org.geysermc.geyser.api.command.Command;
|
import org.geysermc.geyser.api.command.Command;
|
||||||
import org.geysermc.geyser.api.command.CommandSource;
|
import org.geysermc.geyser.api.command.CommandSource;
|
||||||
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||||
|
@ -37,6 +40,9 @@ import org.geysermc.geyser.command.GeyserCommandManager;
|
||||||
import org.geysermc.geyser.item.GeyserCustomItemData;
|
import org.geysermc.geyser.item.GeyserCustomItemData;
|
||||||
import org.geysermc.geyser.item.GeyserCustomItemOptions;
|
import org.geysermc.geyser.item.GeyserCustomItemOptions;
|
||||||
import org.geysermc.geyser.item.GeyserNonVanillaCustomItemData;
|
import org.geysermc.geyser.item.GeyserNonVanillaCustomItemData;
|
||||||
|
import org.geysermc.geyser.level.block.GeyserCustomBlockComponents;
|
||||||
|
import org.geysermc.geyser.level.block.GeyserCustomBlockData;
|
||||||
|
import org.geysermc.geyser.level.block.GeyserCustomBlockPermutation;
|
||||||
import org.geysermc.geyser.registry.ProviderRegistries;
|
import org.geysermc.geyser.registry.ProviderRegistries;
|
||||||
import org.geysermc.geyser.registry.SimpleMappedRegistry;
|
import org.geysermc.geyser.registry.SimpleMappedRegistry;
|
||||||
|
|
||||||
|
@ -52,6 +58,11 @@ public class GeyserBuilderProvider extends AbstractProvider implements BuilderPr
|
||||||
@Override
|
@Override
|
||||||
public void registerProviders(Map<Class<?>, ProviderSupplier> providers) {
|
public void registerProviders(Map<Class<?>, ProviderSupplier> providers) {
|
||||||
providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Class<? extends CommandSource>) args[0]));
|
providers.put(Command.Builder.class, args -> new GeyserCommandManager.CommandBuilder<>((Class<? extends CommandSource>) args[0]));
|
||||||
|
|
||||||
|
providers.put(CustomBlockComponents.Builder.class, args -> new GeyserCustomBlockComponents.BuilderImpl());
|
||||||
|
providers.put(CustomBlockData.Builder.class, args -> new GeyserCustomBlockData.BuilderImpl());
|
||||||
|
providers.put(CustomBlockPermutation.Builder.class, args -> new GeyserCustomBlockPermutation.BuilderImpl());
|
||||||
|
|
||||||
providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder());
|
providers.put(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder());
|
||||||
providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder());
|
providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder());
|
||||||
providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder());
|
providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder());
|
||||||
|
|
|
@ -27,11 +27,14 @@ package org.geysermc.geyser.registry.type;
|
||||||
|
|
||||||
import com.nukkitx.nbt.NbtList;
|
import com.nukkitx.nbt.NbtList;
|
||||||
import com.nukkitx.nbt.NbtMap;
|
import com.nukkitx.nbt.NbtMap;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.BlockPropertyData;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Builder
|
@Builder
|
||||||
|
@ -46,6 +49,7 @@ public class BlockMappings {
|
||||||
int[] javaToBedrockBlocks;
|
int[] javaToBedrockBlocks;
|
||||||
|
|
||||||
NbtList<NbtMap> bedrockBlockStates;
|
NbtList<NbtMap> bedrockBlockStates;
|
||||||
|
int[] remappedVanillaIds;
|
||||||
|
|
||||||
int commandBlockRuntimeId;
|
int commandBlockRuntimeId;
|
||||||
|
|
||||||
|
@ -54,6 +58,9 @@ public class BlockMappings {
|
||||||
|
|
||||||
IntSet jigsawStateIds;
|
IntSet jigsawStateIds;
|
||||||
|
|
||||||
|
List<BlockPropertyData> blockProperties;
|
||||||
|
Object2IntMap<CustomBlockState> customBlockStateIds;
|
||||||
|
|
||||||
public int getBedrockBlockId(int state) {
|
public int getBedrockBlockId(int state) {
|
||||||
if (state >= this.javaToBedrockBlocks.length) {
|
if (state >= this.javaToBedrockBlocks.length) {
|
||||||
return bedrockAirId;
|
return bedrockAirId;
|
||||||
|
|
|
@ -31,9 +31,11 @@ import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||||
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
|
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||||
import org.geysermc.geyser.inventory.item.StoredItemMappings;
|
import org.geysermc.geyser.inventory.item.StoredItemMappings;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
@ -70,6 +72,8 @@ public class ItemMappings {
|
||||||
List<ComponentItemData> componentItemData;
|
List<ComponentItemData> componentItemData;
|
||||||
Int2ObjectMap<String> customIdMappings;
|
Int2ObjectMap<String> customIdMappings;
|
||||||
|
|
||||||
|
Object2IntMap<CustomBlockData> customBlockItemIds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets an {@link ItemMapping} from the given {@link ItemStack}.
|
* Gets an {@link ItemMapping} from the given {@link ItemStack}.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1467,6 +1467,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||||
startGamePacket.setMultiplayerCorrelationId("");
|
startGamePacket.setMultiplayerCorrelationId("");
|
||||||
|
|
||||||
startGamePacket.setItemEntries(this.itemMappings.getItemEntries());
|
startGamePacket.setItemEntries(this.itemMappings.getItemEntries());
|
||||||
|
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("upcoming_creator_features", true));
|
||||||
|
startGamePacket.getExperiments().add(new ExperimentData("experimental_molang_features", true));
|
||||||
|
|
||||||
startGamePacket.setVanillaVersion("*");
|
startGamePacket.setVanillaVersion("*");
|
||||||
startGamePacket.setInventoriesServerAuthoritative(true);
|
startGamePacket.setInventoriesServerAuthoritative(true);
|
||||||
|
|
|
@ -27,12 +27,14 @@ 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.entity.type.player.SkullPlayerEntity;
|
import org.geysermc.geyser.entity.type.player.SkullPlayerEntity;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.translator.level.block.entity.SkullBlockEntityTranslator;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@ -75,6 +77,17 @@ public class SkullCache {
|
||||||
Skull skull = skulls.computeIfAbsent(position, Skull::new);
|
Skull skull = skulls.computeIfAbsent(position, Skull::new);
|
||||||
skull.texturesProperty = texturesProperty;
|
skull.texturesProperty = texturesProperty;
|
||||||
skull.blockState = blockState;
|
skull.blockState = blockState;
|
||||||
|
if (skull.customRuntimeId != -1) {
|
||||||
|
skull.customRuntimeId = -1;
|
||||||
|
|
||||||
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
||||||
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||||
|
updateBlockPacket.setBlockPosition(position);
|
||||||
|
updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(blockState));
|
||||||
|
updateBlockPacket.setDataLayer(0);
|
||||||
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
|
}
|
||||||
|
|
||||||
if (skull.entity != null) {
|
if (skull.entity != null) {
|
||||||
skull.entity.updateSkull(skull);
|
skull.entity.updateSkull(skull);
|
||||||
|
@ -106,6 +119,24 @@ public class SkullCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
@ -122,6 +153,20 @@ public class SkullCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int updateSkull(Vector3i position, int blockState) {
|
||||||
|
Skull skull = skulls.remove(position);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return customRuntimeId;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
public void updateVisibleSkulls() {
|
public void updateVisibleSkulls() {
|
||||||
if (cullingEnabled) {
|
if (cullingEnabled) {
|
||||||
// No need to recheck skull visibility for small movements
|
// No need to recheck skull visibility for small movements
|
||||||
|
@ -132,6 +177,10 @@ public class SkullCache {
|
||||||
|
|
||||||
inRangeSkulls.clear();
|
inRangeSkulls.clear();
|
||||||
for (Skull skull : skulls.values()) {
|
for (Skull skull : skulls.values()) {
|
||||||
|
if (skull.customRuntimeId != -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
skull.distanceSquared = skull.position.distanceSquared(lastPlayerPosition.getX(), lastPlayerPosition.getY(), lastPlayerPosition.getZ());
|
skull.distanceSquared = skull.position.distanceSquared(lastPlayerPosition.getX(), lastPlayerPosition.getY(), lastPlayerPosition.getZ());
|
||||||
if (skull.distanceSquared > skullRenderDistanceSquared) {
|
if (skull.distanceSquared > skullRenderDistanceSquared) {
|
||||||
freeSkullEntity(skull);
|
freeSkullEntity(skull);
|
||||||
|
@ -203,6 +252,7 @@ public class SkullCache {
|
||||||
public static class Skull {
|
public static class Skull {
|
||||||
private String texturesProperty;
|
private String texturesProperty;
|
||||||
private int blockState;
|
private int blockState;
|
||||||
|
private int customRuntimeId = -1;
|
||||||
private SkullPlayerEntity entity;
|
private SkullPlayerEntity entity;
|
||||||
|
|
||||||
private final Vector3i position;
|
private final Vector3i position;
|
||||||
|
|
|
@ -254,7 +254,7 @@ public class SkinManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GameProfileData loadFromJson(String encodedJson) throws IOException {
|
public static GameProfileData loadFromJson(String encodedJson) throws IOException {
|
||||||
JsonNode skinObject = GeyserImpl.JSON_MAPPER.readTree(new String(Base64.getDecoder().decode(encodedJson), StandardCharsets.UTF_8));
|
JsonNode skinObject = GeyserImpl.JSON_MAPPER.readTree(new String(Base64.getDecoder().decode(encodedJson), StandardCharsets.UTF_8));
|
||||||
JsonNode textures = skinObject.get("textures");
|
JsonNode textures = skinObject.get("textures");
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,8 @@ 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;
|
||||||
|
@ -45,11 +47,13 @@ import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
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;
|
||||||
|
import org.geysermc.geyser.skin.SkinManager;
|
||||||
import org.geysermc.geyser.text.MinecraftLocale;
|
import org.geysermc.geyser.text.MinecraftLocale;
|
||||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||||
import org.geysermc.geyser.util.FileUtils;
|
import org.geysermc.geyser.util.FileUtils;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -175,6 +179,10 @@ public abstract class ItemTranslator {
|
||||||
|
|
||||||
translateCustomItem(nbt, builder, bedrockItem);
|
translateCustomItem(nbt, builder, bedrockItem);
|
||||||
|
|
||||||
|
if (bedrockItem == session.getItemMappings().getStoredItems().playerHead()) {
|
||||||
|
translatePlayerHead(session, 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");
|
||||||
|
@ -542,6 +550,50 @@ public abstract class ItemTranslator {
|
||||||
builder.id(bedrockId);
|
builder.id(bedrockId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void translatePlayerHead(GeyserSession session, CompoundTag nbt, ItemData.Builder builder, ItemMapping mapping) {
|
||||||
|
if (nbt != null && nbt.contains("SkullOwner")) {
|
||||||
|
String skinHash = null;
|
||||||
|
Tag skullOwner = nbt.get("SkullOwner");
|
||||||
|
if (skullOwner instanceof StringTag) {
|
||||||
|
// It's a username give up d:
|
||||||
|
return;
|
||||||
|
} else if (skullOwner instanceof CompoundTag) {
|
||||||
|
Tag properties = ((CompoundTag) skullOwner).get("Properties");
|
||||||
|
if (properties instanceof CompoundTag) {
|
||||||
|
Tag textures = ((CompoundTag) properties).get("textures");
|
||||||
|
if (textures instanceof ListTag) {
|
||||||
|
List<Tag> textureTags = ((ListTag) textures).getValue();
|
||||||
|
if (!textureTags.isEmpty()) {
|
||||||
|
Tag skinTag = textureTags.get(0);
|
||||||
|
if (skinTag instanceof CompoundTag) {
|
||||||
|
Tag valueTag = ((CompoundTag) skinTag).get("Value");
|
||||||
|
String encodedJson = (String) valueTag.getValue();
|
||||||
|
try {
|
||||||
|
SkinManager.GameProfileData data = SkinManager.GameProfileData.loadFromJson(encodedJson);
|
||||||
|
skinHash = data.skinUrl().substring(data.skinUrl().lastIndexOf('/') + 1);
|
||||||
|
} catch (IOException e) {
|
||||||
|
session.getGeyser().getLogger().debug("Not sure how to handle skull head item display. " + nbt + " " + e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skinHash == null) {
|
||||||
|
session.getGeyser().getLogger().debug("Not sure how to handle skull head item display. " + nbt);
|
||||||
|
}
|
||||||
|
GeyserConvertSkullInventoryEvent skullInventoryEvent = new GeyserConvertSkullInventoryEvent(skinHash);
|
||||||
|
GeyserImpl.getInstance().getEventBus().fire(skullInventoryEvent);
|
||||||
|
|
||||||
|
CustomBlockData replacementBlock = skullInventoryEvent.replacementBlock();
|
||||||
|
if (replacementBlock != null) {
|
||||||
|
int bedrockId = session.getItemMappings().getCustomBlockItemIds().getInt(replacementBlock);
|
||||||
|
builder.id(bedrockId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static int getCustomItem(CompoundTag nbt, ItemMapping mapping) {
|
private static int getCustomItem(CompoundTag nbt, ItemMapping mapping) {
|
||||||
if (nbt == null) {
|
if (nbt == null) {
|
||||||
|
|
|
@ -31,12 +31,17 @@ 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 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.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;
|
||||||
|
|
||||||
@BlockEntity(type = BlockEntityType.SKULL)
|
@BlockEntity(type = BlockEntityType.SKULL)
|
||||||
public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
|
public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
|
||||||
|
@ -69,18 +74,65 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void translateSkull(GeyserSession session, CompoundTag tag, int posX, int posY, int posZ, int blockState) {
|
public static int translateSkull(GeyserSession session, CompoundTag tag, int posX, int posY, int posZ, int blockState) {
|
||||||
Vector3i blockPosition = Vector3i.from(posX, posY, posZ);
|
try {
|
||||||
getTextures(tag).whenComplete((texturesProperty, throwable) -> {
|
String texturesProperty = getTextures(tag).get();
|
||||||
|
Vector3i blockPosition = Vector3i.from(posX, posY, posZ);
|
||||||
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;
|
return -1;
|
||||||
}
|
}
|
||||||
if (session.getEventLoop().inEventLoop()) {
|
int runtimeId = translateCustomSkull(session, blockPosition, texturesProperty, blockState);
|
||||||
|
if (runtimeId == -1) {
|
||||||
session.getSkullCache().putSkull(blockPosition, texturesProperty, blockState);
|
session.getSkullCache().putSkull(blockPosition, texturesProperty, blockState);
|
||||||
} else {
|
} else {
|
||||||
session.executeInEventLoop(() -> session.getSkullCache().putSkull(blockPosition, texturesProperty, blockState));
|
session.getSkullCache().putSkull(blockPosition, texturesProperty, blockState, runtimeId);
|
||||||
}
|
}
|
||||||
});
|
return runtimeId;
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
String skinUrl = gameProfileData.skinUrl();
|
||||||
|
String skinHash = skinUrl.substring(skinUrl.lastIndexOf('/') + 1);
|
||||||
|
|
||||||
|
byte floorRotation = BlockStateValues.getSkullRotation(blockState);
|
||||||
|
GeyserConvertSkullEvent.WallDirection wallDirection = GeyserConvertSkullEvent.WallDirection.INVALID;
|
||||||
|
boolean onFloor = true;
|
||||||
|
if (floorRotation == -1) {
|
||||||
|
// Wall skull
|
||||||
|
onFloor = false;
|
||||||
|
int wallRotation = BlockStateValues.getSkullWallDirections().get(blockState);
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -505,10 +505,18 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||||
*/
|
*/
|
||||||
private void restoreCorrectBlock(GeyserSession session, Vector3i blockPos, InventoryTransactionPacket packet) {
|
private void restoreCorrectBlock(GeyserSession session, Vector3i blockPos, InventoryTransactionPacket packet) {
|
||||||
int javaBlockState = session.getGeyser().getWorldManager().getBlockAt(session, blockPos);
|
int javaBlockState = session.getGeyser().getWorldManager().getBlockAt(session, blockPos);
|
||||||
|
int bedrockId = session.getBlockMappings().getBedrockBlockId(javaBlockState);
|
||||||
|
if (BlockStateValues.getSkullVariant(javaBlockState) == 3) {
|
||||||
|
int customRuntimeId = session.getSkullCache().updateSkull(blockPos, javaBlockState);
|
||||||
|
if (customRuntimeId != -1) {
|
||||||
|
bedrockId = customRuntimeId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
updateBlockPacket.setDataLayer(0);
|
updateBlockPacket.setDataLayer(0);
|
||||||
updateBlockPacket.setBlockPosition(blockPos);
|
updateBlockPacket.setBlockPosition(blockPos);
|
||||||
updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(javaBlockState));
|
updateBlockPacket.setRuntimeId(bedrockId);
|
||||||
updateBlockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
|
updateBlockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
|
||||||
session.sendUpstreamPacket(updateBlockPacket);
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.Clientb
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
|
||||||
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
|
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
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.translator.level.block.entity.BlockEntityTranslator;
|
import org.geysermc.geyser.translator.level.block.entity.BlockEntityTranslator;
|
||||||
|
@ -63,8 +64,23 @@ 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;
|
||||||
if (session.getPreferencesCache().showCustomSkulls() && packet.getNbt() != null && packet.getNbt().contains("SkullOwner")) {
|
if (session.getPreferencesCache().showCustomSkulls() && packet.getNbt() != null && packet.getNbt().contains("SkullOwner")) {
|
||||||
SkullBlockEntityTranslator.translateSkull(session, packet.getNbt(), position.getX(), position.getY(), position.getZ(), blockState);
|
int runtimeId = SkullBlockEntityTranslator.translateSkull(session, packet.getNbt(), position.getX(), position.getY(), position.getZ(), blockState);
|
||||||
|
if (runtimeId != -1) {
|
||||||
|
customSkull = true;
|
||||||
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
|
updateBlockPacket.setDataLayer(0);
|
||||||
|
updateBlockPacket.setBlockPosition(position);
|
||||||
|
updateBlockPacket.setRuntimeId(runtimeId);
|
||||||
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
|
||||||
|
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
|
||||||
|
session.sendUpstreamPacket(updateBlockPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!customSkull) {
|
||||||
|
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(type, position.getX(), position.getY(), position.getZ(),
|
||||||
|
packet.getNbt(), blockState), packet.getPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If block entity is command block, OP permission level is appropriate, player is in creative mode and the NBT is not empty
|
// If block entity is command block, OP permission level is appropriate, player is in creative mode and the NBT is not empty
|
||||||
|
|
|
@ -46,6 +46,7 @@ import io.netty.buffer.ByteBufAllocator;
|
||||||
import io.netty.buffer.ByteBufOutputStream;
|
import io.netty.buffer.ByteBufOutputStream;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntImmutableList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntLists;
|
import it.unimi.dsi.fastutil.ints.IntLists;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
|
@ -273,7 +274,18 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
||||||
|
|
||||||
// Check for custom skulls
|
// Check for custom skulls
|
||||||
if (session.getPreferencesCache().showCustomSkulls() && type == BlockEntityType.SKULL && tag != null && tag.contains("SkullOwner")) {
|
if (session.getPreferencesCache().showCustomSkulls() && type == BlockEntityType.SKULL && tag != null && tag.contains("SkullOwner")) {
|
||||||
SkullBlockEntityTranslator.translateSkull(session, tag, x + chunkBlockX, y, z + chunkBlockZ, blockState);
|
int runtimeId = SkullBlockEntityTranslator.translateSkull(session, tag, x + chunkBlockX, y, z + chunkBlockZ, blockState);
|
||||||
|
if (runtimeId != -1) {
|
||||||
|
int bedrockSectionY = (y >> 4) - (bedrockDimension.minY() >> 4);
|
||||||
|
GeyserChunkSection bedrockSection = sections[bedrockSectionY];
|
||||||
|
IntList palette = bedrockSection.getBlockStorageArray()[0].getPalette();
|
||||||
|
if (palette instanceof IntImmutableList || palette instanceof IntLists.Singleton) {
|
||||||
|
// TODO there has to be a better way to expand the palette .-.
|
||||||
|
bedrockSection = bedrockSection.copy();
|
||||||
|
sections[bedrockSectionY] = bedrockSection;
|
||||||
|
}
|
||||||
|
bedrockSection.setFullBlock(x, y & 0xF, z, 0, runtimeId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,16 +135,21 @@ public class ChunkUtils {
|
||||||
// Otherwise, let's still store our reference to the item frame, but let the new block take precedence for now
|
// Otherwise, let's still store our reference to the item frame, but let the new block take precedence for now
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BlockStateValues.getSkullVariant(blockState) == -1) {
|
int blockId = session.getBlockMappings().getBedrockBlockId(blockState);
|
||||||
|
int skullVariant = BlockStateValues.getSkullVariant(blockState);
|
||||||
|
if (skullVariant == -1) {
|
||||||
// Skull is gone
|
// Skull is gone
|
||||||
session.getSkullCache().removeSkull(position);
|
session.getSkullCache().removeSkull(position);
|
||||||
|
} else if (skullVariant == 3) {
|
||||||
|
int customRuntimeId = session.getSkullCache().updateSkull(position, blockState);
|
||||||
|
if (customRuntimeId != -1) {
|
||||||
|
blockId = customRuntimeId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent moving_piston from being placed
|
// Prevent moving_piston from being placed
|
||||||
// It's used for extending piston heads, but it isn't needed on Bedrock and causes pistons to flicker
|
// It's used for extending piston heads, but it isn't needed on Bedrock and causes pistons to flicker
|
||||||
if (!BlockStateValues.isMovingPiston(blockState)) {
|
if (!BlockStateValues.isMovingPiston(blockState)) {
|
||||||
int blockId = session.getBlockMappings().getBedrockBlockId(blockState);
|
|
||||||
|
|
||||||
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
|
||||||
updateBlockPacket.setDataLayer(0);
|
updateBlockPacket.setDataLayer(0);
|
||||||
updateBlockPacket.setBlockPosition(position);
|
updateBlockPacket.setBlockPosition(position);
|
||||||
|
|
Loading…
Reference in a new issue