3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-10-02 08:00:07 +02:00

Custom tool breakspeed by server; Closes #3348

Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com>
Dieser Commit ist enthalten in:
Joshua Castle 2023-03-05 20:21:56 -08:00
Ursprung e077407a1a
Commit 0d110ca4f4
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: F674F38216C35D5D
4 geänderte Dateien mit 75 neuen und 228 gelöschten Zeilen

Datei anzeigen

@ -1,174 +0,0 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.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);
}
}

Datei anzeigen

@ -36,13 +36,14 @@ import org.geysermc.geyser.api.item.custom.CustomRenderOffsets;
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
import org.geysermc.geyser.api.util.TriState;
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.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -226,34 +227,44 @@ public class CustomItemRegistryPopulator {
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");
}
}
// This means client side the tool can never destroy a block
// This works because the molang '1' for tags will be true for all blocks and the speed will be 0
// We want this since we calculate break speed server side in BedrockActionTranslator
List<NbtMap> speed = new ArrayList<>(List.of(
NbtMap.builder()
.putCompound("block", NbtMap.builder()
.putString("tags", "1")
.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", 0)
.build()
));
componentBuilder.putCompound("minecraft:digger",
NbtMap.builder()
.putList("destroy_speeds", NbtType.COMPOUND, speed)
.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()
);
if (toolType.equals("sword")) {
miningSpeed = 1.5f;
canDestroyInCreative = false;
componentBuilder.putCompound("minecraft:weapon", NbtMap.EMPTY);
}
itemProperties.putBoolean("hand_equipped", true);

Datei anzeigen

@ -39,9 +39,9 @@ import java.util.OptionalInt;
/**
* This is only a separate class for testing purposes so we don't have to load in GeyserImpl in ItemTranslator.
*/
final class CustomItemTranslator {
public final class CustomItemTranslator {
static int getCustomItem(CompoundTag nbt, ItemMapping mapping) {
public static int getCustomItem(CompoundTag nbt, ItemMapping mapping) {
if (nbt == null) {
return -1;
}

Datei anzeigen

@ -42,9 +42,13 @@ import org.geysermc.geyser.api.block.custom.CustomBlockState;
import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.ItemFrameEntity;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.SkullCache;
import org.geysermc.geyser.translator.inventory.item.CustomItemTranslator;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.BlockUtils;
@ -148,9 +152,16 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
startBreak.setType(LevelEventType.BLOCK_START_BREAK);
startBreak.setPosition(vector.toFloat());
double breakTime = BlockUtils.getSessionBreakTime(session, BlockRegistries.JAVA_BLOCKS.get(blockState)) * 20;
// If the block is custom or the breaking item is custom, we must keep track of break time ourselves
GeyserItemStack item = session.getPlayerInventory().getItemInHand();
ItemMapping mapping = item.getMapping(session);
int customItemId = mapping.isTool() ? CustomItemTranslator.getCustomItem(item.getNbt(), mapping) : -1;
CustomBlockState blockStateOverride = BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get(blockState);
// If the block is custom, we must keep track of when it should break ourselves
if (blockStateOverride != null) {
SkullCache.Skull skull = session.getSkullCache().getSkulls().get(vector);
session.setBlockBreakStartTime(0);
if (blockStateOverride != null || customItemId > -1 || (skull != null && skull.getCustomRuntimeId() != -1)) {
session.setBlockBreakStartTime(System.currentTimeMillis());
}
startBreak.setData((int) (65535 / breakTime));
@ -197,27 +208,26 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
updateBreak.setPosition(vectorFloat);
double breakTime = BlockUtils.getSessionBreakTime(session, BlockRegistries.JAVA_BLOCKS.get(breakingBlock)) * 20;
CustomBlockState blockStateOverride = BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get(breakingBlock);
if (blockStateOverride != null) {
// If the block is custom, we must keep track of when it should break ourselves
long blockBreakStartTime = session.getBlockBreakStartTime();
if (blockBreakStartTime != 0) {
long timeSinceStart = System.currentTimeMillis() - blockBreakStartTime;
if (timeSinceStart >= breakTime * 50) {
// Play break sound and particle
LevelEventPacket effectPacket = new LevelEventPacket();
effectPacket.setPosition(vectorFloat);
effectPacket.setType(LevelEventType.PARTICLE_DESTROY_BLOCK);
effectPacket.setData(session.getBlockMappings().getBedrockBlockId(breakingBlock));
session.sendUpstreamPacket(effectPacket);
// Break the block
ServerboundPlayerActionPacket finishBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.FINISH_DIGGING,
vector, Direction.VALUES[packet.getFace()], session.getWorldCache().nextPredictionSequence());
session.sendDownstreamPacket(finishBreakingPacket);
session.setBlockBreakStartTime(0);
break;
}
// If the block is custom, we must keep track of when it should break ourselves
long blockBreakStartTime = session.getBlockBreakStartTime();
if (blockBreakStartTime != 0) {
long timeSinceStart = System.currentTimeMillis() - blockBreakStartTime;
// We need to add a slight delay to the break time, otherwise the client breaks blocks too fast
if (timeSinceStart >= (breakTime+=2) * 50) {
// Play break sound and particle
LevelEventPacket effectPacket = new LevelEventPacket();
effectPacket.setPosition(vectorFloat);
effectPacket.setType(LevelEventType.PARTICLE_DESTROY_BLOCK);
effectPacket.setData(session.getBlockMappings().getBedrockBlockId(breakingBlock));
session.sendUpstreamPacket(effectPacket);
// Break the block
ServerboundPlayerActionPacket finishBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.FINISH_DIGGING,
vector, Direction.VALUES[packet.getFace()], session.getWorldCache().nextPredictionSequence());
session.sendDownstreamPacket(finishBreakingPacket);
session.setBlockBreakStartTime(0);
break;
}
}