diff options
author | Bjarne Koll <[email protected]> | 2024-09-19 16:36:07 +0200 |
---|---|---|
committer | GitHub <[email protected]> | 2024-09-19 16:36:07 +0200 |
commit | c5a10665b8b80af650500b9263036f778f06d500 (patch) | |
tree | fedc133f0dbc101067951e1fccd9d577c312fdb8 /patches/server/0374-Paper-dumpitem-command.patch | |
parent | 5c829557332f21b34bc81e6ad1a73e511faef8f6 (diff) | |
download | Paper-c5a10665b8b80af650500b9263036f778f06d500.tar.gz Paper-c5a10665b8b80af650500b9263036f778f06d500.zip |
Remove wall-time / unused skip tick protection (#11412)
Spigot still maintains some partial implementation of "tick skipping", a
practice in which the MinecraftServer.currentTick field is updated not
by an increment of one per actual tick, but instead set to
System.currentTimeMillis() / 50. This behaviour means that the tracked
tick may "skip" a tick value in case a previous tick took more than the
expected 50ms.
To compensate for this in important paths, spigot/craftbukkit
implements "wall-time". Instead of incrementing/decrementing ticks on
block entities/entities by one for each call to their tick() method,
they instead increment/decrement important values, like
an ItemEntity's age or pickupDelay, by the difference of
`currentTick - lastTick`, where `lastTick` is the value of
`currentTick` during the last tick() call.
These "fixes" however do not play nicely with minecraft's simulation
distance as entities/block entities implementing the above behaviour
would "catch up" their values when moving from a non-ticking chunk to a
ticking one as their `lastTick` value remains stuck on the last tick in
a ticking chunk and hence lead to a large "catch up" once ticked again.
Paper completely removes the "tick skipping" behaviour (See patch
"Further-improve-server-tick-loop"), making the above precautions
completely unnecessary, which also rids paper of the previous described
incompatibility with non-ticking chunks.
Diffstat (limited to 'patches/server/0374-Paper-dumpitem-command.patch')
-rw-r--r-- | patches/server/0374-Paper-dumpitem-command.patch | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/patches/server/0374-Paper-dumpitem-command.patch b/patches/server/0374-Paper-dumpitem-command.patch new file mode 100644 index 0000000000..088efe087c --- /dev/null +++ b/patches/server/0374-Paper-dumpitem-command.patch @@ -0,0 +1,158 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar <[email protected]> +Date: Sun, 28 Jun 2020 19:27:20 -0400 +Subject: [PATCH] Paper dumpitem command + +Let's you quickly view the item in your hands NBT data + +diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java +index 3010d57efcc97fb409bfe43b1fc9af198c099a67..cdad0fd5257ae842f83b9c1c98b4565b468d4f54 100644 +--- a/src/main/java/io/papermc/paper/command/PaperCommand.java ++++ b/src/main/java/io/papermc/paper/command/PaperCommand.java +@@ -39,6 +39,7 @@ public final class PaperCommand extends Command { + commands.put(Set.of("version"), new VersionCommand()); + commands.put(Set.of("dumpplugins"), new DumpPluginsCommand()); + commands.put(Set.of("syncloadinfo"), new SyncLoadInfoCommand()); ++ commands.put(Set.of("dumpitem"), new DumpItemCommand()); + + return commands.entrySet().stream() + .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue()))) +diff --git a/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java b/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..e993177b052c76cb3f9c44edb598ebb4be858393 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java +@@ -0,0 +1,133 @@ ++package io.papermc.paper.command.subcommands; ++ ++import io.papermc.paper.adventure.PaperAdventure; ++import io.papermc.paper.command.CommandUtil; ++import io.papermc.paper.command.PaperSubcommand; ++import java.util.ArrayList; ++import java.util.Collections; ++import java.util.IdentityHashMap; ++import java.util.List; ++import java.util.Map; ++import java.util.Optional; ++import java.util.Set; ++import java.util.function.Consumer; ++import net.kyori.adventure.text.Component; ++import net.kyori.adventure.text.ComponentLike; ++import net.kyori.adventure.text.JoinConfiguration; ++import net.kyori.adventure.text.TextComponent; ++import net.minecraft.core.Registry; ++import net.minecraft.core.RegistryAccess; ++import net.minecraft.core.component.DataComponentMap; ++import net.minecraft.core.component.DataComponentPatch; ++import net.minecraft.core.component.DataComponentType; ++import net.minecraft.core.component.TypedDataComponent; ++import net.minecraft.core.registries.Registries; ++import net.minecraft.nbt.NbtOps; ++import net.minecraft.nbt.NbtUtils; ++import net.minecraft.nbt.SnbtPrinterTagVisitor; ++import net.minecraft.nbt.Tag; ++import net.minecraft.resources.RegistryOps; ++import net.minecraft.world.item.ItemStack; ++import org.bukkit.command.CommandSender; ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.entity.Player; ++import org.checkerframework.checker.nullness.qual.NonNull; ++import org.checkerframework.checker.nullness.qual.Nullable; ++import org.checkerframework.framework.qual.DefaultQualifier; ++ ++import static net.kyori.adventure.text.Component.join; ++import static net.kyori.adventure.text.Component.text; ++import static net.kyori.adventure.text.Component.textOfChildren; ++import static net.kyori.adventure.text.event.ClickEvent.copyToClipboard; ++import static net.kyori.adventure.text.format.NamedTextColor.AQUA; ++import static net.kyori.adventure.text.format.NamedTextColor.GRAY; ++import static net.kyori.adventure.text.format.NamedTextColor.RED; ++import static net.kyori.adventure.text.format.NamedTextColor.WHITE; ++import static net.kyori.adventure.text.format.NamedTextColor.YELLOW; ++import static net.kyori.adventure.text.format.TextColor.color; ++import static net.kyori.adventure.text.format.TextDecoration.ITALIC; ++ ++@DefaultQualifier(NonNull.class) ++public final class DumpItemCommand implements PaperSubcommand { ++ @Override ++ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { ++ this.doDumpItem(sender, args.length > 0 && "all".equals(args[0])); ++ return true; ++ } ++ ++ @SuppressWarnings({"unchecked", "OptionalAssignedToNull", "rawtypes"}) ++ private void doDumpItem(final CommandSender sender, final boolean includeAllComponents) { ++ if (!(sender instanceof final Player player)) { ++ sender.sendMessage("Only players can use this command"); ++ return; ++ } ++ final ItemStack itemStack = CraftItemStack.asNMSCopy(player.getInventory().getItemInMainHand()); ++ final TextComponent.Builder visualOutput = Component.text(); ++ final StringBuilder itemCommandBuilder = new StringBuilder(); ++ final String itemName = itemStack.getItemHolder().unwrapKey().orElseThrow().location().toString(); ++ itemCommandBuilder.append(itemName); ++ visualOutput.append(text(itemName, YELLOW)); // item type ++ final Set<DataComponentType<?>> referencedComponentTypes = Collections.newSetFromMap(new IdentityHashMap<>()); ++ final DataComponentPatch patch = itemStack.getComponentsPatch(); ++ referencedComponentTypes.addAll(patch.entrySet().stream().map(Map.Entry::getKey).toList()); ++ final DataComponentMap prototype = itemStack.getItem().components(); ++ if (includeAllComponents) { ++ referencedComponentTypes.addAll(prototype.keySet()); ++ } ++ ++ final RegistryAccess.Frozen access = ((CraftServer) sender.getServer()).getServer().registryAccess(); ++ final RegistryOps<Tag> ops = access.createSerializationContext(NbtOps.INSTANCE); ++ final Registry<DataComponentType<?>> registry = access.registryOrThrow(Registries.DATA_COMPONENT_TYPE); ++ final List<ComponentLike> componentComponents = new ArrayList<>(); ++ final List<String> commandComponents = new ArrayList<>(); ++ for (final DataComponentType<?> type : referencedComponentTypes) { ++ final String path = registry.getResourceKey(type).orElseThrow().location().getPath(); ++ final @Nullable Optional<?> patchedValue = patch.get(type); ++ final @Nullable TypedDataComponent<?> prototypeValue = prototype.getTyped(type); ++ if (patchedValue != null) { ++ if (patchedValue.isEmpty()) { ++ componentComponents.add(text().append(text('!', RED), text(path, AQUA))); ++ commandComponents.add("!" + path); ++ } else { ++ final Tag serialized = (Tag) ((DataComponentType) type).codecOrThrow().encodeStart(ops, patchedValue.get()).getOrThrow(); ++ writeComponentValue(componentComponents::add, commandComponents::add, path, serialized); ++ } ++ } else if (includeAllComponents && prototypeValue != null) { ++ final Tag serialized = prototypeValue.encodeValue(ops).getOrThrow(); ++ writeComponentValue(componentComponents::add, commandComponents::add, path, serialized); ++ } ++ } ++ if (!componentComponents.isEmpty()) { ++ visualOutput.append( ++ text("[", color(0x8910CE)), ++ join(JoinConfiguration.separator(text(",", GRAY)), componentComponents), ++ text("]", color(0x8910CE)) ++ ); ++ itemCommandBuilder ++ .append("[") ++ .append(String.join(",", commandComponents)) ++ .append("]"); ++ } ++ player.sendMessage(visualOutput.build().compact()); ++ final Component copyMsg = text("Click to copy item definition to clipboard for use with /give", GRAY, ITALIC); ++ sender.sendMessage(copyMsg.clickEvent(copyToClipboard(itemCommandBuilder.toString()))); ++ } ++ ++ private static void writeComponentValue(final Consumer<Component> visualOutput, final Consumer<String> commandOutput, final String path, final Tag serialized) { ++ visualOutput.accept(textOfChildren( ++ text(path, color(0xFF7FD7)), ++ text("=", WHITE), ++ PaperAdventure.asAdventure(NbtUtils.toPrettyComponent(serialized)) ++ )); ++ commandOutput.accept(path + "=" + new SnbtPrinterTagVisitor("", 0, new ArrayList<>()).visit(serialized)); ++ } ++ ++ @Override ++ public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) { ++ if (args.length == 1) { ++ return CommandUtil.getListMatchingLast(sender, args, "all"); ++ } ++ return Collections.emptyList(); ++ } ++} |