mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-12-27 15:00:28 +01:00
Custom item support for extensions (#2822)
Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com>
This commit is contained in:
parent
474153fd51
commit
36c49a7256
43 changed files with 2536 additions and 74 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -249,4 +249,5 @@ locales/
|
||||||
/packs/
|
/packs/
|
||||||
/dump.json
|
/dump.json
|
||||||
/saved-refresh-tokens.json
|
/saved-refresh-tokens.json
|
||||||
/languages/
|
/custom_mappings/
|
||||||
|
/languages/
|
||||||
|
|
|
@ -116,7 +116,7 @@ public interface GeyserApi extends GeyserApiBase {
|
||||||
EventBus eventBus();
|
EventBus eventBus();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get's the default {@link RemoteServer} configured
|
* Gets the default {@link RemoteServer} configured
|
||||||
* within the config file that is used by default.
|
* within the config file that is used by default.
|
||||||
*
|
*
|
||||||
* @return the default remote server used within Geyser
|
* @return the default remote server used within Geyser
|
||||||
|
|
|
@ -76,7 +76,7 @@ public interface EventBus {
|
||||||
void unregisterAll(@NonNull Extension extension);
|
void unregisterAll(@NonNull Extension extension);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fires the given {@link Event}.
|
* Fires the given {@link Event} and returns the result.
|
||||||
*
|
*
|
||||||
* @param event the event to fire
|
* @param event the event to fire
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* 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 com.google.common.collect.Multimap;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.event.Event;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||||
|
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on Geyser's startup when looking for custom items. Custom items must be registered through this event.
|
||||||
|
*
|
||||||
|
* This event will not be called if the "add non-Bedrock items" setting is disabled in the Geyser config.
|
||||||
|
*/
|
||||||
|
public abstract class GeyserDefineCustomItemsEvent implements Event {
|
||||||
|
private final Multimap<String, CustomItemData> customItems;
|
||||||
|
private final List<NonVanillaCustomItemData> nonVanillaCustomItems;
|
||||||
|
|
||||||
|
public GeyserDefineCustomItemsEvent(Multimap<String, CustomItemData> customItems, List<NonVanillaCustomItemData> nonVanillaCustomItems) {
|
||||||
|
this.customItems = customItems;
|
||||||
|
this.nonVanillaCustomItems = nonVanillaCustomItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a multimap of all the already registered custom items indexed by the item's extended java item's identifier.
|
||||||
|
*
|
||||||
|
* @return a multimap of all the already registered custom items
|
||||||
|
*/
|
||||||
|
public Map<String, Collection<CustomItemData>> getExistingCustomItems() {
|
||||||
|
return Collections.unmodifiableMap(this.customItems.asMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of the already registered non-vanilla custom items.
|
||||||
|
*
|
||||||
|
* @return the list of the already registered non-vanilla custom items
|
||||||
|
*/
|
||||||
|
public List<NonVanillaCustomItemData> getExistingNonVanillaCustomItems() {
|
||||||
|
return Collections.unmodifiableList(this.nonVanillaCustomItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a custom item with a base Java item. This is used to register items with custom textures and properties
|
||||||
|
* based on NBT data.
|
||||||
|
*
|
||||||
|
* @param identifier the base (java) item
|
||||||
|
* @param customItemData the custom item data to register
|
||||||
|
* @return if the item was registered
|
||||||
|
*/
|
||||||
|
public abstract boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a custom item with no base item. This is used for mods.
|
||||||
|
*
|
||||||
|
* @param customItemData the custom item data to register
|
||||||
|
* @return if the item was registered
|
||||||
|
*/
|
||||||
|
public abstract boolean register(@NonNull NonVanillaCustomItemData customItemData);
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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.event.Event;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when resource packs are loaded within Geyser.
|
||||||
|
*
|
||||||
|
* @param resourcePacks a mutable list of the currently listed resource packs
|
||||||
|
*/
|
||||||
|
public record GeyserLoadResourcePacksEvent(@NonNull List<Path> resourcePacks) implements Event {
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* 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.item.custom;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to store data for a custom item.
|
||||||
|
*/
|
||||||
|
public interface CustomItemData {
|
||||||
|
/**
|
||||||
|
* Gets the item's name.
|
||||||
|
*
|
||||||
|
* @return the item's name
|
||||||
|
*/
|
||||||
|
@NonNull String name();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the custom item options of the item.
|
||||||
|
*
|
||||||
|
* @return the custom item options of the item.
|
||||||
|
*/
|
||||||
|
CustomItemOptions customItemOptions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's display name. By default, this is the item's name.
|
||||||
|
*
|
||||||
|
* @return the item's display name
|
||||||
|
*/
|
||||||
|
@NonNull String displayName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's icon. By default, this is the item's name.
|
||||||
|
*
|
||||||
|
* @return the item's icon
|
||||||
|
*/
|
||||||
|
@NonNull String icon();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if the item is allowed to be put into the offhand.
|
||||||
|
*
|
||||||
|
* @return true if the item is allowed to be used in the offhand, false otherwise
|
||||||
|
*/
|
||||||
|
boolean allowOffhand();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's texture size. This is to resize the item if the texture is not 16x16.
|
||||||
|
*
|
||||||
|
* @return the item's texture size
|
||||||
|
*/
|
||||||
|
int textureSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's render offsets. If it is null, the item will be rendered normally, with no offsets.
|
||||||
|
*
|
||||||
|
* @return the item's render offsets
|
||||||
|
*/
|
||||||
|
@Nullable CustomRenderOffsets renderOffsets();
|
||||||
|
|
||||||
|
static CustomItemData.Builder builder() {
|
||||||
|
return GeyserApi.api().providerManager().builderProvider().provideBuilder(CustomItemData.Builder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
/**
|
||||||
|
* Will also set the display name and icon to the provided parameter, if it is currently not set.
|
||||||
|
*/
|
||||||
|
Builder name(@NonNull String name);
|
||||||
|
|
||||||
|
Builder customItemOptions(@NonNull CustomItemOptions customItemOptions);
|
||||||
|
|
||||||
|
Builder displayName(@NonNull String displayName);
|
||||||
|
|
||||||
|
Builder icon(@NonNull String icon);
|
||||||
|
|
||||||
|
Builder allowOffhand(boolean allowOffhand);
|
||||||
|
|
||||||
|
Builder textureSize(int textureSize);
|
||||||
|
|
||||||
|
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
|
||||||
|
|
||||||
|
CustomItemData build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* 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.item.custom;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
import org.geysermc.geyser.api.util.TriState;
|
||||||
|
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents the different ways you can register custom items
|
||||||
|
*/
|
||||||
|
public interface CustomItemOptions {
|
||||||
|
/**
|
||||||
|
* Gets if the item should be unbreakable.
|
||||||
|
*
|
||||||
|
* @return if the item should be unbreakable
|
||||||
|
*/
|
||||||
|
@NonNull TriState unbreakable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's custom model data predicate.
|
||||||
|
*
|
||||||
|
* @return the item's custom model data
|
||||||
|
*/
|
||||||
|
@NonNull OptionalInt customModelData();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's damage predicate.
|
||||||
|
*
|
||||||
|
* @return the item's damage predicate
|
||||||
|
*/
|
||||||
|
@NonNull OptionalInt damagePredicate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the item has at least one option set
|
||||||
|
*
|
||||||
|
* @return true if the item at least one options set
|
||||||
|
*/
|
||||||
|
default boolean hasCustomItemOptions() {
|
||||||
|
return this.unbreakable() != TriState.NOT_SET ||
|
||||||
|
this.customModelData().isPresent() ||
|
||||||
|
this.damagePredicate().isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
static CustomItemOptions.Builder builder() {
|
||||||
|
return GeyserApi.api().providerManager().builderProvider().provideBuilder(CustomItemOptions.Builder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Builder {
|
||||||
|
Builder unbreakable(boolean unbreakable);
|
||||||
|
|
||||||
|
Builder customModelData(int customModelData);
|
||||||
|
|
||||||
|
Builder damagePredicate(int damagePredicate);
|
||||||
|
|
||||||
|
CustomItemOptions build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* 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.item.custom;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is used to store the render offsets of custom items.
|
||||||
|
*/
|
||||||
|
public record CustomRenderOffsets(@Nullable Hand mainHand, @Nullable Hand offhand) {
|
||||||
|
/**
|
||||||
|
* The hand that is used for the offset.
|
||||||
|
*/
|
||||||
|
public record Hand(@Nullable Offset firstPerson, @Nullable Offset thirdPerson) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The offset of the item.
|
||||||
|
*/
|
||||||
|
public record Offset(@Nullable OffsetXYZ position, @Nullable OffsetXYZ rotation, @Nullable OffsetXYZ scale) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* X, Y and Z positions for the offset.
|
||||||
|
*/
|
||||||
|
public record OffsetXYZ(float x, float y, float z) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
/*
|
||||||
|
* 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.item.custom;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.index.qual.NonNegative;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
|
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a completely custom item that is not based on an existing vanilla Minecraft item.
|
||||||
|
*/
|
||||||
|
public interface NonVanillaCustomItemData extends CustomItemData {
|
||||||
|
/**
|
||||||
|
* Gets the java identifier for this item.
|
||||||
|
*
|
||||||
|
* @return The java identifier for this item.
|
||||||
|
*/
|
||||||
|
@NonNull String identifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the java item id of the item.
|
||||||
|
*
|
||||||
|
* @return the java item id of the item
|
||||||
|
*/
|
||||||
|
@NonNegative int javaId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the stack size of the item.
|
||||||
|
*
|
||||||
|
* @return the stack size of the item
|
||||||
|
*/
|
||||||
|
@NonNegative int stackSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the max damage of the item.
|
||||||
|
*
|
||||||
|
* @return the max damage of the item
|
||||||
|
*/
|
||||||
|
int maxDamage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the tool type of the item.
|
||||||
|
*
|
||||||
|
* @return the tool type of the item
|
||||||
|
*/
|
||||||
|
@Nullable String toolType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the tool tier of the item.
|
||||||
|
*
|
||||||
|
* @return the tool tier of the item
|
||||||
|
*/
|
||||||
|
@Nullable String toolTier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the armor type of the item.
|
||||||
|
*
|
||||||
|
* @return the armor type of the item
|
||||||
|
*/
|
||||||
|
@Nullable String armorType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the armor protection value of the item.
|
||||||
|
*
|
||||||
|
* @return the armor protection value of the item
|
||||||
|
*/
|
||||||
|
int protectionValue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's translation string.
|
||||||
|
*
|
||||||
|
* @return the item's translation string
|
||||||
|
*/
|
||||||
|
@Nullable String translationString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the repair materials of the item.
|
||||||
|
*
|
||||||
|
* @return the repair materials of the item
|
||||||
|
*/
|
||||||
|
@Nullable Set<String> repairMaterials();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's creative category, or tab id.
|
||||||
|
*
|
||||||
|
* @return the item's creative category
|
||||||
|
*/
|
||||||
|
@NonNull OptionalInt creativeCategory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the item's creative group.
|
||||||
|
*
|
||||||
|
* @return the item's creative group
|
||||||
|
*/
|
||||||
|
@Nullable String creativeGroup();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if the item is a hat. This is used to determine if the item should be rendered on the player's head, and
|
||||||
|
* normally allow the player to equip it. This is not meant for armor.
|
||||||
|
*
|
||||||
|
* @return if the item is a hat
|
||||||
|
*/
|
||||||
|
boolean isHat();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if the item is a tool. This is used to set the render type of the item, if the item is handheld.
|
||||||
|
*
|
||||||
|
* @return if the item is a tool
|
||||||
|
*/
|
||||||
|
boolean isTool();
|
||||||
|
|
||||||
|
static NonVanillaCustomItemData.Builder builder() {
|
||||||
|
return GeyserApi.api().providerManager().builderProvider().provideBuilder(NonVanillaCustomItemData.Builder.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Builder extends CustomItemData.Builder {
|
||||||
|
Builder name(@NonNull String name);
|
||||||
|
|
||||||
|
Builder identifier(@NonNull String identifier);
|
||||||
|
|
||||||
|
Builder javaId(@NonNegative int javaId);
|
||||||
|
|
||||||
|
Builder stackSize(@NonNegative int stackSize);
|
||||||
|
|
||||||
|
Builder maxDamage(int maxDamage);
|
||||||
|
|
||||||
|
Builder toolType(@Nullable String toolType);
|
||||||
|
|
||||||
|
Builder toolTier(@Nullable String toolTier);
|
||||||
|
|
||||||
|
Builder armorType(@Nullable String armorType);
|
||||||
|
|
||||||
|
Builder protectionValue(int protectionValue);
|
||||||
|
|
||||||
|
Builder translationString(@Nullable String translationString);
|
||||||
|
|
||||||
|
Builder repairMaterials(@Nullable Set<String> repairMaterials);
|
||||||
|
|
||||||
|
Builder creativeCategory(int creativeCategory);
|
||||||
|
|
||||||
|
Builder creativeGroup(@Nullable String creativeGroup);
|
||||||
|
|
||||||
|
Builder hat(boolean isHat);
|
||||||
|
|
||||||
|
Builder tool(boolean isTool);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder displayName(@NonNull String displayName);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder allowOffhand(boolean allowOffhand);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder textureSize(int textureSize);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Builder renderOffsets(@Nullable CustomRenderOffsets renderOffsets);
|
||||||
|
|
||||||
|
NonVanillaCustomItemData build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a way to represent a boolean, but with a non set value added.
|
||||||
|
* This class was inspired by adventure's version https://github.com/KyoriPowered/adventure/blob/main/4/api/src/main/java/net/kyori/adventure/util/TriState.java
|
||||||
|
*/
|
||||||
|
public enum TriState {
|
||||||
|
/**
|
||||||
|
* Describes a value that is not set, null, or not present.
|
||||||
|
*/
|
||||||
|
NOT_SET,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a true value.
|
||||||
|
*/
|
||||||
|
TRUE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes a false value.
|
||||||
|
*/
|
||||||
|
FALSE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the TriState to a boolean.
|
||||||
|
*
|
||||||
|
* @return the boolean value of the TriState
|
||||||
|
*/
|
||||||
|
public @Nullable Boolean toBoolean() {
|
||||||
|
return switch (this) {
|
||||||
|
case TRUE -> true;
|
||||||
|
case FALSE -> false;
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a TriState from a boolean.
|
||||||
|
*
|
||||||
|
* @param value the Boolean value
|
||||||
|
* @return the created TriState
|
||||||
|
*/
|
||||||
|
public static @NonNull TriState fromBoolean(@Nullable Boolean value) {
|
||||||
|
return value == null ? NOT_SET : fromBoolean(value.booleanValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a TriState from a primitive boolean.
|
||||||
|
*
|
||||||
|
* @param value the boolean value
|
||||||
|
* @return the created TriState
|
||||||
|
*/
|
||||||
|
public @NonNull static TriState fromBoolean(boolean value) {
|
||||||
|
return value ? TRUE : FALSE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer
|
import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer
|
||||||
|
|
||||||
val terminalConsoleVersion = "1.2.0"
|
val terminalConsoleVersion = "1.2.0"
|
||||||
val jlineVersion = "3.10.0"
|
val jlineVersion = "3.21.0"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(projects.core)
|
api(projects.core)
|
||||||
|
|
|
@ -180,22 +180,26 @@ public class GeyserImpl implements GeyserApi {
|
||||||
logger.info("");
|
logger.info("");
|
||||||
logger.info("******************************************");
|
logger.info("******************************************");
|
||||||
|
|
||||||
/* Initialize translators and registries */
|
/* Initialize event bus */
|
||||||
BlockRegistries.init();
|
this.eventBus = new GeyserEventBus();
|
||||||
Registries.init();
|
|
||||||
|
|
||||||
|
/* Load Extensions */
|
||||||
|
this.extensionManager = new GeyserExtensionManager();
|
||||||
|
this.extensionManager.init();
|
||||||
|
|
||||||
|
this.extensionManager.enableExtensions();
|
||||||
|
this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus));
|
||||||
|
|
||||||
|
/* Initialize registries */
|
||||||
|
Registries.init();
|
||||||
|
BlockRegistries.init();
|
||||||
|
|
||||||
|
/* Initialize translators */
|
||||||
EntityDefinitions.init();
|
EntityDefinitions.init();
|
||||||
ItemTranslator.init();
|
ItemTranslator.init();
|
||||||
MessageTranslator.init();
|
MessageTranslator.init();
|
||||||
MinecraftLocale.init();
|
MinecraftLocale.init();
|
||||||
|
|
||||||
/* Load Extensions */
|
|
||||||
this.eventBus = new GeyserEventBus();
|
|
||||||
this.extensionManager = new GeyserExtensionManager();
|
|
||||||
this.extensionManager.init();
|
|
||||||
|
|
||||||
this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus));
|
|
||||||
|
|
||||||
start();
|
start();
|
||||||
|
|
||||||
GeyserConfiguration config = bootstrap.getGeyserConfig();
|
GeyserConfiguration config = bootstrap.getGeyserConfig();
|
||||||
|
@ -256,8 +260,6 @@ public class GeyserImpl implements GeyserApi {
|
||||||
|
|
||||||
ResourcePack.loadPacks();
|
ResourcePack.loadPacks();
|
||||||
|
|
||||||
this.extensionManager.enableExtensions();
|
|
||||||
|
|
||||||
if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) {
|
if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) {
|
||||||
// Set the remote address to localhost since that is where we are always connecting
|
// Set the remote address to localhost since that is where we are always connecting
|
||||||
try {
|
try {
|
||||||
|
@ -580,6 +582,7 @@ public class GeyserImpl implements GeyserApi {
|
||||||
@Override
|
@Override
|
||||||
public void reload() {
|
public void reload() {
|
||||||
shutdown();
|
shutdown();
|
||||||
|
this.extensionManager.enableExtensions();
|
||||||
bootstrap.onEnable();
|
bootstrap.onEnable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -615,7 +618,6 @@ public class GeyserImpl implements GeyserApi {
|
||||||
return this.eventBus;
|
return this.eventBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public RemoteServer defaultRemoteServer() {
|
public RemoteServer defaultRemoteServer() {
|
||||||
return this.remoteServer;
|
return this.remoteServer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,6 @@ import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.geyser.entity.EntityDefinition;
|
import org.geysermc.geyser.entity.EntityDefinition;
|
||||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
|
import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
|
||||||
import org.geysermc.geyser.util.InteractionResult;
|
import org.geysermc.geyser.util.InteractionResult;
|
||||||
|
@ -114,7 +113,9 @@ public class ItemFrameEntity extends Entity {
|
||||||
if (entityMetadata.getValue() != null) {
|
if (entityMetadata.getValue() != null) {
|
||||||
this.heldItem = entityMetadata.getValue();
|
this.heldItem = entityMetadata.getValue();
|
||||||
ItemData itemData = ItemTranslator.translateToBedrock(session, heldItem);
|
ItemData itemData = ItemTranslator.translateToBedrock(session, heldItem);
|
||||||
ItemMapping mapping = session.getItemMappings().getMapping(entityMetadata.getValue());
|
|
||||||
|
String customIdentifier = session.getItemMappings().getCustomIdMappings().get(itemData.getId());
|
||||||
|
|
||||||
NbtMapBuilder builder = NbtMap.builder();
|
NbtMapBuilder builder = NbtMap.builder();
|
||||||
|
|
||||||
builder.putByte("Count", (byte) itemData.getCount());
|
builder.putByte("Count", (byte) itemData.getCount());
|
||||||
|
@ -122,7 +123,7 @@ public class ItemFrameEntity extends Entity {
|
||||||
builder.put("tag", itemData.getTag());
|
builder.put("tag", itemData.getTag());
|
||||||
}
|
}
|
||||||
builder.putShort("Damage", (short) itemData.getDamage());
|
builder.putShort("Damage", (short) itemData.getDamage());
|
||||||
builder.putString("Name", mapping.getBedrockIdentifier());
|
builder.putString("Name", customIdentifier != null ? customIdentifier : session.getItemMappings().getMapping(entityMetadata.getValue()).getBedrockIdentifier());
|
||||||
NbtMapBuilder tag = getDefaultTag().toBuilder();
|
NbtMapBuilder tag = getDefaultTag().toBuilder();
|
||||||
tag.put("Item", builder.build());
|
tag.put("Item", builder.build());
|
||||||
tag.putFloat("ItemDropChance", 1.0f);
|
tag.putFloat("ItemDropChance", 1.0f);
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.extension;
|
package org.geysermc.geyser.extension;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import net.kyori.adventure.key.Key;
|
import net.kyori.adventure.key.Key;
|
||||||
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;
|
||||||
|
@ -32,7 +33,6 @@ import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.extension.Extension;
|
import org.geysermc.geyser.api.extension.Extension;
|
||||||
import org.geysermc.geyser.api.extension.ExtensionLoader;
|
import org.geysermc.geyser.api.extension.ExtensionLoader;
|
||||||
import org.geysermc.geyser.api.extension.ExtensionManager;
|
import org.geysermc.geyser.api.extension.ExtensionManager;
|
||||||
import org.geysermc.geyser.registry.Registries;
|
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -44,13 +44,15 @@ import java.util.stream.Collectors;
|
||||||
public class GeyserExtensionManager extends ExtensionManager {
|
public class GeyserExtensionManager extends ExtensionManager {
|
||||||
private static final Key BASE_EXTENSION_LOADER_KEY = Key.key("geysermc", "base");
|
private static final Key BASE_EXTENSION_LOADER_KEY = Key.key("geysermc", "base");
|
||||||
|
|
||||||
|
private final Map<Key, ExtensionLoader> extensionLoaderTypes = new Object2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
private final Map<String, Extension> extensions = new LinkedHashMap<>();
|
private final Map<String, Extension> extensions = new LinkedHashMap<>();
|
||||||
private final Map<Extension, ExtensionLoader> extensionsLoaders = new LinkedHashMap<>();
|
private final Map<Extension, ExtensionLoader> extensionsLoaders = new LinkedHashMap<>();
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.load.loading"));
|
GeyserImpl.getInstance().getLogger().info(GeyserLocale.getLocaleStringLog("geyser.extensions.load.loading"));
|
||||||
|
|
||||||
Registries.EXTENSION_LOADERS.register(BASE_EXTENSION_LOADER_KEY, new GeyserExtensionLoader());
|
extensionLoaderTypes.put(BASE_EXTENSION_LOADER_KEY, new GeyserExtensionLoader());
|
||||||
for (ExtensionLoader loader : this.extensionLoaders().values()) {
|
for (ExtensionLoader loader : this.extensionLoaders().values()) {
|
||||||
this.loadAllExtensions(loader);
|
this.loadAllExtensions(loader);
|
||||||
}
|
}
|
||||||
|
@ -98,6 +100,12 @@ public class GeyserExtensionManager extends ExtensionManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void enableExtensions() {
|
||||||
|
for (Extension extension : this.extensions()) {
|
||||||
|
this.enable(extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void disableExtension(@NonNull Extension extension) {
|
private void disableExtension(@NonNull Extension extension) {
|
||||||
if (extension.isEnabled()) {
|
if (extension.isEnabled()) {
|
||||||
GeyserImpl.getInstance().eventBus().unregisterAll(extension);
|
GeyserImpl.getInstance().eventBus().unregisterAll(extension);
|
||||||
|
@ -107,12 +115,6 @@ public class GeyserExtensionManager extends ExtensionManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableExtensions() {
|
|
||||||
for (Extension extension : this.extensions()) {
|
|
||||||
this.enable(extension);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disableExtensions() {
|
public void disableExtensions() {
|
||||||
for (Extension extension : this.extensions()) {
|
for (Extension extension : this.extensions()) {
|
||||||
this.disable(extension);
|
this.disable(extension);
|
||||||
|
@ -133,18 +135,18 @@ public class GeyserExtensionManager extends ExtensionManager {
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public ExtensionLoader extensionLoader(@NonNull String identifier) {
|
public ExtensionLoader extensionLoader(@NonNull String identifier) {
|
||||||
return Registries.EXTENSION_LOADERS.get(Key.key(identifier));
|
return this.extensionLoaderTypes.get(Key.key(identifier));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerExtensionLoader(@NonNull String identifier, @NonNull ExtensionLoader extensionLoader) {
|
public void registerExtensionLoader(@NonNull String identifier, @NonNull ExtensionLoader extensionLoader) {
|
||||||
Registries.EXTENSION_LOADERS.register(Key.key(identifier), extensionLoader);
|
this.extensionLoaderTypes.put(Key.key(identifier), extensionLoader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Map<String, ExtensionLoader> extensionLoaders() {
|
public Map<String, ExtensionLoader> extensionLoaders() {
|
||||||
return Registries.EXTENSION_LOADERS.get().entrySet().stream().collect(Collectors.toMap(key -> key.getKey().asString(), Map.Entry::getValue));
|
return this.extensionLoaderTypes.entrySet().stream().collect(Collectors.toMap(key -> key.getKey().asString(), Map.Entry::getValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
/*
|
||||||
|
* 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.item;
|
||||||
|
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomRenderOffsets;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
@EqualsAndHashCode
|
||||||
|
@ToString
|
||||||
|
public class GeyserCustomItemData implements CustomItemData {
|
||||||
|
private final String name;
|
||||||
|
private final CustomItemOptions customItemOptions;
|
||||||
|
private final String displayName;
|
||||||
|
private final String icon;
|
||||||
|
private final boolean allowOffhand;
|
||||||
|
private final int textureSize;
|
||||||
|
private final CustomRenderOffsets renderOffsets;
|
||||||
|
|
||||||
|
public GeyserCustomItemData(String name,
|
||||||
|
CustomItemOptions customItemOptions,
|
||||||
|
String displayName,
|
||||||
|
String icon,
|
||||||
|
boolean allowOffhand,
|
||||||
|
int textureSize,
|
||||||
|
CustomRenderOffsets renderOffsets) {
|
||||||
|
this.name = name;
|
||||||
|
this.customItemOptions = customItemOptions;
|
||||||
|
this.displayName = displayName;
|
||||||
|
this.icon = icon;
|
||||||
|
this.allowOffhand = allowOffhand;
|
||||||
|
this.textureSize = textureSize;
|
||||||
|
this.renderOffsets = renderOffsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull String name() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomItemOptions customItemOptions() {
|
||||||
|
return customItemOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull String displayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull String icon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean allowOffhand() {
|
||||||
|
return allowOffhand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int textureSize() {
|
||||||
|
return textureSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomRenderOffsets renderOffsets() {
|
||||||
|
return renderOffsets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CustomItemDataBuilder implements Builder {
|
||||||
|
protected String name = null;
|
||||||
|
protected CustomItemOptions customItemOptions = null;
|
||||||
|
|
||||||
|
protected String displayName = null;
|
||||||
|
protected String icon = null;
|
||||||
|
protected boolean allowOffhand = true; // Bedrock doesn't give items offhand allowance unless they serve gameplay purpose, but we want to be friendly with Java
|
||||||
|
protected int textureSize = 16;
|
||||||
|
protected CustomRenderOffsets renderOffsets = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder name(@NonNull String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder customItemOptions(@NonNull CustomItemOptions customItemOptions) {
|
||||||
|
this.customItemOptions = customItemOptions;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder displayName(@NonNull String displayName) {
|
||||||
|
this.displayName = displayName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder icon(@NonNull String icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder allowOffhand(boolean allowOffhand) {
|
||||||
|
this.allowOffhand = allowOffhand;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder textureSize(int textureSize) {
|
||||||
|
this.textureSize = textureSize;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder renderOffsets(CustomRenderOffsets renderOffsets) {
|
||||||
|
this.renderOffsets = renderOffsets;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomItemData build() {
|
||||||
|
if (this.name == null || this.customItemOptions == null) {
|
||||||
|
throw new IllegalArgumentException("Name and custom item options must be set");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.displayName == null) {
|
||||||
|
this.displayName = this.name;
|
||||||
|
}
|
||||||
|
if (this.icon == null) {
|
||||||
|
this.icon = this.name;
|
||||||
|
}
|
||||||
|
return new GeyserCustomItemData(this.name, this.customItemOptions, this.displayName, this.icon, this.allowOffhand, this.textureSize, this.renderOffsets);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* 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.item;
|
||||||
|
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||||
|
import org.geysermc.geyser.api.util.TriState;
|
||||||
|
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
|
||||||
|
public record GeyserCustomItemOptions(TriState unbreakable,
|
||||||
|
OptionalInt customModelData,
|
||||||
|
OptionalInt damagePredicate) implements CustomItemOptions {
|
||||||
|
|
||||||
|
public static class CustomItemOptionsBuilder implements CustomItemOptions.Builder {
|
||||||
|
private TriState unbreakable = TriState.NOT_SET;
|
||||||
|
private OptionalInt customModelData = OptionalInt.empty();
|
||||||
|
private OptionalInt damagePredicate = OptionalInt.empty();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder unbreakable(boolean unbreakable) {
|
||||||
|
if (unbreakable) {
|
||||||
|
this.unbreakable = TriState.TRUE;
|
||||||
|
} else {
|
||||||
|
this.unbreakable = TriState.FALSE;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder customModelData(int customModelData) {
|
||||||
|
this.customModelData = OptionalInt.of(customModelData);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder damagePredicate(int damagePredicate) {
|
||||||
|
this.damagePredicate = OptionalInt.of(damagePredicate);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomItemOptions build() {
|
||||||
|
return new GeyserCustomItemOptions(unbreakable, customModelData, damagePredicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.item;
|
||||||
|
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
|
||||||
|
|
||||||
|
public record GeyserCustomMappingData(ComponentItemData componentItemData, StartGamePacket.ItemEntry startGamePacketItemEntry, String stringId, int integerId) {
|
||||||
|
}
|
|
@ -0,0 +1,303 @@
|
||||||
|
/*
|
||||||
|
* 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.item;
|
||||||
|
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomRenderOffsets;
|
||||||
|
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ToString
|
||||||
|
public final class GeyserNonVanillaCustomItemData extends GeyserCustomItemData implements NonVanillaCustomItemData {
|
||||||
|
private final String identifier;
|
||||||
|
private final int javaId;
|
||||||
|
private final int stackSize;
|
||||||
|
private final int maxDamage;
|
||||||
|
private final String toolType;
|
||||||
|
private final String toolTier;
|
||||||
|
private final String armorType;
|
||||||
|
private final int protectionValue;
|
||||||
|
private final String translationString;
|
||||||
|
private final Set<String> repairMaterials;
|
||||||
|
private final OptionalInt creativeCategory;
|
||||||
|
private final String creativeGroup;
|
||||||
|
private final boolean isHat;
|
||||||
|
private final boolean isTool;
|
||||||
|
|
||||||
|
public GeyserNonVanillaCustomItemData(NonVanillaCustomItemDataBuilder builder) {
|
||||||
|
super(builder.name, builder.customItemOptions, builder.displayName, builder.icon, builder.allowOffhand,
|
||||||
|
builder.textureSize, builder.renderOffsets);
|
||||||
|
|
||||||
|
this.identifier = builder.identifier;
|
||||||
|
this.javaId = builder.javaId;
|
||||||
|
this.stackSize = builder.stackSize;
|
||||||
|
this.maxDamage = builder.maxDamage;
|
||||||
|
this.toolType = builder.toolType;
|
||||||
|
this.toolTier = builder.toolTier;
|
||||||
|
this.armorType = builder.armorType;
|
||||||
|
this.protectionValue = builder.protectionValue;
|
||||||
|
this.translationString = builder.translationString;
|
||||||
|
this.repairMaterials = builder.repairMaterials;
|
||||||
|
this.creativeCategory = builder.creativeCategory;
|
||||||
|
this.creativeGroup = builder.creativeGroup;
|
||||||
|
this.isHat = builder.hat;
|
||||||
|
this.isTool = builder.tool;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String identifier() {
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int javaId() {
|
||||||
|
return javaId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int stackSize() {
|
||||||
|
return stackSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int maxDamage() {
|
||||||
|
return maxDamage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toolType() {
|
||||||
|
return toolType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toolTier() {
|
||||||
|
return toolTier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String armorType() {
|
||||||
|
return armorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int protectionValue() {
|
||||||
|
return protectionValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String translationString() {
|
||||||
|
return translationString;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> repairMaterials() {
|
||||||
|
return repairMaterials;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull OptionalInt creativeCategory() {
|
||||||
|
return creativeCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String creativeGroup() {
|
||||||
|
return creativeGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isHat() {
|
||||||
|
return isHat;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTool() {
|
||||||
|
return isTool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NonVanillaCustomItemDataBuilder extends GeyserCustomItemData.CustomItemDataBuilder implements NonVanillaCustomItemData.Builder {
|
||||||
|
private String identifier = null;
|
||||||
|
private int javaId = -1;
|
||||||
|
|
||||||
|
private int stackSize = 64;
|
||||||
|
|
||||||
|
private int maxDamage = 0;
|
||||||
|
|
||||||
|
private String toolType = null;
|
||||||
|
private String toolTier = null;
|
||||||
|
|
||||||
|
private String armorType = null;
|
||||||
|
private int protectionValue = 0;
|
||||||
|
|
||||||
|
private String translationString;
|
||||||
|
|
||||||
|
private Set<String> repairMaterials;
|
||||||
|
|
||||||
|
private OptionalInt creativeCategory = OptionalInt.empty();
|
||||||
|
private String creativeGroup = null;
|
||||||
|
|
||||||
|
private boolean hat = false;
|
||||||
|
private boolean tool = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder name(@NonNull String name) {
|
||||||
|
return (NonVanillaCustomItemData.Builder) super.name(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder customItemOptions(@NonNull CustomItemOptions customItemOptions) {
|
||||||
|
//Do nothing, as that value won't be read
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder allowOffhand(boolean allowOffhand) {
|
||||||
|
return (NonVanillaCustomItemData.Builder) super.allowOffhand(allowOffhand);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder displayName(@NonNull String displayName) {
|
||||||
|
return (NonVanillaCustomItemData.Builder) super.displayName(displayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder icon(@NonNull String icon) {
|
||||||
|
return (NonVanillaCustomItemData.Builder) super.icon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder textureSize(int textureSize) {
|
||||||
|
return (NonVanillaCustomItemData.Builder) super.textureSize(textureSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder renderOffsets(CustomRenderOffsets renderOffsets) {
|
||||||
|
return (NonVanillaCustomItemData.Builder) super.renderOffsets(renderOffsets);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder identifier(@NonNull String identifier) {
|
||||||
|
this.identifier = identifier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder javaId(int javaId) {
|
||||||
|
this.javaId = javaId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder stackSize(int stackSize) {
|
||||||
|
this.stackSize = stackSize;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder maxDamage(int maxDamage) {
|
||||||
|
this.maxDamage = maxDamage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder toolType(@Nullable String toolType) {
|
||||||
|
this.toolType = toolType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder toolTier(@Nullable String toolTier) {
|
||||||
|
this.toolTier = toolTier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder armorType(@Nullable String armorType) {
|
||||||
|
this.armorType = armorType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder protectionValue(int protectionValue) {
|
||||||
|
this.protectionValue = protectionValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder translationString(@Nullable String translationString) {
|
||||||
|
this.translationString = translationString;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder repairMaterials(@Nullable Set<String> repairMaterials) {
|
||||||
|
this.repairMaterials = repairMaterials;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder creativeCategory(int creativeCategory) {
|
||||||
|
this.creativeCategory = OptionalInt.of(creativeCategory);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder creativeGroup(@Nullable String creativeGroup) {
|
||||||
|
this.creativeGroup = creativeGroup;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder hat(boolean isHat) {
|
||||||
|
this.hat = isHat;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData.Builder tool(boolean isTool) {
|
||||||
|
this.tool = isTool;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NonVanillaCustomItemData build() {
|
||||||
|
if (identifier == null || javaId == -1) {
|
||||||
|
throw new IllegalArgumentException("Identifier and javaId must be set");
|
||||||
|
}
|
||||||
|
|
||||||
|
super.customItemOptions(CustomItemOptions.builder().build());
|
||||||
|
super.build();
|
||||||
|
return new GeyserNonVanillaCustomItemData(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
* 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.item.components;
|
||||||
|
|
||||||
|
import com.nukkitx.nbt.NbtMap;
|
||||||
|
import com.nukkitx.nbt.NbtType;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ToolBreakSpeedsUtils {
|
||||||
|
public static int toolTierToSpeed(String toolTier) {
|
||||||
|
ToolTier tier = ToolTier.getByName(toolTier);
|
||||||
|
if (tier != null) {
|
||||||
|
return tier.getSpeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NbtMap createTagBreakSpeed(int speed, String... tags) {
|
||||||
|
StringBuilder builder = new StringBuilder("query.any_tag('");
|
||||||
|
builder.append(tags[0]);
|
||||||
|
for (int i = 1; i < tags.length; i++) {
|
||||||
|
builder.append("', '").append(tags[i]);
|
||||||
|
}
|
||||||
|
builder.append("')");
|
||||||
|
|
||||||
|
return NbtMap.builder()
|
||||||
|
.putCompound("block", NbtMap.builder()
|
||||||
|
.putString("tags", builder.toString())
|
||||||
|
.build())
|
||||||
|
.putCompound("on_dig", NbtMap.builder()
|
||||||
|
.putCompound("condition", NbtMap.builder()
|
||||||
|
.putString("expression", "")
|
||||||
|
.putInt("version", -1)
|
||||||
|
.build())
|
||||||
|
.putString("event", "tool_durability")
|
||||||
|
.putString("target", "self")
|
||||||
|
.build())
|
||||||
|
.putInt("speed", speed)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NbtMap createBreakSpeed(int speed, String block) {
|
||||||
|
return NbtMap.builder()
|
||||||
|
.putCompound("block", NbtMap.builder()
|
||||||
|
.putString("name", block).build())
|
||||||
|
.putCompound("on_dig", NbtMap.builder()
|
||||||
|
.putCompound("condition", NbtMap.builder()
|
||||||
|
.putString("expression", "")
|
||||||
|
.putInt("version", -1)
|
||||||
|
.build())
|
||||||
|
.putString("event", "tool_durability")
|
||||||
|
.putString("target", "self")
|
||||||
|
.build())
|
||||||
|
.putInt("speed", speed)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NbtMap createDigger(List<NbtMap> speeds) {
|
||||||
|
return NbtMap.builder()
|
||||||
|
.putList("destroy_speeds", NbtType.COMPOUND, speeds)
|
||||||
|
.putCompound("on_dig", NbtMap.builder()
|
||||||
|
.putCompound("condition", NbtMap.builder()
|
||||||
|
.putString("expression", "")
|
||||||
|
.putInt("version", -1)
|
||||||
|
.build())
|
||||||
|
.putString("event", "tool_durability")
|
||||||
|
.putString("target", "self")
|
||||||
|
.build())
|
||||||
|
.putBoolean("use_efficiency", true)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NbtMap getAxeDigger(int speed) {
|
||||||
|
List<NbtMap> speeds = new ArrayList<>();
|
||||||
|
speeds.add(createTagBreakSpeed(speed, "wood", "pumpkin", "plant"));
|
||||||
|
|
||||||
|
return createDigger(speeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NbtMap getPickaxeDigger(int speed, String toolTier) {
|
||||||
|
List<NbtMap> speeds = new ArrayList<>();
|
||||||
|
if (toolTier.equals(ToolTier.DIAMOND.toString()) || toolTier.equals(ToolTier.NETHERITE.toString())) {
|
||||||
|
speeds.add(createTagBreakSpeed(speed, "iron_pick_diggable", "diamond_pick_diggable"));
|
||||||
|
} else {
|
||||||
|
speeds.add(createTagBreakSpeed(speed, "iron_pick_diggable"));
|
||||||
|
}
|
||||||
|
speeds.add(createTagBreakSpeed(speed, "stone", "metal", "rail", "mob_spawner"));
|
||||||
|
|
||||||
|
return createDigger(speeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NbtMap getShovelDigger(int speed) {
|
||||||
|
List<NbtMap> speeds = new ArrayList<>();
|
||||||
|
speeds.add(createTagBreakSpeed(speed, "dirt", "sand", "gravel", "grass", "snow"));
|
||||||
|
|
||||||
|
return createDigger(speeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NbtMap getSwordDigger(int speed) {
|
||||||
|
List<NbtMap> speeds = new ArrayList<>();
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:web"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:bamboo"));
|
||||||
|
|
||||||
|
return createDigger(speeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NbtMap getHoeDigger(int speed) {
|
||||||
|
List<NbtMap> speeds = new ArrayList<>();
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:leaves"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:leaves2"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves_flowered"));
|
||||||
|
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:sculk"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:sculk_catalyst"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:sculk_sensor"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:sculk_shrieker"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:sculk_vein"));
|
||||||
|
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:nether_wart_block"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:warped_wart_block"));
|
||||||
|
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:hay_block"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:moss_block"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:shroomlight"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:sponge"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:target"));
|
||||||
|
|
||||||
|
return createDigger(speeds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NbtMap getShearsDigger(int speed) {
|
||||||
|
List<NbtMap> speeds = new ArrayList<>();
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:web"));
|
||||||
|
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:leaves"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:leaves2"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:azalea_leaves_flowered"));
|
||||||
|
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:wool"));
|
||||||
|
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:glow_lichen"));
|
||||||
|
speeds.add(createBreakSpeed(speed, "minecraft:vine"));
|
||||||
|
|
||||||
|
return createDigger(speeds);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* 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.item.components;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public enum ToolTier {
|
||||||
|
WOODEN(2),
|
||||||
|
STONE(4),
|
||||||
|
IRON(6),
|
||||||
|
GOLDEN(12),
|
||||||
|
DIAMOND(8),
|
||||||
|
NETHERITE(9);
|
||||||
|
|
||||||
|
public static final ToolTier[] VALUES = values();
|
||||||
|
|
||||||
|
private final int speed;
|
||||||
|
|
||||||
|
ToolTier(int speed) {
|
||||||
|
this.speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSpeed() {
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.name().toLowerCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ToolTier getByName(@NonNull String name) {
|
||||||
|
String upperCase = name.toUpperCase(Locale.ROOT);
|
||||||
|
for (ToolTier tier : VALUES) {
|
||||||
|
if (tier.name().equals(upperCase)) {
|
||||||
|
return tier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* 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.item.components;
|
||||||
|
|
||||||
|
import com.nukkitx.nbt.NbtMap;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public enum WearableSlot {
|
||||||
|
HEAD,
|
||||||
|
CHEST,
|
||||||
|
LEGS,
|
||||||
|
FEET;
|
||||||
|
|
||||||
|
private final NbtMap slotNbt;
|
||||||
|
|
||||||
|
WearableSlot() {
|
||||||
|
this.slotNbt = NbtMap.builder().putString("slot", "slot.armor." + this.name().toLowerCase(Locale.ROOT)).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public NbtMap getSlotNbt() {
|
||||||
|
return slotNbt;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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.item.exception;
|
||||||
|
|
||||||
|
public class InvalidCustomMappingsFileException extends Exception {
|
||||||
|
public InvalidCustomMappingsFileException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidCustomMappingsFileException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidCustomMappingsFileException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* 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.item.mappings;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||||
|
import org.geysermc.geyser.item.mappings.versions.MappingsReader;
|
||||||
|
import org.geysermc.geyser.item.mappings.versions.MappingsReader_v1;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
public class MappingsConfigReader {
|
||||||
|
private final Int2ObjectMap<MappingsReader> mappingReaders = new Int2ObjectOpenHashMap<>();
|
||||||
|
private final Path customMappingsDirectory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("custom_mappings");
|
||||||
|
|
||||||
|
public MappingsConfigReader() {
|
||||||
|
this.mappingReaders.put(1, new MappingsReader_v1());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path[] getCustomMappingsFiles() {
|
||||||
|
try {
|
||||||
|
return Files.walk(this.customMappingsDirectory)
|
||||||
|
.filter(child -> child.toString().endsWith(".json"))
|
||||||
|
.toArray(Path[]::new);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return new Path[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadMappingsFromJson(BiConsumer<String, CustomItemData> consumer) {
|
||||||
|
Path customMappingsDirectory = this.customMappingsDirectory;
|
||||||
|
if (!Files.exists(customMappingsDirectory)) {
|
||||||
|
try {
|
||||||
|
Files.createDirectories(customMappingsDirectory);
|
||||||
|
} catch (IOException e) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Failed to create custom mappings directory", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Path[] mappingsFiles = this.getCustomMappingsFiles();
|
||||||
|
for (Path mappingsFile : mappingsFiles) {
|
||||||
|
this.readMappingsFromJson(mappingsFile, consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readMappingsFromJson(Path file, BiConsumer<String, CustomItemData> consumer) {
|
||||||
|
JsonNode mappingsRoot;
|
||||||
|
try {
|
||||||
|
mappingsRoot = GeyserImpl.JSON_MAPPER.readTree(file.toFile());
|
||||||
|
} catch (IOException e) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Failed to read custom mapping file: " + file, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mappingsRoot.has("format_version")) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Mappings file " + file + " is missing the format version field!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int formatVersion = mappingsRoot.get("format_version").asInt();
|
||||||
|
if (!this.mappingReaders.containsKey(formatVersion)) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Mappings file " + file + " has an unknown format version: " + formatVersion);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mappingReaders.get(formatVersion).readMappings(file, mappingsRoot, consumer);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* 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.item.mappings.versions;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomRenderOffsets;
|
||||||
|
import org.geysermc.geyser.item.exception.InvalidCustomMappingsFileException;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
public abstract class MappingsReader {
|
||||||
|
public abstract void readMappings(Path file, JsonNode mappingsRoot, BiConsumer<String, CustomItemData> consumer);
|
||||||
|
|
||||||
|
public abstract CustomItemData readItemMappingEntry(JsonNode node) throws InvalidCustomMappingsFileException;
|
||||||
|
|
||||||
|
protected CustomRenderOffsets fromJsonNode(JsonNode node) {
|
||||||
|
if (node == null || !node.isObject()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CustomRenderOffsets(
|
||||||
|
getHandOffsets(node, "main_hand"),
|
||||||
|
getHandOffsets(node, "off_hand")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CustomRenderOffsets.Hand getHandOffsets(JsonNode node, String hand) {
|
||||||
|
JsonNode tmpNode = node.get(hand);
|
||||||
|
if (tmpNode == null || !tmpNode.isObject()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CustomRenderOffsets.Hand(
|
||||||
|
getPerspectiveOffsets(tmpNode, "first_person"),
|
||||||
|
getPerspectiveOffsets(tmpNode, "third_person")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CustomRenderOffsets.Offset getPerspectiveOffsets(JsonNode node, String perspective) {
|
||||||
|
JsonNode tmpNode = node.get(perspective);
|
||||||
|
if (tmpNode == null || !tmpNode.isObject()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CustomRenderOffsets.Offset(
|
||||||
|
getOffsetXYZ(tmpNode, "position"),
|
||||||
|
getOffsetXYZ(tmpNode, "rotation"),
|
||||||
|
getOffsetXYZ(tmpNode, "scale")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected CustomRenderOffsets.OffsetXYZ getOffsetXYZ(JsonNode node, String offsetType) {
|
||||||
|
JsonNode tmpNode = node.get(offsetType);
|
||||||
|
if (tmpNode == null || !tmpNode.isObject()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tmpNode.has("x") || !tmpNode.has("y") || !tmpNode.has("z")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CustomRenderOffsets.OffsetXYZ(
|
||||||
|
tmpNode.get("x").floatValue(),
|
||||||
|
tmpNode.get("y").floatValue(),
|
||||||
|
tmpNode.get("z").floatValue()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* 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.item.mappings.versions;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||||
|
import org.geysermc.geyser.item.exception.InvalidCustomMappingsFileException;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
public class MappingsReader_v1 extends MappingsReader {
|
||||||
|
@Override
|
||||||
|
public void readMappings(Path file, JsonNode mappingsRoot, BiConsumer<String, CustomItemData> consumer) {
|
||||||
|
this.readItemMappings(file, mappingsRoot, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readItemMappings(Path file, JsonNode mappingsRoot, BiConsumer<String, CustomItemData> consumer) {
|
||||||
|
JsonNode itemsNode = mappingsRoot.get("items");
|
||||||
|
|
||||||
|
if (itemsNode != null && itemsNode.isObject()) {
|
||||||
|
itemsNode.fields().forEachRemaining(entry -> {
|
||||||
|
if (entry.getValue().isArray()) {
|
||||||
|
entry.getValue().forEach(data -> {
|
||||||
|
try {
|
||||||
|
CustomItemData customItemData = this.readItemMappingEntry(data);
|
||||||
|
consumer.accept(entry.getKey(), customItemData);
|
||||||
|
} catch (InvalidCustomMappingsFileException e) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Error in custom mapping file: " + file.toString(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CustomItemOptions readItemCustomItemOptions(JsonNode node) {
|
||||||
|
CustomItemOptions.Builder customItemOptions = CustomItemOptions.builder();
|
||||||
|
|
||||||
|
JsonNode customModelData = node.get("custom_model_data");
|
||||||
|
if (customModelData != null && customModelData.isInt()) {
|
||||||
|
customItemOptions.customModelData(customModelData.asInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode damagePredicate = node.get("damage_predicate");
|
||||||
|
if (damagePredicate != null && damagePredicate.isInt()) {
|
||||||
|
customItemOptions.damagePredicate(damagePredicate.asInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonNode unbreakable = node.get("unbreakable");
|
||||||
|
if (unbreakable != null && unbreakable.isBoolean()) {
|
||||||
|
customItemOptions.unbreakable(unbreakable.asBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
return customItemOptions.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomItemData readItemMappingEntry(JsonNode node) throws InvalidCustomMappingsFileException {
|
||||||
|
if (node == null || !node.isObject()) {
|
||||||
|
throw new InvalidCustomMappingsFileException("Invalid item mappings entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = node.get("name").asText();
|
||||||
|
if (name == null || name.isEmpty()) {
|
||||||
|
throw new InvalidCustomMappingsFileException("An item entry has no name");
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomItemData.Builder customItemData = CustomItemData.builder()
|
||||||
|
.name(name)
|
||||||
|
.customItemOptions(this.readItemCustomItemOptions(node));
|
||||||
|
|
||||||
|
//The next entries are optional
|
||||||
|
if (node.has("display_name")) {
|
||||||
|
customItemData.displayName(node.get("display_name").asText());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.has("icon")) {
|
||||||
|
customItemData.icon(node.get("icon").asText());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.has("allow_offhand")) {
|
||||||
|
customItemData.allowOffhand(node.get("allow_offhand").asBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.has("texture_size")) {
|
||||||
|
customItemData.textureSize(node.get("texture_size").asInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.has("render_offsets")) {
|
||||||
|
JsonNode tmpNode = node.get("render_offsets");
|
||||||
|
|
||||||
|
customItemData.renderOffsets(fromJsonNode(tmpNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
return customItemData.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.data.ExperimentData;
|
||||||
import com.nukkitx.protocol.bedrock.data.ResourcePackType;
|
import com.nukkitx.protocol.bedrock.data.ResourcePackType;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.*;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
|
||||||
import org.geysermc.geyser.api.network.AuthType;
|
import org.geysermc.geyser.api.network.AuthType;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.geyser.pack.ResourcePack;
|
import org.geysermc.geyser.pack.ResourcePack;
|
||||||
|
@ -38,7 +39,6 @@ import org.geysermc.geyser.pack.ResourcePackManifest;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.Registries;
|
import org.geysermc.geyser.registry.Registries;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
|
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.util.LoginEncryptionUtils;
|
import org.geysermc.geyser.util.LoginEncryptionUtils;
|
||||||
import org.geysermc.geyser.util.MathUtils;
|
import org.geysermc.geyser.util.MathUtils;
|
||||||
|
@ -160,7 +160,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.getUuid().toString(), header.getVersionString(), ""));
|
stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.getUuid().toString(), header.getVersionString(), ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.getItemMappings().getFurnaceMinecartData() != null) {
|
if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
|
||||||
// Allow custom items to work
|
// Allow custom items to work
|
||||||
stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true));
|
stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true));
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,16 @@
|
||||||
package org.geysermc.geyser.pack;
|
package org.geysermc.geyser.pack;
|
||||||
|
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.api.event.lifecycle.GeyserLoadResourcePacksEvent;
|
||||||
import org.geysermc.geyser.util.FileUtils;
|
import org.geysermc.geyser.util.FileUtils;
|
||||||
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.HashMap;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
@ -59,16 +63,33 @@ public class ResourcePack {
|
||||||
* Loop through the packs directory and locate valid resource pack files
|
* Loop through the packs directory and locate valid resource pack files
|
||||||
*/
|
*/
|
||||||
public static void loadPacks() {
|
public static void loadPacks() {
|
||||||
File directory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("packs").toFile();
|
Path directory = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("packs");
|
||||||
|
|
||||||
if (!directory.exists()) {
|
if (!Files.exists(directory)) {
|
||||||
directory.mkdir();
|
try {
|
||||||
|
Files.createDirectory(directory);
|
||||||
|
} catch (IOException e) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Could not create packs directory", e);
|
||||||
|
}
|
||||||
|
|
||||||
// As we just created the directory it will be empty
|
// As we just created the directory it will be empty
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (File file : directory.listFiles()) {
|
List<Path> resourcePacks;
|
||||||
|
try {
|
||||||
|
resourcePacks = Files.walk(directory).collect(Collectors.toList());
|
||||||
|
} catch (IOException e) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Could not list packs directory", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GeyserLoadResourcePacksEvent event = new GeyserLoadResourcePacksEvent(resourcePacks);
|
||||||
|
GeyserImpl.getInstance().eventBus().fire(event);
|
||||||
|
|
||||||
|
for (Path path : event.resourcePacks()) {
|
||||||
|
File file = path.toFile();
|
||||||
|
|
||||||
if (file.getName().endsWith(".zip") || file.getName().endsWith(".mcpack")) {
|
if (file.getName().endsWith(".zip") || file.getName().endsWith(".mcpack")) {
|
||||||
ResourcePack pack = new ResourcePack();
|
ResourcePack pack = new ResourcePack();
|
||||||
|
|
||||||
|
|
|
@ -113,11 +113,6 @@ public final class Registries {
|
||||||
*/
|
*/
|
||||||
public static final SimpleMappedRegistry<EntityType, EntityDefinition<?>> ENTITY_DEFINITIONS = SimpleMappedRegistry.create(RegistryLoaders.empty(() -> new EnumMap<>(EntityType.class)));
|
public static final SimpleMappedRegistry<EntityType, EntityDefinition<?>> ENTITY_DEFINITIONS = SimpleMappedRegistry.create(RegistryLoaders.empty(() -> new EnumMap<>(EntityType.class)));
|
||||||
|
|
||||||
/**
|
|
||||||
* A map containing all the extension loaders.
|
|
||||||
*/
|
|
||||||
public static final SimpleMappedRegistry<Key, ExtensionLoader> EXTENSION_LOADERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map containing all Java entity identifiers and their respective Geyser definitions
|
* A map containing all Java entity identifiers and their respective Geyser definitions
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,360 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.registry.populator;
|
||||||
|
|
||||||
|
import com.nukkitx.nbt.NbtMap;
|
||||||
|
import com.nukkitx.nbt.NbtMapBuilder;
|
||||||
|
import com.nukkitx.nbt.NbtType;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomRenderOffsets;
|
||||||
|
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
||||||
|
import org.geysermc.geyser.item.GeyserCustomMappingData;
|
||||||
|
import org.geysermc.geyser.item.components.ToolBreakSpeedsUtils;
|
||||||
|
import org.geysermc.geyser.item.components.WearableSlot;
|
||||||
|
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||||
|
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||||
|
import org.geysermc.geyser.registry.type.NonVanillaItemRegistration;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
|
||||||
|
public class CustomItemRegistryPopulator {
|
||||||
|
public static GeyserCustomMappingData registerCustomItem(String customItemName, GeyserMappingItem javaItem, CustomItemData customItemData, int bedrockId) {
|
||||||
|
StartGamePacket.ItemEntry startGamePacketItemEntry = new StartGamePacket.ItemEntry(customItemName, (short) bedrockId, true);
|
||||||
|
|
||||||
|
NbtMapBuilder builder = createComponentNbt(customItemData, javaItem, customItemName, bedrockId);
|
||||||
|
ComponentItemData componentItemData = new ComponentItemData(customItemName, builder.build());
|
||||||
|
|
||||||
|
return new GeyserCustomMappingData(componentItemData, startGamePacketItemEntry, customItemName, bedrockId);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean initialCheck(String identifier, CustomItemData item, Map<String, GeyserMappingItem> mappings) {
|
||||||
|
if (!mappings.containsKey(identifier)) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Could not find the Java item to add custom item properties to for " + item.name());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!item.customItemOptions().hasCustomItemOptions()) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("The custom item " + item.name() + " has no registration types");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NonVanillaItemRegistration registerCustomItem(NonVanillaCustomItemData customItemData, int customItemId) {
|
||||||
|
String customIdentifier = customItemData.identifier();
|
||||||
|
|
||||||
|
ItemMapping customItemMapping = ItemMapping.builder()
|
||||||
|
.javaIdentifier(customIdentifier)
|
||||||
|
.bedrockIdentifier(customIdentifier)
|
||||||
|
.javaId(customItemData.javaId())
|
||||||
|
.bedrockId(customItemId)
|
||||||
|
.bedrockData(0)
|
||||||
|
.bedrockBlockId(0)
|
||||||
|
.stackSize(customItemData.stackSize())
|
||||||
|
.toolType(customItemData.toolType())
|
||||||
|
.toolTier(customItemData.toolTier())
|
||||||
|
.translationString(customItemData.translationString())
|
||||||
|
.maxDamage(customItemData.maxDamage())
|
||||||
|
.repairMaterials(customItemData.repairMaterials())
|
||||||
|
.hasSuspiciousStewEffect(false)
|
||||||
|
.customItemOptions(Object2IntMaps.emptyMap())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
NbtMapBuilder builder = createComponentNbt(customItemData, customItemData.identifier(), customItemId,
|
||||||
|
customItemData.creativeCategory(), customItemData.creativeGroup(), customItemData.isHat(), customItemData.isTool());
|
||||||
|
ComponentItemData componentItemData = new ComponentItemData(customIdentifier, builder.build());
|
||||||
|
|
||||||
|
return new NonVanillaItemRegistration(componentItemData, customItemMapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NbtMapBuilder createComponentNbt(CustomItemData customItemData, GeyserMappingItem mapping,
|
||||||
|
String customItemName, int customItemId) {
|
||||||
|
NbtMapBuilder builder = NbtMap.builder();
|
||||||
|
builder.putString("name", customItemName)
|
||||||
|
.putInt("id", customItemId);
|
||||||
|
|
||||||
|
NbtMapBuilder itemProperties = NbtMap.builder();
|
||||||
|
NbtMapBuilder componentBuilder = NbtMap.builder();
|
||||||
|
|
||||||
|
setupBasicItemInfo(mapping.getMaxDamage(), mapping.getStackSize(), mapping.getToolType() != null, customItemData, itemProperties, componentBuilder);
|
||||||
|
|
||||||
|
boolean canDestroyInCreative = true;
|
||||||
|
if (mapping.getToolType() != null) { // This is not using the isTool boolean because it is not just a render type here.
|
||||||
|
canDestroyInCreative = computeToolProperties(mapping.getToolTier(), mapping.getToolType(), itemProperties, componentBuilder);
|
||||||
|
}
|
||||||
|
itemProperties.putBoolean("can_destroy_in_creative", canDestroyInCreative);
|
||||||
|
|
||||||
|
if (mapping.getArmorType() != null) {
|
||||||
|
computeArmorProperties(mapping.getArmorType(), mapping.getProtectionValue(), componentBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
computeRenderOffsets(false, customItemData, componentBuilder);
|
||||||
|
|
||||||
|
componentBuilder.putCompound("item_properties", itemProperties.build());
|
||||||
|
builder.putCompound("components", componentBuilder.build());
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NbtMapBuilder createComponentNbt(NonVanillaCustomItemData customItemData, String customItemName,
|
||||||
|
int customItemId, OptionalInt creativeCategory,
|
||||||
|
String creativeGroup, boolean isHat, boolean isTool) {
|
||||||
|
NbtMapBuilder builder = NbtMap.builder();
|
||||||
|
builder.putString("name", customItemName)
|
||||||
|
.putInt("id", customItemId);
|
||||||
|
|
||||||
|
NbtMapBuilder itemProperties = NbtMap.builder();
|
||||||
|
NbtMapBuilder componentBuilder = NbtMap.builder();
|
||||||
|
|
||||||
|
setupBasicItemInfo(customItemData.maxDamage(), customItemData.stackSize(), isTool, customItemData, itemProperties, componentBuilder);
|
||||||
|
|
||||||
|
boolean canDestroyInCreative = true;
|
||||||
|
if (customItemData.toolType() != null) { // This is not using the isTool boolean because it is not just a render type here.
|
||||||
|
canDestroyInCreative = computeToolProperties(customItemData.toolTier(), customItemData.toolType(), itemProperties, componentBuilder);
|
||||||
|
}
|
||||||
|
itemProperties.putBoolean("can_destroy_in_creative", canDestroyInCreative);
|
||||||
|
|
||||||
|
String armorType = customItemData.armorType();
|
||||||
|
if (armorType != null) {
|
||||||
|
computeArmorProperties(armorType, customItemData.protectionValue(), componentBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
computeRenderOffsets(isHat, customItemData, componentBuilder);
|
||||||
|
|
||||||
|
if (creativeGroup != null) {
|
||||||
|
itemProperties.putString("creative_group", creativeGroup);
|
||||||
|
}
|
||||||
|
if (creativeCategory.isPresent()) {
|
||||||
|
itemProperties.putInt("creative_category", creativeCategory.getAsInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
componentBuilder.putCompound("item_properties", itemProperties.build());
|
||||||
|
builder.putCompound("components", componentBuilder.build());
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setupBasicItemInfo(int maxDamage, int stackSize, boolean isTool, CustomItemData customItemData, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder) {
|
||||||
|
itemProperties.putCompound("minecraft:icon", NbtMap.builder()
|
||||||
|
.putString("texture", customItemData.icon())
|
||||||
|
.build());
|
||||||
|
componentBuilder.putCompound("minecraft:display_name", NbtMap.builder().putString("value", customItemData.displayName()).build());
|
||||||
|
|
||||||
|
itemProperties.putBoolean("allow_off_hand", customItemData.allowOffhand());
|
||||||
|
itemProperties.putBoolean("hand_equipped", isTool);
|
||||||
|
itemProperties.putInt("max_stack_size", stackSize);
|
||||||
|
if (maxDamage > 0) {
|
||||||
|
componentBuilder.putCompound("minecraft:durability", NbtMap.builder()
|
||||||
|
.putCompound("damage_chance", NbtMap.builder()
|
||||||
|
.putInt("max", 1)
|
||||||
|
.putInt("min", 1)
|
||||||
|
.build())
|
||||||
|
.putInt("max_durability", maxDamage)
|
||||||
|
.build());
|
||||||
|
itemProperties.putBoolean("use_duration", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return can destroy in creative
|
||||||
|
*/
|
||||||
|
private static boolean computeToolProperties(String toolTier, String toolType, NbtMapBuilder itemProperties, NbtMapBuilder componentBuilder) {
|
||||||
|
boolean canDestroyInCreative = true;
|
||||||
|
float miningSpeed = 1.0f;
|
||||||
|
|
||||||
|
if (toolType.equals("shears")) {
|
||||||
|
componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getShearsDigger(15));
|
||||||
|
} else {
|
||||||
|
int toolSpeed = ToolBreakSpeedsUtils.toolTierToSpeed(toolTier);
|
||||||
|
switch (toolType) {
|
||||||
|
case "sword" -> {
|
||||||
|
miningSpeed = 1.5f;
|
||||||
|
canDestroyInCreative = false;
|
||||||
|
componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getSwordDigger(toolSpeed));
|
||||||
|
componentBuilder.putCompound("minecraft:weapon", NbtMap.EMPTY);
|
||||||
|
}
|
||||||
|
case "pickaxe" -> {
|
||||||
|
componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getPickaxeDigger(toolSpeed, toolTier));
|
||||||
|
setItemTag(componentBuilder, "pickaxe");
|
||||||
|
}
|
||||||
|
case "axe" -> {
|
||||||
|
componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getAxeDigger(toolSpeed));
|
||||||
|
setItemTag(componentBuilder, "axe");
|
||||||
|
}
|
||||||
|
case "shovel" -> {
|
||||||
|
componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getShovelDigger(toolSpeed));
|
||||||
|
setItemTag(componentBuilder, "shovel");
|
||||||
|
}
|
||||||
|
case "hoe" -> {
|
||||||
|
componentBuilder.putCompound("minecraft:digger", ToolBreakSpeedsUtils.getHoeDigger(toolSpeed));
|
||||||
|
setItemTag(componentBuilder, "hoe");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
itemProperties.putBoolean("hand_equipped", true);
|
||||||
|
itemProperties.putFloat("mining_speed", miningSpeed);
|
||||||
|
|
||||||
|
return canDestroyInCreative;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void computeArmorProperties(String armorType, int protectionValue, NbtMapBuilder componentBuilder) {
|
||||||
|
switch (armorType) {
|
||||||
|
case "boots" -> {
|
||||||
|
componentBuilder.putString("minecraft:render_offsets", "boots");
|
||||||
|
componentBuilder.putCompound("minecraft:wearable", WearableSlot.FEET.getSlotNbt());
|
||||||
|
componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build());
|
||||||
|
}
|
||||||
|
case "chestplate" -> {
|
||||||
|
componentBuilder.putString("minecraft:render_offsets", "chestplates");
|
||||||
|
componentBuilder.putCompound("minecraft:wearable", WearableSlot.CHEST.getSlotNbt());
|
||||||
|
componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build());
|
||||||
|
}
|
||||||
|
case "leggings" -> {
|
||||||
|
componentBuilder.putString("minecraft:render_offsets", "leggings");
|
||||||
|
componentBuilder.putCompound("minecraft:wearable", WearableSlot.LEGS.getSlotNbt());
|
||||||
|
componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build());
|
||||||
|
}
|
||||||
|
case "helmet" -> {
|
||||||
|
componentBuilder.putString("minecraft:render_offsets", "helmets");
|
||||||
|
componentBuilder.putCompound("minecraft:wearable", WearableSlot.HEAD.getSlotNbt());
|
||||||
|
componentBuilder.putCompound("minecraft:armor", NbtMap.builder().putInt("protection", protectionValue).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void computeRenderOffsets(boolean isHat, CustomItemData customItemData, NbtMapBuilder componentBuilder) {
|
||||||
|
if (isHat) {
|
||||||
|
componentBuilder.remove("minecraft:render_offsets");
|
||||||
|
componentBuilder.putString("minecraft:render_offsets", "helmets");
|
||||||
|
|
||||||
|
componentBuilder.remove("minecraft:wearable");
|
||||||
|
componentBuilder.putCompound("minecraft:wearable", WearableSlot.HEAD.getSlotNbt());
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRenderOffsets renderOffsets = customItemData.renderOffsets();
|
||||||
|
if (renderOffsets != null) {
|
||||||
|
componentBuilder.remove("minecraft:render_offsets");
|
||||||
|
componentBuilder.putCompound("minecraft:render_offsets", toNbtMap(renderOffsets));
|
||||||
|
} else if (customItemData.textureSize() != 16 && !componentBuilder.containsKey("minecraft:render_offsets")) {
|
||||||
|
float scale1 = (float) (0.075 / (customItemData.textureSize() / 16f));
|
||||||
|
float scale2 = (float) (0.125 / (customItemData.textureSize() / 16f));
|
||||||
|
float scale3 = (float) (0.075 / (customItemData.textureSize() / 16f * 2.4f));
|
||||||
|
|
||||||
|
componentBuilder.putCompound("minecraft:render_offsets",
|
||||||
|
NbtMap.builder().putCompound("main_hand", NbtMap.builder()
|
||||||
|
.putCompound("first_person", xyzToScaleList(scale3, scale3, scale3))
|
||||||
|
.putCompound("third_person", xyzToScaleList(scale1, scale2, scale1)).build())
|
||||||
|
.putCompound("off_hand", NbtMap.builder()
|
||||||
|
.putCompound("first_person", xyzToScaleList(scale1, scale2, scale1))
|
||||||
|
.putCompound("third_person", xyzToScaleList(scale1, scale2, scale1)).build()).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NbtMap toNbtMap(CustomRenderOffsets renderOffsets) {
|
||||||
|
NbtMapBuilder builder = NbtMap.builder();
|
||||||
|
|
||||||
|
CustomRenderOffsets.Hand mainHand = renderOffsets.mainHand();
|
||||||
|
if (mainHand != null) {
|
||||||
|
NbtMap nbt = toNbtMap(mainHand);
|
||||||
|
if (nbt != null) {
|
||||||
|
builder.putCompound("main_hand", nbt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CustomRenderOffsets.Hand offhand = renderOffsets.offhand();
|
||||||
|
if (offhand != null) {
|
||||||
|
NbtMap nbt = toNbtMap(offhand);
|
||||||
|
if (nbt != null) {
|
||||||
|
builder.putCompound("off_hand", nbt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NbtMap toNbtMap(CustomRenderOffsets.Hand hand) {
|
||||||
|
NbtMap firstPerson = toNbtMap(hand.firstPerson());
|
||||||
|
NbtMap thirdPerson = toNbtMap(hand.thirdPerson());
|
||||||
|
|
||||||
|
if (firstPerson == null && thirdPerson == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
NbtMapBuilder builder = NbtMap.builder();
|
||||||
|
if (firstPerson != null) {
|
||||||
|
builder.putCompound("first_person", firstPerson);
|
||||||
|
}
|
||||||
|
if (thirdPerson != null) {
|
||||||
|
builder.putCompound("third_person", thirdPerson);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NbtMap toNbtMap(@Nullable CustomRenderOffsets.Offset offset) {
|
||||||
|
if (offset == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomRenderOffsets.OffsetXYZ position = offset.position();
|
||||||
|
CustomRenderOffsets.OffsetXYZ rotation = offset.rotation();
|
||||||
|
CustomRenderOffsets.OffsetXYZ scale = offset.scale();
|
||||||
|
|
||||||
|
if (position == null && rotation == null && scale == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
NbtMapBuilder builder = NbtMap.builder();
|
||||||
|
if (position != null) {
|
||||||
|
builder.putList("position", NbtType.FLOAT, toList(position));
|
||||||
|
}
|
||||||
|
if (rotation != null) {
|
||||||
|
builder.putList("rotation", NbtType.FLOAT, toList(rotation));
|
||||||
|
}
|
||||||
|
if (scale != null) {
|
||||||
|
builder.putList("scale", NbtType.FLOAT, toList(scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Float> toList(CustomRenderOffsets.OffsetXYZ xyz) {
|
||||||
|
return List.of(xyz.x(), xyz.y(), xyz.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setItemTag(NbtMapBuilder builder, String tag) {
|
||||||
|
builder.putList("item_tags", NbtType.STRING, List.of("minecraft:is_" + tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static NbtMap xyzToScaleList(float x, float y, float z) {
|
||||||
|
return NbtMap.builder().putList("scale", NbtType.FLOAT, List.of(x, y, z)).build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,8 @@ package org.geysermc.geyser.registry.populator;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.google.common.collect.MultimapBuilder;
|
||||||
import com.nukkitx.nbt.NbtMap;
|
import com.nukkitx.nbt.NbtMap;
|
||||||
import com.nukkitx.nbt.NbtMapBuilder;
|
import com.nukkitx.nbt.NbtMapBuilder;
|
||||||
import com.nukkitx.nbt.NbtType;
|
import com.nukkitx.nbt.NbtType;
|
||||||
|
@ -35,14 +37,22 @@ import com.nukkitx.protocol.bedrock.data.SoundEvent;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData;
|
import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
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.*;
|
||||||
import com.nukkitx.protocol.bedrock.v527.Bedrock_v527;
|
import com.nukkitx.protocol.bedrock.v527.Bedrock_v527;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import it.unimi.dsi.fastutil.objects.*;
|
import it.unimi.dsi.fastutil.objects.*;
|
||||||
|
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.event.lifecycle.GeyserDefineCustomItemsEvent;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||||
|
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
||||||
import org.geysermc.geyser.inventory.item.StoredItemMappings;
|
import org.geysermc.geyser.inventory.item.StoredItemMappings;
|
||||||
|
import org.geysermc.geyser.item.GeyserCustomMappingData;
|
||||||
|
import org.geysermc.geyser.item.mappings.MappingsConfigReader;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.Registries;
|
import org.geysermc.geyser.registry.Registries;
|
||||||
import org.geysermc.geyser.registry.type.*;
|
import org.geysermc.geyser.registry.type.*;
|
||||||
|
@ -78,6 +88,58 @@ public class ItemRegistryPopulator {
|
||||||
throw new AssertionError("Unable to load Java runtime item IDs", e);
|
throw new AssertionError("Unable to load Java runtime item IDs", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean customItemsAllowed = GeyserImpl.getInstance().getConfig().isAddNonBedrockItems();
|
||||||
|
|
||||||
|
Multimap<String, CustomItemData> customItems = MultimapBuilder.hashKeys().hashSetValues().build();
|
||||||
|
List<NonVanillaCustomItemData> nonVanillaCustomItems;
|
||||||
|
|
||||||
|
MappingsConfigReader mappingsConfigReader = new MappingsConfigReader();
|
||||||
|
if (customItemsAllowed) {
|
||||||
|
// Load custom items from mappings files
|
||||||
|
mappingsConfigReader.loadMappingsFromJson((key, item) -> {
|
||||||
|
if (CustomItemRegistryPopulator.initialCheck(key, item, items)) {
|
||||||
|
customItems.get(key).add(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
nonVanillaCustomItems = new ObjectArrayList<>();
|
||||||
|
GeyserImpl.getInstance().eventBus().fire(new GeyserDefineCustomItemsEvent(customItems, nonVanillaCustomItems) {
|
||||||
|
@Override
|
||||||
|
public boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData) {
|
||||||
|
if (CustomItemRegistryPopulator.initialCheck(identifier, customItemData, items)) {
|
||||||
|
customItems.get(identifier).add(customItemData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean register(@NonNull NonVanillaCustomItemData customItemData) {
|
||||||
|
if (customItemData.identifier().startsWith("minecraft:")) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("The custom item " + customItemData.identifier() +
|
||||||
|
" is attempting to masquerade as a vanilla Minecraft item!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (customItemData.javaId() < items.size()) {
|
||||||
|
// Attempting to overwrite an item that already exists in the protocol
|
||||||
|
GeyserImpl.getInstance().getLogger().error("The custom item " + customItemData.identifier() +
|
||||||
|
" is attempting to overwrite a vanilla Minecraft item!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
nonVanillaCustomItems.add(customItemData);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
nonVanillaCustomItems = Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
int customItemCount = customItems.size() + nonVanillaCustomItems.size();
|
||||||
|
if (customItemCount > 0) {
|
||||||
|
GeyserImpl.getInstance().getLogger().info("Registered " + customItemCount + " custom items");
|
||||||
|
}
|
||||||
|
|
||||||
// We can reduce some operations as Java information is the same across all palette versions
|
// We can reduce some operations as Java information is the same across all palette versions
|
||||||
boolean firstMappingsPass = true;
|
boolean firstMappingsPass = true;
|
||||||
Int2IntMap dyeColors = new FixedInt2IntMap();
|
Int2IntMap dyeColors = new FixedInt2IntMap();
|
||||||
|
@ -99,11 +161,20 @@ public class ItemRegistryPopulator {
|
||||||
throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
|
throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used for custom items
|
||||||
|
int nextFreeBedrockId = 0;
|
||||||
|
List<ComponentItemData> componentItemData = new ObjectArrayList<>();
|
||||||
|
|
||||||
Map<String, StartGamePacket.ItemEntry> entries = new Object2ObjectOpenHashMap<>();
|
Map<String, StartGamePacket.ItemEntry> entries = new Object2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
for (PaletteItem entry : itemEntries) {
|
for (PaletteItem entry : itemEntries) {
|
||||||
entries.put(entry.getName(), new StartGamePacket.ItemEntry(entry.getName(), (short) entry.getId()));
|
int id = entry.getId();
|
||||||
bedrockIdentifierToId.put(entry.getName(), entry.getId());
|
if (id >= nextFreeBedrockId) {
|
||||||
|
nextFreeBedrockId = id + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
entries.put(entry.getName(), new StartGamePacket.ItemEntry(entry.getName(), (short) id));
|
||||||
|
bedrockIdentifierToId.put(entry.getName(), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object2IntMap<String> bedrockBlockIdOverrides = new Object2IntOpenHashMap<>();
|
Object2IntMap<String> bedrockBlockIdOverrides = new Object2IntOpenHashMap<>();
|
||||||
|
@ -203,18 +274,20 @@ public class ItemRegistryPopulator {
|
||||||
|
|
||||||
int itemIndex = 0;
|
int itemIndex = 0;
|
||||||
int javaFurnaceMinecartId = 0;
|
int javaFurnaceMinecartId = 0;
|
||||||
boolean usingFurnaceMinecart = GeyserImpl.getInstance().getConfig().isAddNonBedrockItems();
|
|
||||||
|
|
||||||
Set<String> javaOnlyItems = new ObjectOpenHashSet<>();
|
Set<String> javaOnlyItems = new ObjectOpenHashSet<>();
|
||||||
Collections.addAll(javaOnlyItems, "minecraft:spectral_arrow", "minecraft:debug_stick",
|
Collections.addAll(javaOnlyItems, "minecraft:spectral_arrow", "minecraft:debug_stick",
|
||||||
"minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:trader_llama_spawn_egg",
|
"minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:trader_llama_spawn_egg",
|
||||||
"minecraft:bundle");
|
"minecraft:bundle");
|
||||||
if (!usingFurnaceMinecart) {
|
if (!customItemsAllowed) {
|
||||||
javaOnlyItems.add("minecraft:furnace_minecart");
|
javaOnlyItems.add("minecraft:furnace_minecart");
|
||||||
}
|
}
|
||||||
// Java-only items for this version
|
// Java-only items for this version
|
||||||
javaOnlyItems.addAll(palette.getValue().additionalTranslatedItems().keySet());
|
javaOnlyItems.addAll(palette.getValue().additionalTranslatedItems().keySet());
|
||||||
|
|
||||||
|
Int2ObjectMap<String> customIdMappings = new Int2ObjectOpenHashMap<>();
|
||||||
|
Set<String> registeredItemNames = new ObjectOpenHashSet<>(); // This is used to check for duplicate item names
|
||||||
|
|
||||||
for (Map.Entry<String, GeyserMappingItem> entry : items.entrySet()) {
|
for (Map.Entry<String, GeyserMappingItem> entry : items.entrySet()) {
|
||||||
String javaIdentifier = entry.getKey().intern();
|
String javaIdentifier = entry.getKey().intern();
|
||||||
GeyserMappingItem mappingItem;
|
GeyserMappingItem mappingItem;
|
||||||
|
@ -226,7 +299,7 @@ public class ItemRegistryPopulator {
|
||||||
mappingItem = entry.getValue();
|
mappingItem = entry.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usingFurnaceMinecart && javaIdentifier.equals("minecraft:furnace_minecart")) {
|
if (customItemsAllowed && javaIdentifier.equals("minecraft:furnace_minecart")) {
|
||||||
javaFurnaceMinecartId = itemIndex;
|
javaFurnaceMinecartId = itemIndex;
|
||||||
itemIndex++;
|
itemIndex++;
|
||||||
// Will be added later
|
// Will be added later
|
||||||
|
@ -380,12 +453,46 @@ public class ItemRegistryPopulator {
|
||||||
.toolTier("");
|
.toolTier("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (javaOnlyItems.contains(javaIdentifier)) {
|
if (javaOnlyItems.contains(javaIdentifier)) {
|
||||||
// These items don't exist on Bedrock, so set up a variable that indicates they should have custom names
|
// These items don't exist on Bedrock, so set up a variable that indicates they should have custom names
|
||||||
mappingBuilder = mappingBuilder.translationString((bedrockBlockId != -1 ? "block." : "item.") + entry.getKey().replace(":", "."));
|
mappingBuilder = mappingBuilder.translationString((bedrockBlockId != -1 ? "block." : "item.") + entry.getKey().replace(":", "."));
|
||||||
GeyserImpl.getInstance().getLogger().debug("Adding " + entry.getKey() + " as an item that needs to be translated.");
|
GeyserImpl.getInstance().getLogger().debug("Adding " + entry.getKey() + " as an item that needs to be translated.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the custom item properties, if applicable
|
||||||
|
Object2IntMap<CustomItemOptions> customItemOptions;
|
||||||
|
Collection<CustomItemData> customItemsToLoad = customItems.get(javaIdentifier);
|
||||||
|
if (customItemsAllowed && !customItemsToLoad.isEmpty()) {
|
||||||
|
customItemOptions = new Object2IntOpenHashMap<>(customItemsToLoad.size());
|
||||||
|
|
||||||
|
for (CustomItemData customItem : customItemsToLoad) {
|
||||||
|
int customProtocolId = nextFreeBedrockId++;
|
||||||
|
|
||||||
|
String customItemName = "geyser_custom:" + customItem.name();
|
||||||
|
if (!registeredItemNames.add(customItemName)) {
|
||||||
|
if (firstMappingsPass) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Custom item name '" + customItem.name() + "' already exists and was registered again! Skipping...");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
GeyserCustomMappingData customMapping = CustomItemRegistryPopulator.registerCustomItem(
|
||||||
|
customItemName, mappingItem, customItem, customProtocolId
|
||||||
|
);
|
||||||
|
// StartGamePacket entry - needed for Bedrock to recognize the item through the protocol
|
||||||
|
entries.put(customMapping.stringId(), customMapping.startGamePacketItemEntry());
|
||||||
|
// ComponentItemData - used to register some custom properties
|
||||||
|
componentItemData.add(customMapping.componentItemData());
|
||||||
|
customItemOptions.put(customItem.customItemOptions(), customProtocolId);
|
||||||
|
|
||||||
|
customIdMappings.put(customMapping.integerId(), customMapping.stringId());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
customItemOptions = Object2IntMaps.emptyMap();
|
||||||
|
}
|
||||||
|
mappingBuilder.customItemOptions(customItemOptions);
|
||||||
|
|
||||||
ItemMapping mapping = mappingBuilder.build();
|
ItemMapping mapping = mappingBuilder.build();
|
||||||
|
|
||||||
if (javaIdentifier.contains("boat")) {
|
if (javaIdentifier.contains("boat")) {
|
||||||
|
@ -436,12 +543,12 @@ public class ItemRegistryPopulator {
|
||||||
.bedrockData(0)
|
.bedrockData(0)
|
||||||
.bedrockBlockId(-1)
|
.bedrockBlockId(-1)
|
||||||
.stackSize(1)
|
.stackSize(1)
|
||||||
|
.customItemOptions(Object2IntMaps.emptyMap())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
ComponentItemData furnaceMinecartData = null;
|
if (customItemsAllowed) {
|
||||||
if (usingFurnaceMinecart) {
|
|
||||||
// Add the furnace minecart as a custom item
|
// Add the furnace minecart as a custom item
|
||||||
int furnaceMinecartId = mappings.size() + 1;
|
int furnaceMinecartId = nextFreeBedrockId++;
|
||||||
|
|
||||||
entries.put("geysermc:furnace_minecart", new StartGamePacket.ItemEntry("geysermc:furnace_minecart", (short) furnaceMinecartId, true));
|
entries.put("geysermc:furnace_minecart", new StartGamePacket.ItemEntry("geysermc:furnace_minecart", (short) furnaceMinecartId, true));
|
||||||
|
|
||||||
|
@ -456,7 +563,7 @@ public class ItemRegistryPopulator {
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
creativeItems.add(ItemData.builder()
|
creativeItems.add(ItemData.builder()
|
||||||
.netId(netId)
|
.netId(netId++)
|
||||||
.id(furnaceMinecartId)
|
.id(furnaceMinecartId)
|
||||||
.count(1).build());
|
.count(1).build());
|
||||||
|
|
||||||
|
@ -492,7 +599,36 @@ public class ItemRegistryPopulator {
|
||||||
|
|
||||||
componentBuilder.putCompound("item_properties", itemProperties.build());
|
componentBuilder.putCompound("item_properties", itemProperties.build());
|
||||||
builder.putCompound("components", componentBuilder.build());
|
builder.putCompound("components", componentBuilder.build());
|
||||||
furnaceMinecartData = new ComponentItemData("geysermc:furnace_minecart", builder.build());
|
componentItemData.add(new ComponentItemData("geysermc:furnace_minecart", builder.build()));
|
||||||
|
|
||||||
|
// Register any completely custom items given to us
|
||||||
|
IntSet registeredJavaIds = new IntOpenHashSet(); // Used to check for duplicate item java ids
|
||||||
|
for (NonVanillaCustomItemData customItem : nonVanillaCustomItems) {
|
||||||
|
if (!registeredJavaIds.add(customItem.javaId())) {
|
||||||
|
if (firstMappingsPass) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error("Custom item java id " + customItem.javaId() + " already exists and was registered again! Skipping...");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int customItemId = nextFreeBedrockId++;
|
||||||
|
NonVanillaItemRegistration registration = CustomItemRegistryPopulator.registerCustomItem(customItem, customItemId);
|
||||||
|
|
||||||
|
componentItemData.add(registration.componentItemData());
|
||||||
|
ItemMapping mapping = registration.mapping();
|
||||||
|
while (mapping.getJavaId() >= mappings.size()) {
|
||||||
|
// Fill with empty to get to the correct size
|
||||||
|
mappings.add(ItemMapping.AIR);
|
||||||
|
}
|
||||||
|
mappings.set(mapping.getJavaId(), mapping);
|
||||||
|
|
||||||
|
if (customItem.creativeGroup() != null || customItem.creativeCategory().isPresent()) {
|
||||||
|
creativeItems.add(ItemData.builder()
|
||||||
|
.id(customItemId)
|
||||||
|
.netId(netId++)
|
||||||
|
.count(1).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemMappings itemMappings = ItemMappings.builder()
|
ItemMappings itemMappings = ItemMappings.builder()
|
||||||
|
@ -506,8 +642,9 @@ public class ItemRegistryPopulator {
|
||||||
.boatIds(boats)
|
.boatIds(boats)
|
||||||
.spawnEggIds(spawnEggs)
|
.spawnEggIds(spawnEggs)
|
||||||
.carpets(carpets)
|
.carpets(carpets)
|
||||||
.furnaceMinecartData(furnaceMinecartData)
|
.componentItemData(componentItemData)
|
||||||
.lodestoneCompass(lodestoneEntry)
|
.lodestoneCompass(lodestoneEntry)
|
||||||
|
.customIdMappings(customIdMappings)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
Registries.ITEMS.register(palette.getValue().protocolVersion(), itemMappings);
|
Registries.ITEMS.register(palette.getValue().protocolVersion(), itemMappings);
|
||||||
|
|
|
@ -29,8 +29,14 @@ 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.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.CustomItemOptions;
|
||||||
|
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
||||||
import org.geysermc.geyser.api.provider.BuilderProvider;
|
import org.geysermc.geyser.api.provider.BuilderProvider;
|
||||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||||
|
import org.geysermc.geyser.item.GeyserCustomItemData;
|
||||||
|
import org.geysermc.geyser.item.GeyserCustomItemOptions;
|
||||||
|
import org.geysermc.geyser.item.GeyserNonVanillaCustomItemData;
|
||||||
import org.geysermc.geyser.registry.ProviderRegistries;
|
import org.geysermc.geyser.registry.ProviderRegistries;
|
||||||
import org.geysermc.geyser.registry.SimpleMappedRegistry;
|
import org.geysermc.geyser.registry.SimpleMappedRegistry;
|
||||||
|
|
||||||
|
@ -46,6 +52,9 @@ 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(CustomItemData.Builder.class, args -> new GeyserCustomItemData.CustomItemDataBuilder());
|
||||||
|
providers.put(CustomItemOptions.Builder.class, args -> new GeyserCustomItemOptions.CustomItemOptionsBuilder());
|
||||||
|
providers.put(NonVanillaCustomItemData.Builder.class, args -> new GeyserNonVanillaCustomItemData.NonVanillaCustomItemDataBuilder());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -42,6 +42,8 @@ public class GeyserMappingItem {
|
||||||
@JsonProperty("stack_size") int stackSize = 64;
|
@JsonProperty("stack_size") int stackSize = 64;
|
||||||
@JsonProperty("tool_type") String toolType;
|
@JsonProperty("tool_type") String toolType;
|
||||||
@JsonProperty("tool_tier") String toolTier;
|
@JsonProperty("tool_tier") String toolTier;
|
||||||
|
@JsonProperty("armor_type") String armorType;
|
||||||
|
@JsonProperty("protection_value") int protectionValue;
|
||||||
@JsonProperty("max_damage") int maxDamage = 0;
|
@JsonProperty("max_damage") int maxDamage = 0;
|
||||||
@JsonProperty("repair_materials") List<String> repairMaterials;
|
@JsonProperty("repair_materials") List<String> repairMaterials;
|
||||||
@JsonProperty("has_suspicious_stew_effect") boolean hasSuspiciousStewEffect = false;
|
@JsonProperty("has_suspicious_stew_effect") boolean hasSuspiciousStewEffect = false;
|
||||||
|
|
|
@ -25,9 +25,12 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.registry.type;
|
package org.geysermc.geyser.registry.type;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
|
|
||||||
|
@ -39,7 +42,7 @@ import java.util.Set;
|
||||||
public class ItemMapping {
|
public class ItemMapping {
|
||||||
public static final ItemMapping AIR = new ItemMapping("minecraft:air", "minecraft:air", 0, 0, 0,
|
public static final ItemMapping AIR = new ItemMapping("minecraft:air", "minecraft:air", 0, 0, 0,
|
||||||
BlockRegistries.BLOCKS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getBedrockAirId(),
|
BlockRegistries.BLOCKS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getBedrockAirId(),
|
||||||
64, null, null, null, 0, null, false);
|
64, null, null, null, Object2IntMaps.emptyMap(), 0, null, false);
|
||||||
|
|
||||||
String javaIdentifier;
|
String javaIdentifier;
|
||||||
String bedrockIdentifier;
|
String bedrockIdentifier;
|
||||||
|
@ -59,6 +62,8 @@ public class ItemMapping {
|
||||||
|
|
||||||
String translationString;
|
String translationString;
|
||||||
|
|
||||||
|
Object2IntMap<CustomItemOptions> customItemOptions;
|
||||||
|
|
||||||
int maxDamage;
|
int maxDamage;
|
||||||
|
|
||||||
Set<String> repairMaterials;
|
Set<String> repairMaterials;
|
||||||
|
@ -91,4 +96,4 @@ public class ItemMapping {
|
||||||
public boolean isTool() {
|
public boolean isTool() {
|
||||||
return this.toolType != null;
|
return this.toolType != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData;
|
import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
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.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
@ -36,7 +37,6 @@ import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.inventory.item.StoredItemMappings;
|
import org.geysermc.geyser.inventory.item.StoredItemMappings;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -67,7 +67,8 @@ public class ItemMappings {
|
||||||
IntList spawnEggIds;
|
IntList spawnEggIds;
|
||||||
List<ItemData> carpets;
|
List<ItemData> carpets;
|
||||||
|
|
||||||
@Nullable ComponentItemData furnaceMinecartData;
|
List<ComponentItemData> componentItemData;
|
||||||
|
Int2ObjectMap<String> customIdMappings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets an {@link ItemMapping} from the given {@link ItemStack}.
|
* Gets an {@link ItemMapping} from the given {@link ItemStack}.
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @author GeyserMC
|
||||||
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.geysermc.geyser.registry.type;
|
||||||
|
|
||||||
|
import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The return data of a successful registration of a custom item.
|
||||||
|
*/
|
||||||
|
public record NonVanillaItemRegistration(ComponentItemData componentItemData, ItemMapping mapping) {
|
||||||
|
}
|
|
@ -594,9 +594,9 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||||
// Set the hardcoded shield ID to the ID we just defined in StartGamePacket
|
// Set the hardcoded shield ID to the ID we just defined in StartGamePacket
|
||||||
upstream.getSession().getHardcodedBlockingId().set(this.itemMappings.getStoredItems().shield().getBedrockId());
|
upstream.getSession().getHardcodedBlockingId().set(this.itemMappings.getStoredItems().shield().getBedrockId());
|
||||||
|
|
||||||
if (this.itemMappings.getFurnaceMinecartData() != null) {
|
if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
|
||||||
ItemComponentPacket componentPacket = new ItemComponentPacket();
|
ItemComponentPacket componentPacket = new ItemComponentPacket();
|
||||||
componentPacket.getItems().add(this.itemMappings.getFurnaceMinecartData());
|
componentPacket.getItems().addAll(itemMappings.getComponentItemData());
|
||||||
upstream.sendPacket(componentPacket);
|
upstream.sendPacket(componentPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1465,7 +1465,9 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||||
// startGamePacket.setCurrentTick(0);
|
// startGamePacket.setCurrentTick(0);
|
||||||
startGamePacket.setEnchantmentSeed(0);
|
startGamePacket.setEnchantmentSeed(0);
|
||||||
startGamePacket.setMultiplayerCorrelationId("");
|
startGamePacket.setMultiplayerCorrelationId("");
|
||||||
|
|
||||||
startGamePacket.setItemEntries(this.itemMappings.getItemEntries());
|
startGamePacket.setItemEntries(this.itemMappings.getItemEntries());
|
||||||
|
|
||||||
startGamePacket.setVanillaVersion("*");
|
startGamePacket.setVanillaVersion("*");
|
||||||
startGamePacket.setInventoriesServerAuthoritative(true);
|
startGamePacket.setInventoriesServerAuthoritative(true);
|
||||||
startGamePacket.setServerEngine(""); // Do we want to fill this in?
|
startGamePacket.setServerEngine(""); // Do we want to fill this in?
|
||||||
|
|
|
@ -79,8 +79,7 @@ public class CompassTranslator extends ItemTranslator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ItemMapping> getAppliedItems() {
|
public List<ItemMapping> getAppliedItems() {
|
||||||
return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
|
return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems())
|
||||||
.getItems())
|
|
||||||
.filter(entry -> entry.getJavaIdentifier().endsWith("compass"))
|
.filter(entry -> entry.getJavaIdentifier().endsWith("compass"))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,12 @@ import com.nukkitx.nbt.NbtType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
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.item.custom.CustomItemOptions;
|
||||||
|
import org.geysermc.geyser.api.util.TriState;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||||
|
@ -122,7 +125,7 @@ public abstract class ItemTranslator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (itemStack.getNbt().isEmpty()) {
|
if (itemStack.getNbt().isEmpty()) {
|
||||||
// Otherwise, seems to causes issues with villagers accepting books, and I don't see how this will break anything else. - Camotoy
|
// Otherwise, seems to cause issues with villagers accepting books, and I don't see how this will break anything else. - Camotoy
|
||||||
itemStack = new ItemStack(itemStack.getId(), itemStack.getAmount(), null);
|
itemStack = new ItemStack(itemStack.getId(), itemStack.getAmount(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,6 +173,8 @@ public abstract class ItemTranslator {
|
||||||
builder.blockRuntimeId(bedrockItem.getBedrockBlockId());
|
builder.blockRuntimeId(bedrockItem.getBedrockBlockId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
translateCustomItem(nbt, builder, bedrockItem);
|
||||||
|
|
||||||
if (nbt != null) {
|
if (nbt != null) {
|
||||||
// Translate the canDestroy and canPlaceOn Java NBT
|
// Translate the canDestroy and canPlaceOn Java NBT
|
||||||
ListTag canDestroy = nbt.get("CanDestroy");
|
ListTag canDestroy = nbt.get("CanDestroy");
|
||||||
|
@ -292,6 +297,10 @@ public abstract class ItemTranslator {
|
||||||
if (itemStack.getNbt() != null) {
|
if (itemStack.getNbt() != null) {
|
||||||
builder.tag(this.translateNbtToBedrock(itemStack.getNbt()));
|
builder.tag(this.translateNbtToBedrock(itemStack.getNbt()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CompoundTag nbt = itemStack.getNbt();
|
||||||
|
translateCustomItem(nbt, builder, mapping);
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,7 +425,7 @@ public abstract class ItemTranslator {
|
||||||
if (object instanceof byte[]) {
|
if (object instanceof byte[]) {
|
||||||
return new ByteArrayTag(name, (byte[]) object);
|
return new ByteArrayTag(name, (byte[]) object);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object instanceof Byte) {
|
if (object instanceof Byte) {
|
||||||
return new ByteTag(name, (byte) object);
|
return new ByteTag(name, (byte) object);
|
||||||
}
|
}
|
||||||
|
@ -524,6 +533,48 @@ public abstract class ItemTranslator {
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translates the custom model data of an item
|
||||||
|
*/
|
||||||
|
private static void translateCustomItem(CompoundTag nbt, ItemData.Builder builder, ItemMapping mapping) {
|
||||||
|
int bedrockId = getCustomItem(nbt, mapping);
|
||||||
|
if (bedrockId != -1) {
|
||||||
|
builder.id(bedrockId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getCustomItem(CompoundTag nbt, ItemMapping mapping) {
|
||||||
|
if (nbt == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Object2IntMap<CustomItemOptions> customMappings = mapping.getCustomItemOptions();
|
||||||
|
if (customMappings.isEmpty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int customModelData = nbt.get("CustomModelData") instanceof IntTag customModelDataTag ? customModelDataTag.getValue() : 0;
|
||||||
|
TriState unbreakable = TriState.fromBoolean(nbt.get("Unbreakable") instanceof ByteTag unbreakableTag && unbreakableTag.getValue() == 1);
|
||||||
|
int damage = nbt.get("Damage") instanceof IntTag damageTag ? damageTag.getValue() : 0;
|
||||||
|
for (Object2IntMap.Entry<CustomItemOptions> mappingTypes : customMappings.object2IntEntrySet()) {
|
||||||
|
CustomItemOptions options = mappingTypes.getKey();
|
||||||
|
|
||||||
|
TriState unbreakableOption = options.unbreakable();
|
||||||
|
if (unbreakableOption == unbreakable) { // Implementation note: if the option is NOT_SET then this comparison will always be false because of how the item unbreaking TriState is created
|
||||||
|
return mappingTypes.getIntValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionalInt customModelDataOption = options.customModelData();
|
||||||
|
if (customModelDataOption.isPresent() && customModelDataOption.getAsInt() == customModelData) {
|
||||||
|
return mappingTypes.getIntValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionalInt damagePredicate = options.damagePredicate();
|
||||||
|
if (damagePredicate.isPresent() && damagePredicate.getAsInt() == damage) {
|
||||||
|
return mappingTypes.getIntValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an {@link ItemStack} is equal to another item stack
|
* Checks if an {@link ItemStack} is equal to another item stack
|
||||||
*
|
*
|
||||||
|
|
|
@ -74,8 +74,7 @@ public class PotionTranslator extends ItemTranslator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ItemMapping> getAppliedItems() {
|
public List<ItemMapping> getAppliedItems() {
|
||||||
return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
|
return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems())
|
||||||
.getItems())
|
|
||||||
.filter(entry -> entry.getJavaIdentifier().endsWith("potion"))
|
.filter(entry -> entry.getJavaIdentifier().endsWith("potion"))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,8 +81,7 @@ public class TippedArrowTranslator extends ItemTranslator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ItemMapping> getAppliedItems() {
|
public List<ItemMapping> getAppliedItems() {
|
||||||
return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
|
return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems())
|
||||||
.getItems())
|
|
||||||
.filter(entry -> entry.getJavaIdentifier().contains("arrow")
|
.filter(entry -> entry.getJavaIdentifier().contains("arrow")
|
||||||
&& !entry.getJavaIdentifier().contains("spectral"))
|
&& !entry.getJavaIdentifier().contains("spectral"))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
|
@ -76,8 +76,7 @@ public class BannerTranslator extends NbtItemStackTranslator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public BannerTranslator() {
|
public BannerTranslator() {
|
||||||
appliedItems = Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
|
appliedItems = Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems())
|
||||||
.getItems())
|
|
||||||
.filter(entry -> entry.getJavaIdentifier().endsWith("banner"))
|
.filter(entry -> entry.getJavaIdentifier().endsWith("banner"))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,11 +168,12 @@ public class JavaMerchantOffersTranslator extends PacketTranslator<ClientboundMe
|
||||||
|
|
||||||
private static NbtMap getItemTag(GeyserSession session, ItemStack stack, ItemMapping mapping, int count) {
|
private static NbtMap getItemTag(GeyserSession session, ItemStack stack, ItemMapping mapping, int count) {
|
||||||
ItemData itemData = ItemTranslator.translateToBedrock(session, stack);
|
ItemData itemData = ItemTranslator.translateToBedrock(session, stack);
|
||||||
|
String customIdentifier = session.getItemMappings().getCustomIdMappings().get(itemData.getId());
|
||||||
|
|
||||||
NbtMapBuilder builder = NbtMap.builder();
|
NbtMapBuilder builder = NbtMap.builder();
|
||||||
builder.putByte("Count", (byte) count);
|
builder.putByte("Count", (byte) count);
|
||||||
builder.putShort("Damage", (short) itemData.getDamage());
|
builder.putShort("Damage", (short) itemData.getDamage());
|
||||||
builder.putString("Name", mapping.getBedrockIdentifier());
|
builder.putString("Name", customIdentifier != null ? customIdentifier : mapping.getBedrockIdentifier());
|
||||||
if (itemData.getTag() != null) {
|
if (itemData.getTag() != null) {
|
||||||
NbtMap tag = itemData.getTag().toBuilder().build();
|
NbtMap tag = itemData.getTag().toBuilder().build();
|
||||||
builder.put("tag", tag);
|
builder.put("tag", tag);
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 99a1f8070e844d059454dacbb6e8b203521eed23
|
Subproject commit 919908f4825e9fa1bb7b5a2f5e09218f0a3f72f3
|
Loading…
Reference in a new issue