9
0
mirror of https://github.com/LeavesMC/Leaves.git synced 2025-12-28 19:39:22 +00:00
---------

Co-authored-by: violetc <58360096+s-yh-china@users.noreply.github.com>
Co-authored-by: Fortern <blueten.ki@gmail.com>
Co-authored-by: MC_XiaoHei <xor7xiaohei@gmail.com>
Co-authored-by: Helvetica Volubi <88063803+Suisuroru@users.noreply.github.com>
Co-authored-by: MC_XiaoHei <xiaohei.xor7@outlook.com>
This commit is contained in:
Lumine1909
2025-06-05 18:41:51 +08:00
committed by GitHub
parent f553c53e42
commit f09fbb247d
379 changed files with 6930 additions and 6288 deletions

View File

@@ -0,0 +1,6 @@
package co.aikar.timings;
// It is only used to make certain plugin "confirm" that this is a Paper server
// since we removed all timings related codes and classes
public class Timings {
}

View File

@@ -4,7 +4,9 @@ import com.destroystokyo.paper.util.SneakyThrow;
import io.papermc.paper.configuration.GlobalConfiguration;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import org.bukkit.command.Command;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.permissions.Permission;
@@ -28,6 +30,7 @@ import org.leavesmc.leaves.protocol.CarpetServerProtocol.CarpetRules;
import org.leavesmc.leaves.protocol.bladeren.BladerenProtocol.LeavesFeature;
import org.leavesmc.leaves.protocol.bladeren.BladerenProtocol.LeavesFeatureSet;
import org.leavesmc.leaves.region.RegionFileFormat;
import org.leavesmc.leaves.util.ElytraAeronauticsHelper;
import org.leavesmc.leaves.util.ForcePeacefulModeSwitchType;
import org.leavesmc.leaves.util.MathUtils;
@@ -39,6 +42,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.Set;
import java.util.function.Predicate;
public final class LeavesConfig {
@@ -216,14 +220,19 @@ public final class LeavesConfig {
@GlobalConfig("cce-update-suppression")
public boolean cceUpdateSuppression = false;
@GlobalConfig("sound-update-suppression")
public boolean soundUpdateSuppression = false;
@RemovedConfig(name = "redstone-wire-dont-connect-if-on-trapdoor", category = "modify", transform = true)
@RemovedConfig(name = "redstone-wire-dont-connect-if-on-trapdoor", category = {"modify", "minecraft-old"}, transform = true)
@GlobalConfig("redstone-wire-dont-connect-if-on-trapdoor")
public boolean redstoneDontCantOnTrapDoor = false;
@RemovedConfig(name = "redstone-wire-dont-connect-if-on-trapdoor", category = {"modify", "minecraft-old", "block-updater"}, transform = true)
@GlobalConfig("redstone-ignore-upwards-update")
public boolean redstoneIgnoreUpwardsUpdate = false;
@RemovedConfig(name = "old-block-entity-behaviour", category = {"modify", "minecraft-old"}, transform = true)
@GlobalConfig("old-block-entity-behaviour")
public boolean oldBlockEntityBehaviour = false;
@RemovedConfig(name = "old-block-entity-behaviour", category = {"modify", "minecraft-old", "block-updater"}, transform = true)
@GlobalConfig("old-block-remove-behaviour")
public boolean oldBlockRemoveBehaviour = false;
}
@RemovedConfig(name = "shears-in-dispenser-can-zero-amount", category = {}, transform = true)
@@ -235,7 +244,7 @@ public final class LeavesConfig {
public boolean armorStandCantKillByMobProjectile = false;
@GlobalConfig(value = "villager-infinite-discounts", validator = VillagerInfiniteDiscountsValidator.class)
public boolean villagerInfiniteDiscounts = false;
private boolean villagerInfiniteDiscounts = false;
private static class VillagerInfiniteDiscountsValidator extends BooleanConfigValidator {
@Override
@@ -277,12 +286,18 @@ public final class LeavesConfig {
@GlobalConfig("disable-LivingEntity-ai-step-alive-check")
public boolean disableLivingEntityAiStepAliveCheck = false;
@GlobalConfig("fix-fortress-mob-spawn")
public boolean fixFortressMobSpawn = false;
@GlobalConfig("spawn-invulnerable-time")
public boolean spawnInvulnerableTime = false;
@GlobalConfig("old-hopper-suck-in-behavior")
public boolean oldHopperSuckInBehavior = false;
@GlobalConfig("old-nether-portal-collision") // Should remove in 1.21.6
public boolean oldNetherPortalCollision = false;
@GlobalConfig("old-zombie-piglin-drop")
public boolean oldZombiePiglinDrop = false;
public RaidConfig raid = new RaidConfig();
@GlobalConfigCategory("revert-raid-changes")
@@ -300,11 +315,27 @@ public final class LeavesConfig {
public boolean skipHeightCheck = false;
}
@GlobalConfig("old-zombie-reinforcement")
public boolean oldZombieReinforcement = false;
@GlobalConfig("allow-anvil-destroy-item-entities")
public boolean allowAnvilDestroyItemEntities = false;
@GlobalConfig("string-tripwire-hook-duplicate")
public boolean stringTripwireHookDuplicate = false;
public TripwireConfig tripwire = new TripwireConfig();
@GlobalConfigCategory("tripwire-and-hook-behavior")
public static class TripwireConfig {
@RemovedConfig(name = "string-tripwire-hook-duplicate", category = {"modify", "minecraft-old"}, transform = true)
@GlobalConfig("string-tripwire-hook-duplicate")
public boolean stringTripwireHookDuplicate = false;
@GlobalConfig("tripwire-behavior")
public TripwireBehavior tripwireBehavior = TripwireBehavior.VANILLA_21;
public enum TripwireBehavior {
VANILLA_20, VANILLA_21, MIXED
}
}
}
public ElytraAeronauticsConfig elytraAeronautics = new ElytraAeronauticsConfig();
@@ -312,7 +343,7 @@ public final class LeavesConfig {
@GlobalConfigCategory("elytra-aeronautics")
public static class ElytraAeronauticsConfig {
@GlobalConfig("no-chunk-load")
public boolean noChunk = false;
public boolean enableNoChunkLoad = false;
@GlobalConfig(value = "no-chunk-height")
public double noChunkHeight = 500.0D;
@@ -321,13 +352,13 @@ public final class LeavesConfig {
public double noChunkSpeed = -1.0D;
@GlobalConfig("message")
public boolean noChunkMes = true;
public boolean doSendMessages = true;
@GlobalConfig(value = "message-start")
public String noChunkStartMes = "Flight enter cruise mode";
public String startMessage = "Flight enter cruise mode";
@GlobalConfig(value = "message-end")
public String noChunkEndMes = "Flight exit cruise mode";
public String endMessage = "Flight exit cruise mode";
}
@RemovedConfig(name = "redstone-shears-wrench", category = {}, transform = true)
@@ -451,9 +482,6 @@ public final class LeavesConfig {
@GlobalConfig("shave-snow-layers")
public boolean shaveSnowLayers = true;
@GlobalConfig("ignore-lc")
public boolean ignoreLC = false;
@GlobalConfig("disable-packet-limit")
public boolean disablePacketLimit = false;
@@ -580,11 +608,33 @@ public final class LeavesConfig {
@GlobalConfig("disable-vault-blacklist")
public boolean disableVaultBlacklist = false;
@GlobalConfig(value = "exp-orb-absorb-mode", validator = ExpOrbModeValidator.class)
private ExpOrbAbsorbMode expOrbAbsorbMode = ExpOrbAbsorbMode.VANILLA;
public Predicate<ServerPlayer> fastAbsorbPredicate = player -> false;
public enum ExpOrbAbsorbMode {
VANILLA, FAST, FAST_CREATIVE
}
private static class ExpOrbModeValidator extends EnumConfigValidator<ExpOrbAbsorbMode> {
@Override
public void verify(ExpOrbAbsorbMode old, ExpOrbAbsorbMode value) throws IllegalArgumentException {
LeavesConfig.modify.fastAbsorbPredicate = switch (value) {
case FAST -> player -> true;
case VANILLA -> player -> false;
case FAST_CREATIVE -> Player::hasInfiniteMaterials;
};
}
}
@RemovedConfig(name = "tick-command", category = "modify")
@RemovedConfig(name = "player-can-edit-sign", category = "modify")
@RemovedConfig(name = "mending-compatibility-infinity", category = {"modify", "minecraft-old"})
@RemovedConfig(name = "protection-stacking", category = {"modify", "minecraft-old"})
@RemovedConfig(name = "disable-moved-wrongly-threshold", category = {"modify"})
@RemovedConfig(name = "disable-moved-wrongly-threshold", category = "modify")
@RemovedConfig(name = "ignore-lc", category = "modify")
@RemovedConfig(name = "fix-fortress-mob-spawn", category = {"modify", "minecraft-old"})
private final boolean removed = false;
}
@@ -947,7 +997,7 @@ public final class LeavesConfig {
public boolean loginProtect = false;
@GlobalConfig(value = "urls", lock = true, validator = ExtraYggdrasilUrlsValidator.class)
public List<String> serviceList = List.of("https://url.with.authlib-injector-yggdrasil");
private List<String> serviceList = List.of("https://url.with.authlib-injector-yggdrasil");
public static class ExtraYggdrasilUrlsValidator extends ListConfigValidator.STRING {
@Override
@@ -1022,9 +1072,6 @@ public final class LeavesConfig {
@GlobalConfig(value = "version", lock = true)
public org.leavesmc.leaves.region.linear.LinearVersion version = org.leavesmc.leaves.region.linear.LinearVersion.V2;
@GlobalConfig(value = "auto-convert-anvil-to-linear", lock = true)
public boolean autoConvertAnvilToLinear = false;
@GlobalConfig(value = "flush-max-threads", lock = true)
public int flushThreads = 6;
@@ -1056,7 +1103,8 @@ public final class LeavesConfig {
@RemovedConfig(name = "flush-frequency", category = {"region", "linear"})
@RemovedConfig(name = "crash-on-broken-symlink", category = {"region", "linear"})
private final boolean linearCrashOnBrokenSymlink = true;
@RemovedConfig(name = "auto-convert-anvil-to-linear", category = {"region", "linear"})
private final boolean removed = true;
}
}
@@ -1070,6 +1118,16 @@ public final class LeavesConfig {
@GlobalConfig("vanilla-display-name")
public boolean vanillaDisplayName = false;
@GlobalConfig(value = "collision-behavior")
public CollisionBehavior collisionBehavior = CollisionBehavior.BLOCK_SHAPE_VANILLA;
public enum CollisionBehavior {
VANILLA, BLOCK_SHAPE_VANILLA, PAPER
}
@GlobalConfig("vanilla-portal-handle")
public boolean vanillaPortalHandle = false;
@RemovedConfig(name = "spigot-EndPlatform-destroy", category = "fix")
private final boolean spigotEndPlatformDestroy = false;
}

View File

@@ -1,5 +1,7 @@
package org.leavesmc.leaves.bot;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import it.unimi.dsi.fastutil.Pair;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@@ -23,13 +25,24 @@ import org.leavesmc.leaves.bot.subcommands.BotRemoveCommand;
import org.leavesmc.leaves.bot.subcommands.BotSaveCommand;
import org.leavesmc.leaves.command.LeavesCommandUtil;
import org.leavesmc.leaves.command.LeavesSubcommand;
import org.leavesmc.leaves.command.LeavesSuggestionCommand;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static net.kyori.adventure.text.Component.text;
public class BotCommand extends Command {
//TODO rewrite
public class BotCommand extends Command implements LeavesSuggestionCommand {
public BotCommand(String name) {
super(name);
@@ -74,6 +87,17 @@ public class BotCommand extends Command {
return Collections.emptyList();
}
@Override
public @Nullable CompletableFuture<Suggestions> tabSuggestion(@NotNull CommandSender sender, @NotNull String alias, @NotNull String @NotNull [] args, @Nullable Location location, @NotNull SuggestionsBuilder builder) throws IllegalArgumentException {
if (args.length > 1) {
final @Nullable Pair<String, LeavesSubcommand> subCommand = resolveCommand(args[0]);
if (subCommand != null) {
return subCommand.second().tabSuggestion(sender, subCommand.first(), Arrays.copyOfRange(args, 1, args.length), location, builder);
}
}
return null;
}
@Override
public boolean execute(final @NotNull CommandSender sender, final @NotNull String commandLabel, final String @NotNull [] args) {
if (!testPermission(sender) || !LeavesConfig.modify.fakeplayer.enable) return true;

View File

@@ -1,6 +1,7 @@
package org.leavesmc.leaves.bot;
import com.mojang.logging.LogUtils;
import net.minecraft.core.UUIDUtil;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtAccounter;
import net.minecraft.nbt.NbtIo;
@@ -64,7 +65,7 @@ public class BotDataStorage implements IPlayerDataStorage {
if (flag && player instanceof ServerBot bot) {
CompoundTag nbt = new CompoundTag();
nbt.putString("name", bot.createState.name());
nbt.putUUID("uuid", bot.getUUID());
nbt.store("uuid", UUIDUtil.CODEC, bot.getUUID());
nbt.putBoolean("resume", bot.resume);
this.savedBotList.put(bot.createState.realName(), nbt);
this.saveBotList();

View File

@@ -1,157 +1,114 @@
package org.leavesmc.leaves.bot;
import com.google.common.collect.ImmutableList;
import com.mojang.datafixers.util.Pair;
import net.minecraft.core.NonNullList;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.CustomData;
import javax.annotation.Nonnull;
import java.util.List;
// Power by gugle-carpet-addition(https://github.com/Gu-ZT/gugle-carpet-addition)
public class BotInventoryContainer extends SimpleContainer {
public class BotInventoryContainer extends Inventory {
public final NonNullList<ItemStack> items;
public final NonNullList<ItemStack> armor;
public final NonNullList<ItemStack> offhand;
private final List<NonNullList<ItemStack>> compartments;
private final NonNullList<ItemStack> buttons = NonNullList.withSize(13, ItemStack.EMPTY);
private final ServerBot player;
private static final ItemStack button;
public BotInventoryContainer(ServerBot player) {
this.player = player;
this.items = this.player.getInventory().items;
this.armor = this.player.getInventory().armor;
this.offhand = this.player.getInventory().offhand;
this.compartments = ImmutableList.of(this.items, this.armor, this.offhand, this.buttons);
createButton();
static {
CompoundTag customData = new CompoundTag();
customData.putBoolean("Leaves.Gui.Placeholder", true);
DataComponentPatch patch = DataComponentPatch.builder()
.set(DataComponents.CUSTOM_NAME, Component.empty())
.set(DataComponents.CUSTOM_DATA, CustomData.of(customData))
.build();
button = new ItemStack(Items.STRUCTURE_VOID);
button.applyComponents(patch);
}
private final Inventory original;
public BotInventoryContainer(Inventory original) {
super(original.player, original.equipment);
this.original = original;
}
@Override
public int getContainerSize() {
return this.items.size() + this.armor.size() + this.offhand.size() + this.buttons.size();
}
@Override
public boolean isEmpty() {
for (ItemStack itemStack : this.items) {
if (itemStack.isEmpty()) {
continue;
}
return false;
}
for (ItemStack itemStack : this.armor) {
if (itemStack.isEmpty()) {
continue;
}
return false;
}
for (ItemStack itemStack : this.offhand) {
if (itemStack.isEmpty()) {
continue;
}
return false;
}
return true;
return 54;
}
@Override
@Nonnull
public ItemStack getItem(int slot) {
Pair<NonNullList<ItemStack>, Integer> pair = getItemSlot(slot);
if (pair != null) {
return pair.getFirst().get(pair.getSecond());
} else {
return ItemStack.EMPTY;
int realSlot = convertSlot(slot);
if (realSlot == -999) {
// buttons are the same
return button;
}
return original.getItem(realSlot);
}
public Pair<NonNullList<ItemStack>, Integer> getItemSlot(int slot) {
switch (slot) {
case 0 -> {
return new Pair<>(buttons, 0);
}
case 1, 2, 3, 4 -> {
return new Pair<>(armor, 4 - slot);
}
case 5, 6 -> {
return new Pair<>(buttons, slot - 4);
}
case 7 -> {
return new Pair<>(offhand, 0);
}
case 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 -> {
return new Pair<>(buttons, slot - 5);
}
public int convertSlot(int slot) {
return switch (slot) {
// Mainhand is always store at slot 0
case 6 -> 0;
// Offhand
case 7 -> 40;
// Equipment slot start at 36
case 1, 2, 3, 4 -> 40 - slot;
// Inventory storage
case 18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44 -> {
return new Pair<>(items, slot - 9);
}
case 45, 46, 47, 48, 49, 50, 51, 52, 53 -> {
return new Pair<>(items, slot - 45);
}
default -> {
return null;
}
}
36, 37, 38, 39, 40, 41, 42, 43, 44 -> slot - 9;
// Hotbar, 45 -> Mainhand (0)
case 45, 46, 47, 48, 49, 50, 51, 52, 53 -> slot - 45;
// Buttons
default -> -999;
};
}
@Override
@Nonnull
public ItemStack removeItem(int slot, int amount) {
Pair<NonNullList<ItemStack>, Integer> pair = getItemSlot(slot);
NonNullList<ItemStack> list = null;
ItemStack itemStack = ItemStack.EMPTY;
if (pair != null) {
list = pair.getFirst();
slot = pair.getSecond();
int realSlot = convertSlot(slot);
if (realSlot == -999) {
// Don't remove buttons
return ItemStack.EMPTY;
}
if (list != null && !list.get(slot).isEmpty()) {
itemStack = ContainerHelper.removeItem(list, slot, amount);
player.detectEquipmentUpdatesPublic();
}
return itemStack;
ItemStack removed = original.removeItem(realSlot, amount);
player.detectEquipmentUpdates();
return removed;
}
@Override
@Nonnull
public ItemStack removeItemNoUpdate(int slot) {
Pair<NonNullList<ItemStack>, Integer> pair = getItemSlot(slot);
NonNullList<ItemStack> list = null;
if (pair != null) {
list = pair.getFirst();
slot = pair.getSecond();
int realSlot = convertSlot(slot);
if (realSlot == -999) {
// Don't remove buttons
return ItemStack.EMPTY;
}
if (list != null && !list.get(slot).isEmpty()) {
ItemStack itemStack = list.get(slot);
list.set(slot, ItemStack.EMPTY);
return itemStack;
}
return ItemStack.EMPTY;
return original.removeItemNoUpdate(realSlot);
}
@Override
public void setItem(int slot, @Nonnull ItemStack stack) {
Pair<NonNullList<ItemStack>, Integer> pair = getItemSlot(slot);
NonNullList<ItemStack> list = null;
if (pair != null) {
list = pair.getFirst();
slot = pair.getSecond();
}
if (list != null) {
list.set(slot, stack);
player.detectEquipmentUpdatesPublic();
int realSlot = convertSlot(slot);
if (realSlot == -999) {
// Don't modify buttons
return;
}
original.setItem(realSlot, stack);
player.detectEquipmentUpdates();
}
@Override
@@ -165,27 +122,4 @@ public class BotInventoryContainer extends SimpleContainer {
}
return !(player.distanceToSqr(this.player) > 64.0);
}
@Override
public void clearContent() {
for (List<ItemStack> list : this.compartments) {
list.clear();
}
}
private void createButton() {
CompoundTag customData = new CompoundTag();
customData.putBoolean("Leaves.Gui.Placeholder", true);
DataComponentPatch patch = DataComponentPatch.builder()
.set(DataComponents.CUSTOM_NAME, Component.empty())
.set(DataComponents.CUSTOM_DATA, CustomData.of(customData))
.build();
for (int i = 0; i < 13; i++) {
ItemStack button = new ItemStack(Items.STRUCTURE_VOID);
button.applyComponents(patch);
buttons.set(i, button);
}
}
}
}

View File

@@ -23,7 +23,6 @@ import org.bukkit.craftbukkit.CraftWorld;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.LeavesConfig;
import org.leavesmc.leaves.bot.agent.Configs;
import org.leavesmc.leaves.event.bot.BotCreateEvent;
import org.leavesmc.leaves.event.bot.BotJoinEvent;
import org.leavesmc.leaves.event.bot.BotLoadEvent;
@@ -60,7 +59,7 @@ public class BotList {
public ServerBot createNewBot(BotCreateState state) {
BotCreateEvent event = new BotCreateEvent(state.name(), state.skinName(), state.location(), state.createReason(), state.creator());
event.setCancelled(!isCreateLegal(state.name()));
event.setCancelled(!BotUtil.isCreateLegal(state.name()));
this.server.server.getPluginManager().callEvent(event);
if (event.isCancelled()) {
@@ -100,10 +99,11 @@ public class BotList {
if (optional.isEmpty()) {
return null;
}
CompoundTag nbt = optional.get();
ResourceKey<Level> resourcekey = null;
if (optional.get().contains("WorldUUIDMost") && optional.get().contains("WorldUUIDLeast")) {
org.bukkit.World bWorld = Bukkit.getServer().getWorld(new UUID(optional.get().getLong("WorldUUIDMost"), optional.get().getLong("WorldUUIDLeast")));
if (nbt.contains("WorldUUIDMost") && nbt.contains("WorldUUIDLeast")) {
org.bukkit.World bWorld = Bukkit.getServer().getWorld(new UUID(nbt.getLong("WorldUUIDMost").orElseThrow(), nbt.getLong("WorldUUIDLeast").orElseThrow()));
if (bWorld != null) {
resourcekey = ((CraftWorld) bWorld).getHandle().dimension();
}
@@ -113,11 +113,11 @@ public class BotList {
}
ServerLevel world = this.server.getLevel(resourcekey);
return this.placeNewBot(bot, world, bot.getLocation(), optional.get());
return this.placeNewBot(bot, world, bot.getLocation(), nbt);
}
public ServerBot placeNewBot(ServerBot bot, ServerLevel world, Location location, @Nullable CompoundTag nbt) {
Optional<CompoundTag> optional = Optional.ofNullable(nbt);
public ServerBot placeNewBot(@NotNull ServerBot bot, ServerLevel world, Location location, @Nullable CompoundTag save) {
Optional<CompoundTag> optional = Optional.ofNullable(save);
bot.isRealPlayer = true;
bot.loginTime = System.currentTimeMillis();
@@ -142,8 +142,10 @@ public class BotList {
bot.supressTrackerForLogin = true;
world.addNewPlayer(bot);
bot.loadAndSpawnEnderpearls(optional);
bot.loadAndSpawnParentVehicle(optional);
optional.ifPresent(nbt -> {
bot.loadAndSpawnEnderPearls(nbt);
bot.loadAndSpawnParentVehicle(nbt);
});
BotJoinEvent event1 = new BotJoinEvent(bot.getBukkitEntity(), PaperAdventure.asAdventure(Component.translatable("multiplayer.player.joined", bot.getDisplayName())).style(Style.style(NamedTextColor.YELLOW)));
this.server.server.getPluginManager().callEvent(event1);
@@ -178,17 +180,13 @@ public class BotList {
bot.removeTaskId = -1;
}
if (this.server.isSameThread()) {
bot.doTick();
}
if (event.shouldSave()) {
playerIO.save(bot);
} else {
bot.dropAll();
bot.dropAll(true);
}
if (bot.isPassenger()) {
if (bot.isPassenger() && event.shouldSave()) {
Entity entity = bot.getRootVehicle();
if (entity.hasExactlyOnePlayerPassenger()) {
bot.stopRiding();
@@ -217,7 +215,7 @@ public class BotList {
bot.removeTab();
for (ServerPlayer player : bot.serverLevel().players()) {
if (!(player instanceof ServerBot) && !bot.needSendFakeData(player)) {
if (!(player instanceof ServerBot)) {
player.connection.send(new ClientboundRemoveEntitiesPacket(bot.getId()));
}
}
@@ -239,9 +237,9 @@ public class BotList {
public void loadResume() {
if (LeavesConfig.modify.fakeplayer.enable && LeavesConfig.modify.fakeplayer.canResident) {
CompoundTag savedBotList = this.getSavedBotList().copy();
for (String realName : savedBotList.getAllKeys()) {
CompoundTag nbt = savedBotList.getCompound(realName);
if (nbt.getBoolean("resume")) {
for (String realName : savedBotList.keySet()) {
CompoundTag nbt = savedBotList.getCompound(realName).orElseThrow();
if (nbt.getBoolean("resume").orElse(false)) {
this.loadNewBot(realName);
}
}
@@ -249,7 +247,7 @@ public class BotList {
}
public void networkTick() {
this.bots.stream().filter(bot -> bot.getConfigValue(Configs.TICK_TYPE) == ServerBot.TickType.NETWORK).forEach(ServerBot::doTick); // TODO perf?
this.bots.forEach(ServerBot::networkTick);
}
@Nullable
@@ -266,22 +264,6 @@ public class BotList {
return this.dataStorage.getSavedBotList();
}
public boolean isCreateLegal(@NotNull String name) {
if (!name.matches("^[a-zA-Z0-9_]{4,16}$")) {
return false;
}
if (Bukkit.getPlayerExact(name) != null || this.getBotByName(name) != null) {
return false;
}
if (LeavesConfig.modify.fakeplayer.unableNames.contains(name)) {
return false;
}
return this.bots.size() < LeavesConfig.modify.fakeplayer.limit;
}
public static class CustomGameProfile extends GameProfile {
public CustomGameProfile(UUID uuid, String name, String[] skin) {

View File

@@ -4,7 +4,9 @@ import com.google.common.base.Charsets;
import net.minecraft.core.NonNullList;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.ItemStack;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.LeavesConfig;
import java.util.UUID;
@@ -70,4 +72,24 @@ public class BotUtil {
public static UUID getBotUUID(@NotNull String realName) {
return UUID.nameUUIDFromBytes(("Fakeplayer:" + realName).getBytes(Charsets.UTF_8));
}
public static String getFullName(String inputName) {
return LeavesConfig.modify.fakeplayer.prefix + inputName + LeavesConfig.modify.fakeplayer.suffix;
}
public static boolean isCreateLegal(@NotNull String name) {
if (!name.matches("^[a-zA-Z0-9_]{4,16}$")) {
return false;
}
if (Bukkit.getPlayerExact(name) != null || BotList.INSTANCE.getBotByName(name) != null) {
return false;
}
if (LeavesConfig.modify.fakeplayer.unableNames.contains(name)) {
return false;
}
return BotList.INSTANCE.bots.size() < LeavesConfig.modify.fakeplayer.limit;
}
}

View File

@@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMap;
import com.mojang.authlib.GameProfile;
import io.papermc.paper.adventure.PaperAdventure;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.core.particles.BlockParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
@@ -14,6 +15,7 @@ import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ClientInformation;
@@ -35,6 +37,7 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.portal.TeleportTransition;
@@ -99,7 +102,7 @@ public class ServerBot extends ServerPlayer {
this.configs = configBuilder.build();
this.stats = new BotStatsCounter(server);
this.container = new BotInventoryContainer(this);
this.container = new BotInventoryContainer(this.getInventory());
this.tracingRange = world.spigotConfig.playerTrackingRange * world.spigotConfig.playerTrackingRange;
this.notSleepTicks = 0;
@@ -130,7 +133,7 @@ public class ServerBot extends ServerPlayer {
// copy ServerPlayer end
if (this.getConfigValue(Configs.SPAWN_PHANTOM)) {
notSleepTicks++;
this.notSleepTicks++;
}
if (LeavesConfig.modify.fakeplayer.regenAmount > 0.0 && server.getTickCount() % 20 == 0) {
@@ -155,7 +158,12 @@ public class ServerBot extends ServerPlayer {
@Override
public void doTick() {
this.absMoveTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
if (!this.isAlive()) {
this.die(this.damageSources().generic());
return;
}
this.absSnapTo(this.getX(), this.getY(), this.getZ(), this.getYRot(), this.getXRot());
if (this.isPassenger()) {
this.setOnGround(false);
@@ -172,7 +180,7 @@ public class ServerBot extends ServerPlayer {
this.notSleepTicks = 0;
}
if (!this.level().isClientSide && this.level().isDay()) {
if (!this.level().isClientSide && this.level().isBrightOutside()) {
this.stopSleepInBed(false, true);
}
} else if (this.sleepCounter > 0) {
@@ -212,27 +220,56 @@ public class ServerBot extends ServerPlayer {
this.updatePlayerPose();
}
@Override
public @Nullable ServerBot teleport(@NotNull TeleportTransition teleportTarget) {
if (this.isSleeping() || this.isRemoved()) {
return null;
}
if (teleportTarget.newLevel().dimension() != this.serverLevel().dimension()) {
return null;
} else {
if (!teleportTarget.asPassenger()) {
this.stopRiding();
}
this.connection.internalTeleport(PositionMoveRotation.of(teleportTarget), teleportTarget.relatives());
this.connection.resetPosition();
teleportTarget.postTeleportTransition().onTransition(this);
return this;
public void networkTick() {
if (this.getConfigValue(Configs.TICK_TYPE) == TickType.NETWORK) {
this.doTick();
}
}
@Override
public void handlePortal() {
public @Nullable ServerBot teleport(@NotNull TeleportTransition teleportTransition) {
if (this.isSleeping() || this.isRemoved()) {
return null;
}
if (!teleportTransition.asPassenger()) {
this.removeVehicle();
}
ServerLevel fromLevel = this.serverLevel();
ServerLevel toLevel = teleportTransition.newLevel();
if (toLevel.dimension() == fromLevel.dimension()) {
this.teleportSetPosition(PositionMoveRotation.of(teleportTransition), teleportTransition.relatives());
teleportTransition.postTeleportTransition().onTransition(this);
return this;
} else {
this.isChangingDimension = true;
fromLevel.removePlayerImmediately(this, Entity.RemovalReason.CHANGED_DIMENSION);
this.unsetRemoved();
this.setServerLevel(toLevel);
this.teleportSetPosition(PositionMoveRotation.of(teleportTransition), teleportTransition.relatives());
toLevel.addDuringTeleport(this);
this.stopUsingItem();
teleportTransition.postTeleportTransition().onTransition(this);
this.isChangingDimension = false;
if (org.leavesmc.leaves.LeavesConfig.modify.netherPortalFix) {
final ResourceKey<Level> fromDim = fromLevel.dimension();
final ResourceKey<Level> toDim = level().dimension();
if (!((fromDim != Level.OVERWORLD || toDim != Level.NETHER) && (fromDim != Level.NETHER || toDim != Level.OVERWORLD))) {
BlockPos fromPortal = org.leavesmc.leaves.util.ReturnPortalManager.findPortalAt(this, fromDim, lastPos);
BlockPos toPos = this.blockPosition();
if (fromPortal != null) {
org.leavesmc.leaves.util.ReturnPortalManager.storeReturnPortal(this, toDim, toPos, fromPortal);
}
}
}
if (this.isBlocking()) {
this.stopUsingItem();
}
}
return this;
}
@Override
@@ -245,12 +282,12 @@ public class ServerBot extends ServerPlayer {
ItemStack item = this.getItemInHand(hand);
if (!item.isEmpty()) {
BotUtil.replenishment(item, getInventory().items);
BotUtil.replenishment(item, getInventory().getNonEquipmentItems());
if (BotUtil.isDamage(item, 10)) {
BotUtil.replaceTool(hand == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND, this);
}
}
this.detectEquipmentUpdatesPublic();
this.detectEquipmentUpdates();
}
@Override
@@ -271,6 +308,9 @@ public class ServerBot extends ServerPlayer {
@Override
public void checkFallDamage(double y, boolean onGround, @NotNull BlockState state, @NotNull BlockPos pos) {
ServerLevel serverLevel = this.serverLevel();
if (!this.isInWater() && y < 0.0) {
this.fallDistance -= (float) y;
}
if (onGround && this.fallDistance > 0.0F) {
this.onChangedBlock(serverLevel, pos);
double attributeValue = this.getAttributeValue(Attributes.SAFE_FALL_DISTANCE);
@@ -354,21 +394,21 @@ public class ServerBot extends ServerPlayer {
@Override
public void readAdditionalSaveData(@NotNull CompoundTag nbt) {
super.readAdditionalSaveData(nbt);
this.setShiftKeyDown(nbt.getBoolean("isShiftKeyDown"));
this.setShiftKeyDown(nbt.getBoolean("isShiftKeyDown").orElse(false));
CompoundTag createNbt = nbt.getCompound("createStatus");
BotCreateState.Builder createBuilder = BotCreateState.builder(createNbt.getString("realName"), null).name(createNbt.getString("name"));
CompoundTag createNbt = nbt.getCompound("createStatus").orElseThrow();
BotCreateState.Builder createBuilder = BotCreateState.builder(createNbt.getString("realName").orElseThrow(), null).name(createNbt.getString("name").orElseThrow());
String[] skin = null;
if (createNbt.contains("skin")) {
ListTag skinTag = createNbt.getList("skin", 8);
ListTag skinTag = createNbt.getList("skin").orElseThrow();
skin = new String[skinTag.size()];
for (int i = 0; i < skinTag.size(); i++) {
skin[i] = skinTag.getString(i);
skin[i] = skinTag.getString(i).orElseThrow();
}
}
createBuilder.skinName(createNbt.getString("skinName")).skin(skin);
createBuilder.skinName(createNbt.getString("skinName").orElseThrow()).skin(skin);
createBuilder.createReason(BotCreateEvent.CreateReason.INTERNAL).creator(null);
this.createState = createBuilder.build();
@@ -376,10 +416,10 @@ public class ServerBot extends ServerPlayer {
if (nbt.contains("actions")) {
ListTag actionNbt = nbt.getList("actions", 10);
ListTag actionNbt = nbt.getList("actions").orElseThrow();
for (int i = 0; i < actionNbt.size(); i++) {
CompoundTag actionTag = actionNbt.getCompound(i);
AbstractBotAction<?> action = Actions.getForName(actionTag.getString("actionName"));
CompoundTag actionTag = actionNbt.getCompound(i).orElseThrow();
AbstractBotAction<?> action = Actions.getForName(actionTag.getString("actionName").orElseThrow());
if (action != null) {
AbstractBotAction<?> newAction = action.create();
newAction.load(actionTag);
@@ -389,10 +429,10 @@ public class ServerBot extends ServerPlayer {
}
if (nbt.contains("configs")) {
ListTag configNbt = nbt.getList("configs", 10);
ListTag configNbt = nbt.getList("configs").orElseThrow();
for (int i = 0; i < configNbt.size(); i++) {
CompoundTag configTag = configNbt.getCompound(i);
Configs<?> configKey = Configs.getConfig(configTag.getString("configName"));
CompoundTag configTag = configNbt.getCompound(i).orElseThrow();
Configs<?> configKey = Configs.getConfig(configTag.getString("configName").orElseThrow());
if (configKey != null) {
this.configs.get(configKey).load(configTag);
}
@@ -400,6 +440,11 @@ public class ServerBot extends ServerPlayer {
}
}
@Override
public boolean isClientAuthoritative() {
return false;
}
public void sendPlayerInfo(ServerPlayer player) {
player.connection.send(new ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME), List.of(this)));
}
@@ -510,9 +555,23 @@ public class ServerBot extends ServerPlayer {
return null;
}
public void dropAll() {
this.getInventory().dropAll();
this.detectEquipmentUpdatesPublic();
public void dropAll(boolean death) {
NonNullList<ItemStack> items = this.getInventory().getNonEquipmentItems();
for (int i = 0; i < items.size(); i++) {
ItemStack itemStack = items.get(i);
if (!itemStack.isEmpty()) {
this.drop(itemStack, death, false);
items.set(i, ItemStack.EMPTY);
}
}
for (EquipmentSlot slot : EquipmentSlot.values()) {
ItemStack itemStack;
if (!(itemStack = this.equipment.get(slot)).isEmpty()) {
this.drop(itemStack, death, false);
this.equipment.set(slot, ItemStack.EMPTY);
}
}
this.detectEquipmentUpdates();
}
private void runAction() {

View File

@@ -1,7 +1,10 @@
package org.leavesmc.leaves.bot.agent;
import net.minecraft.core.UUIDUtil;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.bot.ServerBot;
@@ -12,21 +15,24 @@ import org.leavesmc.leaves.event.bot.BotActionStopEvent;
import java.util.List;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.function.Supplier;
//TODO onStop for fully terminate action (use, etc.)
public abstract class AbstractBotAction<E extends AbstractBotAction<E>> {
private final String name;
private final CommandArgument argument;
private final Supplier<E> creator;
private boolean cancel;
private int tickDelay;
private int number;
private UUID uuid;
private int needWaitTick;
private int canDoNumber;
private int initialTickDelay;
private int initialTickInterval;
private int initialNumber;
private int tickToNext;
private int numberRemaining;
private boolean cancel;
public AbstractBotAction(String name, CommandArgument argument, Supplier<E> creator) {
this.name = name;
@@ -35,16 +41,98 @@ public abstract class AbstractBotAction<E extends AbstractBotAction<E>> {
this.creator = creator;
this.cancel = false;
this.tickDelay = 20;
this.number = -1;
this.initialTickInterval = 20;
this.initialNumber = -1;
}
public void init() {
this.needWaitTick = 0;
this.canDoNumber = this.getNumber();
this.tickToNext = initialTickDelay;
this.numberRemaining = this.getInitialNumber();
this.setCancelled(false);
}
public void tryTick(ServerBot bot) {
if (this.numberRemaining == 0) {
this.stop(bot, BotActionStopEvent.Reason.DONE);
return;
}
if (this.tickToNext <= 0) {
BotActionExecuteEvent event = new BotActionExecuteEvent(bot.getBukkitEntity(), name, uuid);
event.callEvent();
if (event.getResult() == BotActionExecuteEvent.Result.SOFT_CANCEL) {
this.tickToNext = this.getInitialTickInterval() - 1;
return;
} else if (event.getResult() == BotActionExecuteEvent.Result.HARD_CANCEL) {
if (this.numberRemaining > 0) {
this.numberRemaining--;
}
this.tickToNext = this.getInitialTickInterval() - 1;
return;
}
if (this.doTick(bot)) {
if (this.numberRemaining > 0) {
this.numberRemaining--;
}
this.tickToNext = this.getInitialTickInterval() - 1;
}
} else {
this.tickToNext--;
}
}
@NotNull
public CompoundTag save(@NotNull CompoundTag nbt) {
if (!this.cancel) {
nbt.putString("actionName", this.name);
nbt.store("actionUUID", UUIDUtil.CODEC, this.uuid);
nbt.putInt("initialTickDelay", this.initialTickDelay);
nbt.putInt("initialTickInterval", this.initialTickInterval);
nbt.putInt("initialNumber", this.initialNumber);
nbt.putInt("tickToNext", this.tickToNext);
nbt.putInt("numberRemaining", this.numberRemaining);
}
return nbt;
}
public void load(@NotNull CompoundTag nbt) {
this.uuid = nbt.read("actionUUID", UUIDUtil.CODEC).orElse(UUID.randomUUID());
this.initialTickDelay = nbt.getInt("initialTickDelay").orElse(0);
this.initialTickInterval = nbt.getInt("initialTickInterval").orElse(0);
this.initialNumber = nbt.getInt("initialNumber").orElse(0);
this.tickToNext = nbt.getInt("tickToNext").orElse(0);
this.numberRemaining = nbt.getInt("numberRemaining").orElse(0);
}
public void stop(@NotNull ServerBot bot, BotActionStopEvent.Reason reason) {
new BotActionStopEvent(bot.getBukkitEntity(), this.name, this.uuid, reason, null).callEvent();
this.setCancelled(true);
}
public abstract void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result);
public abstract boolean doTick(@NotNull ServerBot bot);
@SuppressWarnings("unchecked")
public E setSuggestion(int n, BiFunction<CommandSender, String, Pair<List<String>, String>> suggestion) {
this.argument.setSuggestion(n, suggestion);
return (E) this;
}
public E setSuggestion(int n, Pair<List<String>, String> suggestion) {
return this.setSuggestion(n, (sender, arg) -> suggestion);
}
public E setSuggestion(int n, List<String> tabComplete) {
return this.setSuggestion(n, Pair.of(tabComplete, null));
}
public String getName() {
return this.name;
}
@@ -53,28 +141,42 @@ public abstract class AbstractBotAction<E extends AbstractBotAction<E>> {
return uuid;
}
public int getTickDelay() {
return this.tickDelay;
}
@SuppressWarnings("unchecked")
public E setTickDelay(int tickDelay) {
this.tickDelay = Math.max(0, tickDelay);
public E setInitialTickDelay(int initialTickDelay) {
this.initialTickDelay = initialTickDelay;
return (E) this;
}
public int getNumber() {
return this.number;
public int getInitialTickDelay() {
return this.initialTickDelay;
}
public int getInitialTickInterval() {
return this.initialTickInterval;
}
@SuppressWarnings("unchecked")
public E setNumber(int number) {
this.number = Math.max(-1, number);
public E setInitialTickInterval(int initialTickInterval) {
this.initialTickInterval = Math.max(1, initialTickInterval);
return (E) this;
}
public int getCanDoNumber() {
return this.canDoNumber;
public int getInitialNumber() {
return this.initialNumber;
}
@SuppressWarnings("unchecked")
public E setInitialNumber(int initialNumber) {
this.initialNumber = Math.max(-1, initialNumber);
return (E) this;
}
public int getTickToNext() {
return this.tickToNext;
}
public int getNumberRemaining() {
return this.numberRemaining;
}
public boolean isCancelled() {
@@ -85,79 +187,12 @@ public abstract class AbstractBotAction<E extends AbstractBotAction<E>> {
this.cancel = cancel;
}
public void stop(@NotNull ServerBot bot, BotActionStopEvent.Reason reason) {
new BotActionStopEvent(bot.getBukkitEntity(), this.name, this.uuid, reason, null).callEvent();
this.setCancelled(true);
}
public CommandArgument getArgument() {
return this.argument;
}
@SuppressWarnings("unchecked")
public E setTabComplete(int index, List<String> list) {
this.argument.setTabComplete(index, list);
return (E) this;
}
public void tryTick(ServerBot bot) {
if (this.canDoNumber == 0) {
this.stop(bot, BotActionStopEvent.Reason.DONE);
return;
}
if (this.needWaitTick <= 0) {
BotActionExecuteEvent event = new BotActionExecuteEvent(bot.getBukkitEntity(), name, uuid);
event.callEvent();
if (event.getResult() == BotActionExecuteEvent.Result.SOFT_CANCEL) {
this.needWaitTick = this.getTickDelay();
return;
} else if (event.getResult() == BotActionExecuteEvent.Result.HARD_CANCEL) {
if (this.canDoNumber > 0) {
this.canDoNumber--;
}
this.needWaitTick = this.getTickDelay();
return;
}
if (this.doTick(bot)) {
if (this.canDoNumber > 0) {
this.canDoNumber--;
}
this.needWaitTick = this.getTickDelay();
}
} else {
this.needWaitTick--;
}
}
@NotNull
public E create() {
return this.creator.get();
}
@NotNull
public CompoundTag save(@NotNull CompoundTag nbt) {
if (!this.cancel) {
nbt.putString("actionName", this.name);
nbt.putUUID("actionUUID", this.uuid);
nbt.putInt("canDoNumber", this.canDoNumber);
nbt.putInt("needWaitTick", this.needWaitTick);
nbt.putInt("tickDelay", this.tickDelay);
}
return nbt;
}
public void load(@NotNull CompoundTag nbt) {
this.tickDelay = nbt.getInt("tickDelay");
this.needWaitTick = nbt.getInt("needWaitTick");
this.canDoNumber = nbt.getInt("canDoNumber");
this.uuid = nbt.getUUID("actionUUID");
}
public abstract void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result);
public abstract boolean doTick(@NotNull ServerBot bot);
}

View File

@@ -31,6 +31,7 @@ public class Actions {
register(new UseItemOnOffhandAction());
register(new UseItemToOffhandAction());
register(new RotationAction());
register(new ShootAction());
}
public static boolean register(@NotNull AbstractBotAction<?> action) {

View File

@@ -1,6 +1,7 @@
package org.leavesmc.leaves.bot.agent.actions;
import net.minecraft.server.level.ServerPlayer;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.bot.agent.AbstractBotAction;
@@ -8,18 +9,25 @@ import org.leavesmc.leaves.command.CommandArgument;
import org.leavesmc.leaves.command.CommandArgumentResult;
import org.leavesmc.leaves.command.CommandArgumentType;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
public abstract class AbstractTimerAction<E extends AbstractTimerAction<E>> extends AbstractBotAction<E> {
public AbstractTimerAction(String name, Supplier<E> creator) {
super(name, CommandArgument.of(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER), creator);
this.setTabComplete(0, List.of("[TickDelay]")).setTabComplete(1, List.of("[DoNumber]"));
this(name, CommandArgument.of(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER, CommandArgumentType.INTEGER), creator);
}
public AbstractTimerAction(String name, CommandArgument argument, Supplier<E> creator) {
super(name, argument, creator);
this.setSuggestion(0, Pair.of(Collections.singletonList("0"), "[TickDelay]"));
this.setSuggestion(1, Pair.of(Collections.singletonList("20"), "[TickInterval]"));
this.setSuggestion(2, Pair.of(List.of("1", "-1"), "[DoNumber]"));
}
@Override
public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) {
this.setTickDelay(result.readInt(20)).setNumber(result.readInt(-1));
this.setInitialTickDelay(result.readInt(0)).setInitialTickInterval(result.readInt(20)).setInitialNumber(result.readInt(1));
}
}

View File

@@ -13,7 +13,7 @@ public class CraftBotAction extends LeavesBotAction {
private final AbstractBotAction<?> handle;
public CraftBotAction(@NotNull AbstractBotAction<?> action) {
super(BotActionType.valueOf(action.getName()), action.getTickDelay(), action.getCanDoNumber());
super(BotActionType.valueOf(action.getName()), action.getInitialTickInterval(), action.getNumberRemaining());
this.handle = action;
}
@@ -31,7 +31,7 @@ public class CraftBotAction extends LeavesBotAction {
}
AbstractBotAction<?> newAction = null;
String[] args = new String[]{String.valueOf(action.getExecuteInterval()), String.valueOf(action.getRemainingExecuteTime())};
String[] args = new String[]{String.valueOf(action.getInitialTickDelay()), String.valueOf(action.getInitialTickInterval()), String.valueOf(action.getInitialNumber())};
try {
if (act instanceof CraftCustomBotAction customBotAction) {
newAction = customBotAction.createCraft(action.getActionPlayer(), args);

View File

@@ -15,7 +15,7 @@ public class CraftCustomBotAction extends AbstractBotAction<CraftCustomBotAction
private final CustomBotAction realAction;
public CraftCustomBotAction(String name, @NotNull CustomBotAction realAction) {
super(name, CommandArgument.of().setAllTabComplete(realAction.getTabComplete()), null);
super(name, CommandArgument.EMPTY, null);
this.realAction = realAction;
}
@@ -33,13 +33,18 @@ public class CraftCustomBotAction extends AbstractBotAction<CraftCustomBotAction
}
@Override
public int getNumber() {
return realAction.getNumber();
public int getInitialNumber() {
return realAction.getInitialNumber();
}
@Override
public int getTickDelay() {
return realAction.getTickDelay();
public int getInitialTickDelay() {
return realAction.getInitialTickDelay();
}
@Override
public int getInitialTickInterval() {
return realAction.getInitialTickInterval();
}
@Override

View File

@@ -1,10 +1,7 @@
package org.leavesmc.leaves.bot.agent.actions;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.command.CommandArgumentResult;
public class DropAction extends AbstractTimerAction<DropAction> {
@@ -12,14 +9,9 @@ public class DropAction extends AbstractTimerAction<DropAction> {
super("drop", DropAction::new);
}
@Override
public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) {
this.setTickDelay(result.readInt(100)).setNumber(result.readInt(1));
}
@Override
public boolean doTick(@NotNull ServerBot bot) {
bot.dropAll();
bot.dropAll(false);
return true;
}
}

View File

@@ -14,13 +14,15 @@ public class FishAction extends AbstractTimerAction<FishAction> {
super("fish", FishAction::new);
}
private int delay = 0;
private int nowDelay = 0;
private static final int CATCH_ENTITY_DELAY = 20;
private int initialFishInterval = 0;
private int tickToNextFish = 0;
@Override
public FishAction setTickDelay(int tickDelay) {
super.setTickDelay(0);
this.delay = tickDelay;
public FishAction setInitialTickInterval(int initialTickInterval) {
super.setInitialTickInterval(1);
this.initialFishInterval = initialTickInterval;
return this;
}
@@ -28,22 +30,22 @@ public class FishAction extends AbstractTimerAction<FishAction> {
@NotNull
public CompoundTag save(@NotNull CompoundTag nbt) {
super.save(nbt);
nbt.putInt("fishDelay", this.delay);
nbt.putInt("fishNowDelay", this.nowDelay);
nbt.putInt("initialFishInterval", this.initialFishInterval);
nbt.putInt("tickToNextFish", this.tickToNextFish);
return nbt;
}
@Override
public void load(@NotNull CompoundTag nbt) {
super.load(nbt);
this.delay = nbt.getInt("fishDelay");
this.nowDelay = nbt.getInt("fishNowDelay");
this.initialFishInterval = nbt.getInt("initialFishInterval").orElseThrow();
this.tickToNextFish = nbt.getInt("tickToNextFish").orElseThrow();
}
@Override
public boolean doTick(@NotNull ServerBot bot) {
if (this.nowDelay > 0) {
this.nowDelay--;
if (this.tickToNextFish > 0) {
this.tickToNextFish--;
return false;
}
@@ -56,12 +58,12 @@ public class FishAction extends AbstractTimerAction<FishAction> {
if (fishingHook != null) {
if (fishingHook.currentState == FishingHook.FishHookState.HOOKED_IN_ENTITY) {
mainHand.use(bot.level(), bot, InteractionHand.MAIN_HAND);
this.nowDelay = 20;
this.tickToNextFish = CATCH_ENTITY_DELAY;
return false;
}
if (fishingHook.nibble > 0) {
mainHand.use(bot.level(), bot, InteractionHand.MAIN_HAND);
this.nowDelay = this.delay;
this.tickToNextFish = this.initialFishInterval - 1;
return true;
}
} else {

View File

@@ -2,6 +2,7 @@ package org.leavesmc.leaves.bot.agent.actions;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -11,15 +12,18 @@ import org.leavesmc.leaves.command.CommandArgument;
import org.leavesmc.leaves.command.CommandArgumentResult;
import org.leavesmc.leaves.command.CommandArgumentType;
import java.text.DecimalFormat;
import java.util.List;
public class LookAction extends AbstractBotAction<LookAction> {
private static final DecimalFormat DF = new DecimalFormat("0.0");
public LookAction() {
super("look", CommandArgument.of(CommandArgumentType.DOUBLE, CommandArgumentType.DOUBLE, CommandArgumentType.DOUBLE), LookAction::new);
this.setTabComplete(0, List.of("<X>"));
this.setTabComplete(1, List.of("<Y>"));
this.setTabComplete(2, List.of("<Z>"));
this.setSuggestion(0, (sender, arg) -> sender instanceof ServerPlayer player ? Pair.of(List.of(DF.format(player.getX())), "<X>") : Pair.of(List.of("0"), "<X>"));
this.setSuggestion(1, (sender, arg) -> sender instanceof ServerPlayer player ? Pair.of(List.of(DF.format(player.getY())), "<Y>") : Pair.of(List.of("0"), "<Y>"));
this.setSuggestion(2, (sender, arg) -> sender instanceof ServerPlayer player ? Pair.of(List.of(DF.format(player.getZ())), "<Z>") : Pair.of(List.of("0"), "<Z>"));
}
private Vector pos;
@@ -28,7 +32,7 @@ public class LookAction extends AbstractBotAction<LookAction> {
public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) throws IllegalArgumentException {
Vector pos = result.readVector();
if (pos != null) {
this.setPos(pos).setTickDelay(0).setNumber(1);
this.setPos(pos).setInitialTickDelay(0).setInitialTickInterval(1).setInitialNumber(1);
} else {
throw new IllegalArgumentException("pos?");
}
@@ -47,7 +51,13 @@ public class LookAction extends AbstractBotAction<LookAction> {
@Override
public void load(@NotNull CompoundTag nbt) {
super.load(nbt);
this.setPos(new Vector(nbt.getDouble("x"), nbt.getDouble("y"), nbt.getDouble("z")));
this.setPos(
new Vector(
nbt.getDouble("x").orElse(0.0),
nbt.getDouble("y").orElse(0.0),
nbt.getDouble("z").orElse(0.0)
)
);
}
public LookAction setPos(Vector pos) {

View File

@@ -19,7 +19,7 @@ public class RotateAction extends AbstractBotAction<RotateAction> {
@Override
public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) {
this.setPlayer(player).setTickDelay(0).setNumber(1);
this.setPlayer(player).setInitialTickDelay(0).setInitialTickInterval(1).setInitialNumber(1);
}
public RotateAction setPlayer(ServerPlayer player) {

View File

@@ -2,6 +2,7 @@ package org.leavesmc.leaves.bot.agent.actions;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.bot.ServerBot;
@@ -10,14 +11,17 @@ import org.leavesmc.leaves.command.CommandArgument;
import org.leavesmc.leaves.command.CommandArgumentResult;
import org.leavesmc.leaves.command.CommandArgumentType;
import java.text.DecimalFormat;
import java.util.List;
public class RotationAction extends AbstractBotAction<RotationAction> {
private static final DecimalFormat DF = new DecimalFormat("0.00");
public RotationAction() {
super("rotation", CommandArgument.of(CommandArgumentType.FLOAT, CommandArgumentType.FLOAT), RotationAction::new);
this.setTabComplete(0, List.of("<yaw>"));
this.setTabComplete(1, List.of("<pitch>"));
this.setSuggestion(0, (sender, arg) -> sender instanceof ServerPlayer player ? Pair.of(List.of(DF.format(player.getYRot())), "[yaw]") : Pair.of(List.of("0"), "<yaw>"));
this.setSuggestion(0, (sender, arg) -> sender instanceof ServerPlayer player ? Pair.of(List.of(DF.format(player.getXRot())), "[pitch]") : Pair.of(List.of("0"), "<pitch>"));
}
private float yaw;
@@ -29,7 +33,7 @@ public class RotationAction extends AbstractBotAction<RotationAction> {
return;
}
this.setYaw(result.readFloat(player.getYRot())).setPitch(result.readFloat(player.getXRot())).setTickDelay(0).setNumber(1);
this.setYaw(result.readFloat(player.getYRot())).setPitch(result.readFloat(player.getXRot())).setInitialTickDelay(0).setInitialTickInterval(1).setInitialNumber(1);
}
public RotationAction setYaw(float yaw) {
@@ -54,7 +58,7 @@ public class RotationAction extends AbstractBotAction<RotationAction> {
@Override
public void load(@NotNull CompoundTag nbt) {
super.load(nbt);
this.setYaw(nbt.getFloat("yaw")).setPitch(nbt.getFloat("pitch"));
this.setYaw(nbt.getFloat("yaw").orElseThrow()).setPitch(nbt.getFloat("pitch").orElseThrow());
}
@Override

View File

@@ -0,0 +1,80 @@
package org.leavesmc.leaves.bot.agent.actions;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.Items;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.command.CommandArgument;
import org.leavesmc.leaves.command.CommandArgumentResult;
import org.leavesmc.leaves.command.CommandArgumentType;
import java.util.Collections;
public class ShootAction extends AbstractTimerAction<ShootAction> {
private int drawingTick;
private int tickToRelease = -1;
public ShootAction() {
super("shoot", CommandArgument.of(CommandArgumentType.INTEGER, CommandArgumentType.INTEGER, CommandArgumentType.INTEGER, CommandArgumentType.INTEGER), ShootAction::new);
this.setSuggestion(3, Pair.of(Collections.singletonList("20"), "[DrawingTick]"));
}
@Override
public void init() {
super.init();
tickToRelease = drawingTick;
}
@Override
public boolean doTick(@NotNull ServerBot bot) {
if (!bot.getItemInHand(InteractionHand.MAIN_HAND).is(Items.BOW)) {
return false;
}
tickToRelease--;
if (tickToRelease >= 0) {
bot.gameMode.useItem(bot, bot.level(), bot.getItemInHand(InteractionHand.MAIN_HAND), InteractionHand.MAIN_HAND).consumesAction();
bot.updateItemInHand(InteractionHand.MAIN_HAND);
return false;
} else {
bot.releaseUsingItem();
tickToRelease = drawingTick;
return true;
}
}
@Override
public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) {
super.loadCommand(player, result);
this.setDrawingTick(result.readInt(20));
}
@Override
@NotNull
public CompoundTag save(@NotNull CompoundTag nbt) {
super.save(nbt);
nbt.putInt("drawingTick", this.drawingTick);
nbt.putInt("tickToRelease", this.tickToRelease);
return nbt;
}
@Override
public void load(@NotNull CompoundTag nbt) {
super.load(nbt);
this.drawingTick = nbt.getInt("drawingTick").orElseThrow();
this.tickToRelease = nbt.getInt("tickToRelease").orElseThrow();
}
public int getDrawingTick() {
return drawingTick;
}
public ShootAction setDrawingTick(int drawingTick) {
this.drawingTick = drawingTick;
return this;
}
}

View File

@@ -16,7 +16,7 @@ public class SneakAction extends AbstractBotAction<SneakAction> {
@Override
public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) {
this.setTickDelay(0).setNumber(1);
this.setInitialTickDelay(0).setInitialTickInterval(1).setInitialNumber(1);
}
@Override

View File

@@ -17,7 +17,7 @@ public class SwimAction extends AbstractBotAction<SwimAction> {
@Override
public void loadCommand(@Nullable ServerPlayer player, @NotNull CommandArgumentResult result) {
this.setTickDelay(0).setNumber(-1);
this.setInitialTickDelay(0).setInitialTickInterval(1).setInitialNumber(-1);
}
@Override

View File

@@ -16,7 +16,7 @@ public class AlwaysSendDataConfig extends AbstractBotConfig<Boolean> {
private boolean value;
public AlwaysSendDataConfig() {
super(NAME, CommandArgument.of(CommandArgumentType.BOOLEAN).setTabComplete(0, List.of("true", "false")));
super(NAME, CommandArgument.of(CommandArgumentType.BOOLEAN).setSuggestion(0, List.of("true", "false")));
this.value = LeavesConfig.modify.fakeplayer.canSendDataAlways;
}
@@ -40,6 +40,6 @@ public class AlwaysSendDataConfig extends AbstractBotConfig<Boolean> {
@Override
public void load(@NotNull CompoundTag nbt) {
this.setValue(nbt.getBoolean(NAME));
this.setValue(nbt.getBoolean(NAME).orElseThrow());
}
}

View File

@@ -1,6 +1,7 @@
package org.leavesmc.leaves.bot.agent.configs;
import net.minecraft.nbt.CompoundTag;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.bot.agent.AbstractBotConfig;
import org.leavesmc.leaves.command.CommandArgument;
@@ -13,7 +14,7 @@ public class SimulationDistanceConfig extends AbstractBotConfig<Integer> {
public static final String NAME = "simulation_distance";
public SimulationDistanceConfig() {
super(NAME, CommandArgument.of(CommandArgumentType.INTEGER).setTabComplete(0, List.of("2", "10", "<INT 2 - 32>")));
super(NAME, CommandArgument.of(CommandArgumentType.INTEGER).setSuggestion(0, Pair.of(List.of("2", "10"), "<INT 2 - 32>")));
}
@Override
@@ -39,6 +40,6 @@ public class SimulationDistanceConfig extends AbstractBotConfig<Integer> {
@Override
public void load(@NotNull CompoundTag nbt) {
this.setValue(nbt.getInt(NAME));
this.setValue(nbt.getInt(NAME).orElseThrow());
}
}

View File

@@ -13,7 +13,7 @@ public class SkipSleepConfig extends AbstractBotConfig<Boolean> {
public static final String NAME = "skip_sleep";
public SkipSleepConfig() {
super(NAME, CommandArgument.of(CommandArgumentType.BOOLEAN).setTabComplete(0, List.of("true", "false")));
super(NAME, CommandArgument.of(CommandArgumentType.BOOLEAN).setSuggestion(0, List.of("true", "false")));
}
@Override
@@ -35,6 +35,6 @@ public class SkipSleepConfig extends AbstractBotConfig<Boolean> {
@Override
public void load(@NotNull CompoundTag nbt) {
this.setValue(nbt.getBoolean(NAME));
this.setValue(nbt.getBoolean(NAME).orElseThrow());
}
}

View File

@@ -16,7 +16,7 @@ public class SpawnPhantomConfig extends AbstractBotConfig<Boolean> {
private boolean value;
public SpawnPhantomConfig() {
super(NAME, CommandArgument.of(CommandArgumentType.BOOLEAN).setTabComplete(0, List.of("true", "false")));
super(NAME, CommandArgument.of(CommandArgumentType.BOOLEAN).setSuggestion(0, List.of("true", "false")));
this.value = LeavesConfig.modify.fakeplayer.canSpawnPhantom;
}
@@ -47,6 +47,6 @@ public class SpawnPhantomConfig extends AbstractBotConfig<Boolean> {
@Override
public void load(@NotNull CompoundTag nbt) {
this.setValue(nbt.getBoolean(NAME));
this.setValue(nbt.getBoolean(NAME).orElseThrow());
}
}

View File

@@ -18,7 +18,7 @@ public class TickTypeConfig extends AbstractBotConfig<ServerBot.TickType> {
private ServerBot.TickType value;
public TickTypeConfig() {
super(NAME, CommandArgument.of(TICK_TYPE_ARGUMENT).setTabComplete(0, List.of("network", "entity_list")));
super(NAME, CommandArgument.of(TICK_TYPE_ARGUMENT).setSuggestion(0, List.of("network", "entity_list")));
this.value = LeavesConfig.modify.fakeplayer.tickType;
}
@@ -42,6 +42,6 @@ public class TickTypeConfig extends AbstractBotConfig<ServerBot.TickType> {
@Override
public void load(@NotNull CompoundTag nbt) {
this.setValue(TICK_TYPE_ARGUMENT.parse(nbt.getString(NAME)));
this.setValue(TICK_TYPE_ARGUMENT.parse(nbt.getString(NAME).orElseThrow()));
}
}

View File

@@ -1,6 +1,10 @@
package org.leavesmc.leaves.bot.subcommands;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.network.chat.Component;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.entity.CraftPlayer;
@@ -18,6 +22,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import static net.kyori.adventure.text.Component.text;
@@ -165,10 +170,7 @@ public class BotActionCommand implements LeavesSubcommand {
list.add(String.valueOf(i));
}
} else {
AbstractBotAction<?> action = Actions.getForName(args[1]);
if (action != null) {
list.addAll(action.getArgument().tabComplete(args.length - 3));
}
return Collections.singletonList("<" + args[1] + " not found>");
}
}
}
@@ -176,6 +178,40 @@ public class BotActionCommand implements LeavesSubcommand {
return list;
}
@Override
public CompletableFuture<Suggestions> tabSuggestion(CommandSender sender, String subCommand, String[] args, Location location, SuggestionsBuilder builder) {
if (!LeavesConfig.modify.fakeplayer.canUseAction) {
return null;
}
if (args.length >= 3) {
if (args[1].equals("stop")) {
return null;
}
AbstractBotAction<?> action = Actions.getForName(args[1]);
if (action != null) {
Pair<List<String>, String> results = action.getArgument().suggestion(args.length - 3, sender, args[args.length - 1]);
if (results == null || results.getLeft() == null) {
return builder.buildFuture();
}
builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1);
for (String s : results.getLeft()) {
if (results.getRight() != null) {
builder.suggest(s, Component.literal(results.getRight()));
} else {
builder.suggest(s);
}
}
return builder.buildFuture();
}
}
return null;
}
@Override
public boolean tabCompletes() {
return LeavesConfig.modify.fakeplayer.canUseAction;

View File

@@ -1,6 +1,10 @@
package org.leavesmc.leaves.bot.subcommands;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.network.chat.Component;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
@@ -17,6 +21,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import static net.kyori.adventure.text.Component.text;
@@ -90,12 +95,7 @@ public class BotConfigCommand implements LeavesSubcommand {
}
if (args.length >= 3) {
Configs<?> config = Configs.getConfig(args[1]);
if (config != null) {
list.addAll(bot.getConfig(config).getArgument().tabComplete(args.length - 3));
} else {
return Collections.singletonList("<" + args[1] + " not found>");
}
return Collections.singletonList("<" + args[1] + " not found>");
}
}
}
@@ -103,6 +103,43 @@ public class BotConfigCommand implements LeavesSubcommand {
return list;
}
@Override
public CompletableFuture<Suggestions> tabSuggestion(CommandSender sender, String subCommand, String[] args, Location location, SuggestionsBuilder builder) {
if (!LeavesConfig.modify.fakeplayer.canModifyConfig) {
return null;
}
if (args.length >= 3) {
ServerBot bot = BotList.INSTANCE.getBotByName(args[0]);
if (bot == null) {
return null;
}
Configs<?> config = Configs.getConfig(args[1]);
if (config != null) {
AbstractBotConfig<?> botConfig = bot.getConfig(config);
Pair<List<String>, String> results = botConfig.getArgument().suggestion(args.length - 3, sender, args[args.length - 1]);
if (results == null || results.getLeft() == null) {
return builder.buildFuture();
}
builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1);
for (String s : results.getLeft()) {
if (results.getRight() != null) {
builder.suggest(s, Component.literal(results.getRight()));
} else {
builder.suggest(s);
}
}
return builder.buildFuture();
}
}
return null;
}
@Override
public boolean tabCompletes() {
return LeavesConfig.modify.fakeplayer.canModifyConfig;

View File

@@ -12,6 +12,7 @@ import org.leavesmc.leaves.LeavesConfig;
import org.leavesmc.leaves.LeavesLogger;
import org.leavesmc.leaves.bot.BotCreateState;
import org.leavesmc.leaves.bot.BotList;
import org.leavesmc.leaves.bot.BotUtil;
import org.leavesmc.leaves.command.LeavesSubcommand;
import org.leavesmc.leaves.event.bot.BotCreateEvent;
@@ -30,7 +31,8 @@ public class BotCreateCommand implements LeavesSubcommand {
}
String botName = args[0];
if (this.canCreate(sender, botName)) {
String fullName = BotUtil.getFullName(botName);
if (this.canCreate(sender, fullName)) {
BotCreateState.Builder builder = BotCreateState.builder(botName, Bukkit.getWorlds().getFirst().getSpawnLocation()).createReason(BotCreateEvent.CreateReason.COMMAND).creator(sender);
if (args.length >= 2) {

View File

@@ -50,7 +50,7 @@ public class BotLoadCommand implements LeavesSubcommand {
BotList botList = BotList.INSTANCE;
if (args.length <= 1) {
list.addAll(botList.getSavedBotList().getAllKeys());
list.addAll(botList.getSavedBotList().keySet());
}
return list;

View File

@@ -13,11 +13,13 @@ public class SimpleBytebufManager implements BytebufManager {
this.internal = internal;
}
@SuppressWarnings("deprecation")
@Override
public void registerListener(Plugin plugin, PacketListener listener) {
internal.listenerMap.put(listener, plugin);
}
@SuppressWarnings("deprecation")
@Override
public void unregisterListener(Plugin plugin, PacketListener listener) {
internal.listenerMap.remove(listener);

View File

@@ -270,6 +270,11 @@ public class WrappedBytebuf implements Bytebuf {
return new WrappedBytebuf(this.buf.copy());
}
@Override
public void retain() {
this.buf.retain();
}
@Override
public boolean release() {
return this.buf.release();

View File

@@ -31,10 +31,12 @@ import org.leavesmc.leaves.bytebuf.packet.PacketType;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.leavesmc.leaves.bytebuf.packet.PacketType.*;
@SuppressWarnings({"deprecation", "rawtypes", "unchecked"})
public class InternalBytebufHandler {
private class PacketHandler extends ChannelDuplexHandler {
@@ -87,6 +89,17 @@ public class InternalBytebufHandler {
}
}
private static final List<String> PACKET_PACKAGES = List.of(
"net.minecraft.network.protocol.common",
"net.minecraft.network.protocol.configuration",
"net.minecraft.network.protocol.cookie",
"net.minecraft.network.protocol.game",
"net.minecraft.network.protocol.handshake",
"net.minecraft.network.protocol.login",
"net.minecraft.network.protocol.ping",
"net.minecraft.network.protocol.status"
);
public final Map<PacketListener, Plugin> listenerMap = new HashMap<>();
private final BytebufManager manager = new SimpleBytebufManager(this);
private final ImmutableMap<PacketType, StreamCodec> type2CodecMap;
@@ -94,26 +107,18 @@ public class InternalBytebufHandler {
public InternalBytebufHandler() {
ImmutableMap.Builder<PacketType, StreamCodec> builder = ImmutableMap.builder();
for (PacketType packet : PacketType.values()) {
Class<?> packetClass;
try {
packetClass = Class.forName("net.minecraft.network.protocol.game." + packet.name() + "Packet");
} catch (ClassNotFoundException e) {
String className = packet.name() + "Packet";
for (String basePackage : PACKET_PACKAGES) {
try {
packetClass = Class.forName("net.minecraft.network.protocol.common." + packet.name() + "Packet");
} catch (ClassNotFoundException e2) {
try {
packetClass = Class.forName("net.minecraft.network.protocol.ping." + packet.name() + "Packet");
} catch (ClassNotFoundException ignored) {
continue;
}
Class<?> packetClass = Class.forName(basePackage + "." + className);
Field field = packetClass.getDeclaredField("STREAM_CODEC");
field.setAccessible(true);
builder.put(packet, (StreamCodec) field.get(null));
} catch (Exception ignored) {
}
}
try {
Field field = packetClass.getDeclaredField("STREAM_CODEC");
builder.put(packet, (StreamCodec<FriendlyByteBuf, net.minecraft.network.protocol.Packet<?>>) field.get(null));
} catch (Exception ignored) {
}
}
builder.put(ClientboundMoveEntityPos, ClientboundMoveEntityPacket.Pos.STREAM_CODEC);
@@ -232,4 +237,4 @@ public class InternalBytebufHandler {
codec.encode(buf, nmsPacket);
return new Packet(type, new WrappedBytebuf(buf));
}
}
}

View File

@@ -1,23 +1,29 @@
package org.leavesmc.leaves.command;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
public class CommandArgument {
public static final CommandArgument EMPTY = new CommandArgument();
private static final Pair<List<String>, String> EMPTY_SUGGESTION_RESULT = Pair.of(List.of(), null);
private static final BiFunction<CommandSender, String, Pair<List<String>, String>> EMPTY_SUGGESTION = (sender, arg) -> EMPTY_SUGGESTION_RESULT;
private final List<BiFunction<CommandSender, String, Pair<List<String>, String>>> suggestions;
private final List<CommandArgumentType<?>> argumentTypes;
private final List<List<String>> tabComplete;
private CommandArgument(CommandArgumentType<?>... argumentTypes) {
this.argumentTypes = List.of(argumentTypes);
this.tabComplete = new ArrayList<>();
this.suggestions = new ArrayList<>();
for (int i = 0; i < argumentTypes.length; i++) {
tabComplete.add(new ArrayList<>());
suggestions.add(EMPTY_SUGGESTION);
}
}
@@ -25,29 +31,31 @@ public class CommandArgument {
return new CommandArgument(argumentTypes);
}
public List<String> tabComplete(int n) {
if (tabComplete.size() > n) {
return tabComplete.get(n);
} else {
return List.of();
}
}
public CommandArgument setTabComplete(int index, List<String> list) {
tabComplete.set(index, list);
return this;
}
public CommandArgument setAllTabComplete(List<List<String>> tabComplete) {
this.tabComplete.clear();
this.tabComplete.addAll(tabComplete);
return this;
}
public List<CommandArgumentType<?>> getArgumentTypes() {
return argumentTypes;
}
public CommandArgument setSuggestion(int n, BiFunction<CommandSender, String, Pair<List<String>, String>> suggestion) {
this.suggestions.set(n, suggestion);
return this;
}
public CommandArgument setSuggestion(int n, Pair<List<String>, String> suggestion) {
return this.setSuggestion(n, (sender, arg) -> suggestion);
}
public CommandArgument setSuggestion(int n, List<String> tabComplete) {
return this.setSuggestion(n, Pair.of(tabComplete, null));
}
public Pair<List<String>, String> suggestion(int n, CommandSender sender, String arg) {
if (suggestions.size() > n) {
return suggestions.get(n).apply(sender, arg);
} else {
return EMPTY_SUGGESTION.apply(sender, arg);
}
}
public CommandArgumentResult parse(int index, String @NotNull [] args) {
Object[] result = new Object[argumentTypes.size()];
Arrays.fill(result, null);

View File

@@ -14,7 +14,12 @@ import org.bukkit.permissions.PermissionDefault;
import org.bukkit.plugin.PluginManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.command.subcommands.*;
import org.leavesmc.leaves.command.subcommands.ConfigCommand;
import org.leavesmc.leaves.command.subcommands.CounterCommand;
import org.leavesmc.leaves.command.subcommands.PeacefulModeSwitchCommand;
import org.leavesmc.leaves.command.subcommands.ReloadCommand;
import org.leavesmc.leaves.command.subcommands.ReportCommand;
import org.leavesmc.leaves.command.subcommands.UpdateCommand;
import java.util.ArrayList;
import java.util.Arrays;

View File

@@ -15,7 +15,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -57,22 +56,15 @@ public class LeavesCommandUtil {
ArrayList<String> results = Lists.newArrayList();
if (!collection.isEmpty()) {
Iterator iterator = Iterables.transform(collection, Functions.toStringFunction()).iterator();
while (iterator.hasNext()) {
String s1 = (String) iterator.next();
for (String s1 : Iterables.transform(collection, Functions.toStringFunction())) {
if (matches(last, s1) && (sender.hasPermission(basePermission + s1) || sender.hasPermission(overridePermission))) {
results.add(s1);
}
}
if (results.isEmpty()) {
iterator = collection.iterator();
while (iterator.hasNext()) {
Object object = iterator.next();
for (Object object : collection) {
if (object instanceof ResourceLocation && matches(last, ((ResourceLocation) object).getPath())) {
results.add(String.valueOf(object));
}
@@ -122,6 +114,7 @@ public class LeavesCommandUtil {
}
// Copy from org/bukkit/command/defaults/HelpCommand.java
/**
* Computes the Dameraur-Levenshtein Distance between two strings. Adapted
* from the algorithm at <a href="http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance">Wikipedia: DamerauLevenshtein distance</a>

View File

@@ -14,6 +14,7 @@ import org.leavesmc.leaves.LeavesConfig;
import java.util.List;
// TODO merge to /leaves blockupdate
public class NoBlockUpdateCommand extends Command {
private static boolean noBlockUpdate = false;

View File

@@ -0,0 +1,14 @@
package org.leavesmc.leaves.config;
public class InternalConfigProvider implements LeavesConfigProvider {
public static final InternalConfigProvider INSTANCE = new InternalConfigProvider();
public LeavesConfigValue getConfig(String configNode) {
return new LeavesConfigValue(GlobalConfigManager.getVerifiedConfig(configNode).get());
}
public void setConfig(String configNode, LeavesConfigValue configValue) {
GlobalConfigManager.getVerifiedConfig(configNode);
}
}

View File

@@ -72,7 +72,7 @@ public class CraftBot extends CraftPlayer implements Bot {
}
@Override
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause, io.papermc.paper.entity.TeleportFlag... flags) {
public boolean teleport(Location location, PlayerTeleportEvent.@NotNull TeleportCause cause, io.papermc.paper.entity.TeleportFlag... flags) {
Preconditions.checkArgument(location != null, "location cannot be null");
Preconditions.checkState(location.getWorld().equals(this.getWorld()), "[Leaves] Fakeplayers do not support changing world, Please use leaves fakeplayer-api instead!");
return super.teleport(location, cause, flags);

View File

@@ -26,12 +26,7 @@ public class CraftBotManager implements BotManager {
public CraftBotManager() {
this.botList = MinecraftServer.getServer().getBotList();
this.botViews = Collections.unmodifiableList(Lists.transform(botList.bots, new Function<ServerBot, CraftBot>() {
@Override
public CraftBot apply(ServerBot bot) {
return bot.getBukkitEntity();
}
}));
this.botViews = Collections.unmodifiableList(Lists.transform(botList.bots, bot -> bot.getBukkitEntity()));
}
@Override

View File

@@ -0,0 +1,32 @@
// This file is licensed under the MIT license.
package org.leavesmc.leaves.plugin;
import java.util.HashSet;
import java.util.Set;
import static org.leavesmc.leaves.plugin.Features.*;
public class ServerFeatureManager implements FeatureManager {
public static ServerFeatureManager INSTANCE = new ServerFeatureManager();
private final Set<String> availableFeatures = new HashSet<>();
private ServerFeatureManager() {
availableFeatures.addAll(Set.of(
FAKEPLAYER,
PHOTOGRAPHER
));
if (Boolean.getBoolean("leavesclip.enable.mixin")) {
availableFeatures.add(MIXIN);
}
}
@Override
public Set<String> getAvailableFeatures() {
return availableFeatures;
}
@Override
public boolean isFeatureAvailable(String feature) {
return availableFeatures.contains(feature);
}
}

View File

@@ -0,0 +1,21 @@
// This file is licensed under the MIT license.
package org.leavesmc.leaves.plugin.provider.configuration;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import java.util.List;
@SuppressWarnings("FieldMayBeFinal")
@ConfigSerializable
public class FeaturesConfiguration {
private List<String> required = List.of();
private List<String> optional = List.of();
public List<String> getRequired() {
return required;
}
public List<String> getOptional() {
return optional;
}
}

View File

@@ -1,3 +1,4 @@
// This file is licensed under the MIT license.
package org.leavesmc.leaves.plugin.provider.configuration;
import com.google.common.collect.ImmutableList;
@@ -11,9 +12,10 @@ import io.papermc.paper.plugin.provider.configuration.serializer.PermissionConfi
import io.papermc.paper.plugin.provider.configuration.serializer.constraints.PluginConfigConstraints;
import io.papermc.paper.plugin.provider.configuration.type.PermissionConfiguration;
import org.bukkit.craftbukkit.util.ApiVersion;
import org.spongepowered.configurate.CommentedConfigurationNode;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.configurate.ConfigurateException;
import org.spongepowered.configurate.hocon.HoconConfigurationLoader;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.gson.GsonConfigurationLoader;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.ObjectMapper;
import org.spongepowered.configurate.serialize.ScalarSerializer;
@@ -21,25 +23,23 @@ import org.spongepowered.configurate.serialize.SerializationException;
import java.io.BufferedReader;
import java.lang.reflect.Type;
import java.util.List;
import java.util.function.Predicate;
@SuppressWarnings("FieldMayBeFinal")
@ConfigSerializable
public class LeavesPluginMeta extends PaperPluginMeta {
private List<String> mixins;
static final ApiVersion MINIMUM = ApiVersion.getOrCreateVersion("1.21");
private FeaturesConfiguration features = new FeaturesConfiguration();
private MixinConfiguration mixin = new MixinConfiguration();
static final ApiVersion MINIMUM = ApiVersion.getOrCreateVersion("1.21.4");
public static LeavesPluginMeta create(BufferedReader reader) throws ConfigurateException {
HoconConfigurationLoader loader = HoconConfigurationLoader.builder()
.prettyPrinting(true)
.emitComments(true)
.emitJsonCompatible(true)
GsonConfigurationLoader loader = GsonConfigurationLoader.builder()
.source(() -> reader)
.defaultOptions((options) ->
options.serializers((serializers) ->
serializers.register(new ScalarSerializer<>(ApiVersion.class) {
@Override
public ApiVersion deserialize(final Type type, final Object obj) throws SerializationException {
public ApiVersion deserialize(final @NotNull Type type, final @NotNull Object obj) throws SerializationException {
try {
final ApiVersion version = ApiVersion.getOrCreateVersion(obj.toString());
if (version.isOlderThan(MINIMUM)) {
@@ -52,7 +52,7 @@ public class LeavesPluginMeta extends PaperPluginMeta {
}
@Override
protected Object serialize(final ApiVersion item, final Predicate<Class<?>> typeSupported) {
protected @NotNull Object serialize(final ApiVersion item, final @NotNull Predicate<Class<?>> typeSupported) {
return item.getVersionString();
}
})
@@ -70,21 +70,30 @@ public class LeavesPluginMeta extends PaperPluginMeta {
)
)
.build();
CommentedConfigurationNode node = loader.load();
ConfigurationNode node = loader.load();
LegacyPaperMeta.migrate(node);
LeavesPluginMeta pluginConfiguration = node.require(LeavesPluginMeta.class);
if (!node.node("author").virtual()) {
pluginConfiguration.authors = ImmutableList.<String>builder()
var authorNode = node.node("author");
if (!authorNode.virtual()) {
String author = authorNode.getString();
var authorsBuilder = ImmutableList.<String>builder();
if (author != null) {
authorsBuilder.add(author);
}
pluginConfiguration.authors = authorsBuilder
.addAll(pluginConfiguration.authors)
.add(node.node("author").getString())
.build();
}
return pluginConfiguration;
}
public List<String> getMixins() {
return mixins;
public FeaturesConfiguration getFeatures() {
return features;
}
public MixinConfiguration getMixin() {
return mixin;
}
}

View File

@@ -0,0 +1,35 @@
// This file is licensed under the MIT license.
package org.leavesmc.leaves.plugin.provider.configuration;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.PostProcess;
import java.util.List;
@SuppressWarnings({"FieldMayBeFinal", "unused"})
@ConfigSerializable
public class MixinConfiguration {
private String packageName;
private List<String> mixins = List.of();
private String accessWidener;
@PostProcess
public void postProcess() {
if (mixins.isEmpty()) return;
if (packageName == null) {
throw new IllegalStateException("Already define mixins: " + mixins + ", but no mixin package-name provided");
}
}
public List<String> getMixins() {
return mixins;
}
public String getAccessWidener() {
return accessWidener;
}
public String getPackageName() {
return packageName;
}
}

View File

@@ -17,8 +17,8 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@LeavesProtocol(namespace = "appleskin")
public class AppleSkinProtocol {
@LeavesProtocol.Register(namespace = "appleskin")
public class AppleSkinProtocol implements LeavesProtocol {
public static final String PROTOCOL_ID = "appleskin";
@@ -41,64 +41,52 @@ public class AppleSkinProtocol {
@ProtocolHandler.PlayerJoin
public static void onPlayerLoggedIn(@NotNull ServerPlayer player) {
if (LeavesConfig.protocol.appleskin.enable) {
resetPlayerData(player);
}
resetPlayerData(player);
}
@ProtocolHandler.PlayerLeave
public static void onPlayerLoggedOut(@NotNull ServerPlayer player) {
if (LeavesConfig.protocol.appleskin.enable) {
subscribedChannels.remove(player);
resetPlayerData(player);
}
subscribedChannels.remove(player);
resetPlayerData(player);
}
@ProtocolHandler.MinecraftRegister(ignoreId = true)
public static void onPlayerSubscribed(@NotNull ServerPlayer player, String channel) {
if (LeavesConfig.protocol.appleskin.enable) {
subscribedChannels.computeIfAbsent(player, k -> new HashSet<>()).add(channel);
}
@ProtocolHandler.MinecraftRegister(onlyNamespace = true)
public static void onPlayerSubscribed(@NotNull ServerPlayer player, ResourceLocation id) {
subscribedChannels.computeIfAbsent(player, k -> new HashSet<>()).add(id.getPath());
}
@ProtocolHandler.Ticker
public static void tick() {
if (LeavesConfig.protocol.appleskin.enable) {
if (MinecraftServer.getServer().getTickCount() % LeavesConfig.protocol.appleskin.syncTickInterval != 0) {
return;
}
for (Map.Entry<ServerPlayer, Set<String>> entry : subscribedChannels.entrySet()) {
ServerPlayer player = entry.getKey();
FoodData data = player.getFoodData();
for (Map.Entry<ServerPlayer, Set<String>> entry : subscribedChannels.entrySet()) {
ServerPlayer player = entry.getKey();
FoodData data = player.getFoodData();
for (String channel : entry.getValue()) {
switch (channel) {
case "saturation" -> {
float saturation = data.getSaturationLevel();
Float previousSaturation = previousSaturationLevels.get(player);
if (previousSaturation == null || saturation != previousSaturation) {
ProtocolUtils.sendPayloadPacket(player, SATURATION_KEY, buf -> buf.writeFloat(saturation));
previousSaturationLevels.put(player, saturation);
}
for (String channel : entry.getValue()) {
switch (channel) {
case "saturation" -> {
float saturation = data.getSaturationLevel();
Float previousSaturation = previousSaturationLevels.get(player);
if (previousSaturation == null || saturation != previousSaturation) {
ProtocolUtils.sendBytebufPacket(player, SATURATION_KEY, buf -> buf.writeFloat(saturation));
previousSaturationLevels.put(player, saturation);
}
}
case "exhaustion" -> {
float exhaustion = data.exhaustionLevel;
Float previousExhaustion = previousExhaustionLevels.get(player);
if (previousExhaustion == null || Math.abs(exhaustion - previousExhaustion) >= MINIMUM_EXHAUSTION_CHANGE_THRESHOLD) {
ProtocolUtils.sendPayloadPacket(player, EXHAUSTION_KEY, buf -> buf.writeFloat(exhaustion));
previousExhaustionLevels.put(player, exhaustion);
}
case "exhaustion" -> {
float exhaustion = data.exhaustionLevel;
Float previousExhaustion = previousExhaustionLevels.get(player);
if (previousExhaustion == null || Math.abs(exhaustion - previousExhaustion) >= MINIMUM_EXHAUSTION_CHANGE_THRESHOLD) {
ProtocolUtils.sendBytebufPacket(player, EXHAUSTION_KEY, buf -> buf.writeFloat(exhaustion));
previousExhaustionLevels.put(player, exhaustion);
}
}
case "natural_regeneration" -> {
boolean regeneration = player.serverLevel().getGameRules().getBoolean(GameRules.RULE_NATURAL_REGENERATION);
Boolean previousRegeneration = previousNaturalRegeneration.get(player);
if (previousRegeneration == null || regeneration != previousRegeneration) {
ProtocolUtils.sendPayloadPacket(player, NATURAL_REGENERATION_KEY, buf -> buf.writeBoolean(regeneration));
previousNaturalRegeneration.put(player, regeneration);
}
case "natural_regeneration" -> {
boolean regeneration = player.serverLevel().getGameRules().getBoolean(GameRules.RULE_NATURAL_REGENERATION);
Boolean previousRegeneration = previousNaturalRegeneration.get(player);
if (previousRegeneration == null || regeneration != previousRegeneration) {
ProtocolUtils.sendBytebufPacket(player, NATURAL_REGENERATION_KEY, buf -> buf.writeBoolean(regeneration));
previousNaturalRegeneration.put(player, regeneration);
}
}
}
@@ -108,9 +96,7 @@ public class AppleSkinProtocol {
@ProtocolHandler.ReloadServer
public static void onServerReload() {
if (!LeavesConfig.protocol.appleskin.enable) {
disableAllPlayer();
}
disableAllPlayer();
}
public static void disableAllPlayer() {
@@ -124,4 +110,14 @@ public class AppleSkinProtocol {
previousSaturationLevels.remove(player);
previousNaturalRegeneration.remove(player);
}
@Override
public int tickerInterval(String tickerID) {
return LeavesConfig.protocol.appleskin.syncTickInterval;
}
@Override
public boolean isActive() {
return LeavesConfig.protocol.appleskin.enable;
}
}

View File

@@ -27,10 +27,8 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import static org.leavesmc.leaves.protocol.core.LeavesProtocolManager.EmptyPayload;
@LeavesProtocol(namespace = "bbor")
public class BBORProtocol {
@LeavesProtocol.Register(namespace = "bbor")
public class BBORProtocol implements LeavesProtocol {
public static final String PROTOCOL_ID = "bbor";
@@ -43,6 +41,8 @@ public class BBORProtocol {
private static final Map<Integer, Set<BBoundingBox>> playerBoundingBoxesCache = new HashMap<>();
private static final Map<ResourceLocation, Map<BBoundingBox, Set<BBoundingBox>>> dimensionCache = new ConcurrentHashMap<>();
private static boolean initialized = false;
@Contract("_ -> new")
public static ResourceLocation id(String path) {
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
@@ -50,10 +50,8 @@ public class BBORProtocol {
@ProtocolHandler.Ticker
public static void tick() {
if (LeavesConfig.protocol.bborProtocol) {
for (var playerEntry : players.entrySet()) {
sendBoundingToPlayer(playerEntry.getKey(), playerEntry.getValue());
}
for (var playerEntry : players.entrySet()) {
sendBoundingToPlayer(playerEntry.getKey(), playerEntry.getValue());
}
}
@@ -68,50 +66,41 @@ public class BBORProtocol {
@ProtocolHandler.PlayerJoin
public static void onPlayerLoggedIn(@NotNull ServerPlayer player) {
if (LeavesConfig.protocol.bborProtocol) {
ServerLevel overworld = MinecraftServer.getServer().overworld();
ProtocolUtils.sendPayloadPacket(player, INITIALIZE_CLIENT, buf -> {
buf.writeLong(overworld.getSeed());
buf.writeInt(overworld.levelData.getSpawnPos().getX());
buf.writeInt(overworld.levelData.getSpawnPos().getZ());
});
sendStructureList(player);
}
ServerLevel overworld = MinecraftServer.getServer().overworld();
ProtocolUtils.sendBytebufPacket(player, INITIALIZE_CLIENT, buf -> {
buf.writeLong(overworld.getSeed());
buf.writeInt(overworld.levelData.getSpawnPos().getX());
buf.writeInt(overworld.levelData.getSpawnPos().getZ());
});
sendStructureList(player);
}
@ProtocolHandler.PlayerLeave
public static void onPlayerLoggedOut(@NotNull ServerPlayer player) {
if (LeavesConfig.protocol.bborProtocol) {
players.remove(player.getId());
playerBoundingBoxesCache.remove(player.getId());
}
players.remove(player.getId());
playerBoundingBoxesCache.remove(player.getId());
}
@ProtocolHandler.PayloadReceiver(payload = EmptyPayload.class, payloadId = "subscribe")
public static void onPlayerSubscribed(@NotNull ServerPlayer player, EmptyPayload payload) {
if (LeavesConfig.protocol.bborProtocol) {
players.put(player.getId(), player);
sendBoundingToPlayer(player.getId(), player);
}
@ProtocolHandler.BytebufReceiver(key = "subscribe")
public static void onPlayerSubscribed(@NotNull ServerPlayer player, FriendlyByteBuf buf) {
players.put(player.getId(), player);
sendBoundingToPlayer(player.getId(), player);
}
@ProtocolHandler.ReloadDataPack
public static void onDataPackReload() {
if (LeavesConfig.protocol.bborProtocol) {
players.values().forEach(BBORProtocol::sendStructureList);
}
players.values().forEach(BBORProtocol::sendStructureList);
}
public static void onChunkLoaded(@NotNull LevelChunk chunk) {
if (LeavesConfig.protocol.bborProtocol) {
Map<String, StructureStart> structures = new HashMap<>();
final Registry<Structure> structureFeatureRegistry = chunk.getLevel().registryAccess().lookupOrThrow(Registries.STRUCTURE);
for (var es : chunk.getAllStarts().entrySet()) {
final var optional = structureFeatureRegistry.getResourceKey(es.getKey());
optional.ifPresent(key -> structures.put(key.location().toString(), es.getValue()));
}
if (!structures.isEmpty()) {
onStructuresLoaded(chunk.getLevel().dimension().location(), structures);
}
Map<String, StructureStart> structures = new HashMap<>();
final Registry<Structure> structureFeatureRegistry = chunk.getLevel().registryAccess().lookupOrThrow(Registries.STRUCTURE);
for (var es : chunk.getAllStarts().entrySet()) {
final var optional = structureFeatureRegistry.getResourceKey(es.getKey());
optional.ifPresent(key -> structures.put(key.location().toString(), es.getValue()));
}
if (!structures.isEmpty()) {
onStructuresLoaded(chunk.getLevel().dimension().location(), structures);
}
}
@@ -148,7 +137,7 @@ public class BBORProtocol {
final Registry<Structure> structureRegistry = player.server.registryAccess().lookupOrThrow(Registries.STRUCTURE);
final Set<String> structureIds = structureRegistry.entrySet().stream()
.map(e -> e.getKey().location().toString()).collect(Collectors.toSet());
ProtocolUtils.sendPayloadPacket(player, STRUCTURE_LIST_SYNC, buf -> {
ProtocolUtils.sendBytebufPacket(player, STRUCTURE_LIST_SYNC, buf -> {
buf.writeVarInt(structureIds.size());
structureIds.forEach(buf::writeUtf);
});
@@ -168,7 +157,7 @@ public class BBORProtocol {
}
Set<BBoundingBox> boundingBoxes = boundingBoxMap.get(key);
ProtocolUtils.sendPayloadPacket(player, ADD_BOUNDING_BOX, buf -> {
ProtocolUtils.sendBytebufPacket(player, ADD_BOUNDING_BOX, buf -> {
buf.writeResourceLocation(entry.getKey());
key.serialize(buf);
if (boundingBoxes != null && boundingBoxes.size() > 1) {
@@ -186,6 +175,7 @@ public class BBORProtocol {
for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) {
onPlayerLoggedIn(player);
}
initialized = true;
}
public static void loggedOutAllPlayer() {
@@ -201,8 +191,27 @@ public class BBORProtocol {
return dimensionCache.computeIfAbsent(dimensionId, dt -> new ConcurrentHashMap<>());
}
@Override
public boolean isActive() {
boolean active = LeavesConfig.protocol.bborProtocol;
if (!active && initialized) {
initialized = false;
loggedOutAllPlayer();
}
return active;
}
private record BBoundingBox(String type, BlockPos min, BlockPos max) {
private static int combineHashCodes(int @NotNull ... hashCodes) {
final int prime = 31;
int result = 0;
for (int hashCode : hashCodes) {
result = prime * result + hashCode;
}
return result;
}
public void serialize(@NotNull FriendlyByteBuf buf) {
buf.writeChar('S');
buf.writeInt(type.hashCode());
@@ -214,14 +223,5 @@ public class BBORProtocol {
public int hashCode() {
return combineHashCodes(min.hashCode(), max.hashCode());
}
private static int combineHashCodes(int @NotNull ... hashCodes) {
final int prime = 31;
int result = 0;
for (int hashCode : hashCodes) {
result = prime * result + hashCode;
}
return result;
}
}
}

View File

@@ -2,6 +2,8 @@ package org.leavesmc.leaves.protocol;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Contract;
@@ -17,14 +19,12 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@LeavesProtocol(namespace = "carpet")
public class CarpetServerProtocol {
@LeavesProtocol.Register(namespace = "carpet")
public class CarpetServerProtocol implements LeavesProtocol {
public static final String PROTOCOL_ID = "carpet";
public static final String VERSION = ProtocolUtils.buildProtocolVersion(PROTOCOL_ID);
private static final ResourceLocation HELLO_ID = CarpetServerProtocol.id("hello");
private static final String HI = "69";
private static final String HELLO = "420";
@@ -42,7 +42,7 @@ public class CarpetServerProtocol {
}
}
@ProtocolHandler.PayloadReceiver(payload = CarpetPayload.class, payloadId = "hello")
@ProtocolHandler.PayloadReceiver(payload = CarpetPayload.class)
private static void handleHello(@NotNull ServerPlayer player, @NotNull CarpetServerProtocol.CarpetPayload payload) {
if (LeavesConfig.protocol.leavesCarpetSupport) {
if (payload.nbt.contains(HELLO)) {
@@ -54,6 +54,11 @@ public class CarpetServerProtocol {
}
}
@Override
public boolean isActive() {
return false;
}
public static class CarpetRules {
private static final Map<String, CarpetRule> rules = new HashMap<>();
@@ -99,22 +104,13 @@ public class CarpetServerProtocol {
}
}
public record CarpetPayload(CompoundTag nbt) implements LeavesCustomPayload<CarpetPayload> {
public record CarpetPayload(CompoundTag nbt) implements LeavesCustomPayload {
@ID
private static final ResourceLocation HELLO_ID = CarpetServerProtocol.id("hello");
@New
public CarpetPayload(ResourceLocation location, FriendlyByteBuf buf) {
this(buf.readNbt());
}
@Override
public void write(FriendlyByteBuf buf) {
buf.writeNbt(nbt);
}
@Override
@NotNull
public ResourceLocation id() {
return HELLO_ID;
}
@Codec
private static final StreamCodec<FriendlyByteBuf, CarpetPayload> CODEC = StreamCodec.composite(
ByteBufCodecs.COMPOUND_TAG, CarpetPayload::nbt, CarpetPayload::new
);
}
}

View File

@@ -3,6 +3,8 @@ package org.leavesmc.leaves.protocol;
import com.google.common.collect.Sets;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
@@ -22,39 +24,33 @@ import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.Consumer;
@LeavesProtocol(namespace = "litematica-server-paster")
public class LMSPasterProtocol {
@LeavesProtocol.Register(namespace = "litematica-server-paster")
public class LMSPasterProtocol implements LeavesProtocol {
public static final String MOD_ID = "litematica-server-paster";
public static final String MOD_VERSION = "1.3.5";
private static final ResourceLocation PACKET_ID = ResourceLocation.fromNamespaceAndPath(MOD_ID, "network_v2");
private static final Map<ServerGamePacketListenerImpl, StringBuilder> VERY_LONG_CHATS = new WeakHashMap<>();
@ProtocolHandler.PayloadReceiver(payload = LmsPasterPayload.class, payloadId = "network_v2")
@ProtocolHandler.PayloadReceiver(payload = LmsPasterPayload.class)
public static void handlePackets(ServerPlayer player, LmsPasterPayload payload) {
if (!LeavesConfig.protocol.lmsPasterProtocol) {
return;
}
String playerName = player.getName().getString();
int id = payload.getPacketId();
CompoundTag nbt = payload.getNbt();
int id = payload.id();
CompoundTag nbt = payload.nbt();
switch (id) {
case LMSPasterProtocol.C2S.HI -> {
String clientModVersion = nbt.getString("mod_version");
String clientModVersion = nbt.getString("mod_version").orElseThrow();
LeavesLogger.LOGGER.info(String.format("Player %s connected with %s @ %s", playerName, LMSPasterProtocol.MOD_ID, clientModVersion));
ProtocolUtils.sendPayloadPacket(player, LMSPasterProtocol.S2C.build(LMSPasterProtocol.S2C.HI, nbt2 -> nbt2.putString("mod_version", LMSPasterProtocol.MOD_VERSION)));
ProtocolUtils.sendPayloadPacket(player, LMSPasterProtocol.S2C.build(LMSPasterProtocol.S2C.ACCEPT_PACKETS, nbt2 -> nbt2.putIntArray("ids", C2S.ALL_PACKET_IDS)));
}
case LMSPasterProtocol.C2S.CHAT -> {
String message = nbt.getString("chat");
String message = nbt.getString("chat").orElseThrow();
triggerCommand(player, playerName, message);
}
case LMSPasterProtocol.C2S.VERY_LONG_CHAT_START -> VERY_LONG_CHATS.put(player.connection, new StringBuilder());
case LMSPasterProtocol.C2S.VERY_LONG_CHAT_CONTENT -> {
String segment = nbt.getString("segment");
String segment = nbt.getString("segment").orElseThrow();
getVeryLongChatBuilder(player).ifPresent(builder -> builder.append(segment));
}
case LMSPasterProtocol.C2S.VERY_LONG_CHAT_END -> {
@@ -76,6 +72,11 @@ public class LMSPasterProtocol {
}
}
@Override
public boolean isActive() {
return LeavesConfig.protocol.lmsPasterProtocol;
}
private static class C2S {
public static final int HI = 0;
public static final int CHAT = 1;
@@ -115,36 +116,17 @@ public class LMSPasterProtocol {
}
}
public static class LmsPasterPayload implements LeavesCustomPayload<LmsPasterPayload> {
private final int id;
private final CompoundTag nbt;
public record LmsPasterPayload(int id, CompoundTag nbt) implements LeavesCustomPayload {
@ID
private static final ResourceLocation PACKET_ID = ResourceLocation.fromNamespaceAndPath(MOD_ID, "network_v2");
public LmsPasterPayload(int id, CompoundTag nbt) {
this.id = id;
this.nbt = nbt;
}
@New
public LmsPasterPayload(ResourceLocation location, FriendlyByteBuf buf) {
this(buf.readVarInt(), buf.readNbt());
}
public void write(FriendlyByteBuf buf) {
buf.writeVarInt(this.id);
buf.writeNbt(this.nbt);
}
@Override
public ResourceLocation id() {
return PACKET_ID;
}
public int getPacketId() {
return this.id;
}
public CompoundTag getNbt() {
return this.nbt;
}
@Codec
private static final StreamCodec<FriendlyByteBuf, LmsPasterPayload> CODEC = StreamCodec.composite(
ByteBufCodecs.VAR_INT,
LmsPasterPayload::id,
ByteBufCodecs.COMPOUND_TAG,
LmsPasterPayload::nbt,
LmsPasterPayload::new
);
}
}

View File

@@ -3,6 +3,8 @@ package org.leavesmc.leaves.protocol;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
@@ -34,10 +36,8 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import static org.leavesmc.leaves.protocol.core.LeavesProtocolManager.EmptyPayload;
@LeavesProtocol(namespace = "pca")
public class PcaSyncProtocol {
@LeavesProtocol.Register(namespace = "pca")
public class PcaSyncProtocol implements LeavesProtocol {
public static final String PROTOCOL_ID = "pca";
@@ -67,28 +67,18 @@ public class PcaSyncProtocol {
}
}
@ProtocolHandler.PayloadReceiver(payload = EmptyPayload.class, payloadId = "cancel_sync_block_entity")
private static void cancelSyncBlockEntityHandler(ServerPlayer player, EmptyPayload payload) {
if (!LeavesConfig.protocol.pca.enable) {
return;
}
@ProtocolHandler.BytebufReceiver(key = "cancel_sync_block_entity")
private static void cancelSyncBlockEntityHandler(ServerPlayer player, FriendlyByteBuf buf) {
PcaSyncProtocol.clearPlayerWatchBlock(player);
}
@ProtocolHandler.PayloadReceiver(payload = EmptyPayload.class, payloadId = "cancel_sync_entity")
private static void cancelSyncEntityHandler(ServerPlayer player, EmptyPayload payload) {
if (!LeavesConfig.protocol.pca.enable) {
return;
}
@ProtocolHandler.BytebufReceiver(key = "cancel_sync_entity")
private static void cancelSyncEntityHandler(ServerPlayer player, FriendlyByteBuf buf) {
PcaSyncProtocol.clearPlayerWatchEntity(player);
}
@ProtocolHandler.PayloadReceiver(payload = SyncBlockEntityPayload.class, payloadId = "sync_block_entity")
@ProtocolHandler.PayloadReceiver(payload = SyncBlockEntityPayload.class)
private static void syncBlockEntityHandler(ServerPlayer player, SyncBlockEntityPayload payload) {
if (!LeavesConfig.protocol.pca.enable) {
return;
}
MinecraftServer server = MinecraftServer.getServer();
BlockPos pos = payload.pos;
ServerLevel world = player.serverLevel();
@@ -127,12 +117,8 @@ public class PcaSyncProtocol {
});
}
@ProtocolHandler.PayloadReceiver(payload = SyncEntityPayload.class, payloadId = "sync_entity")
@ProtocolHandler.PayloadReceiver(payload = SyncEntityPayload.class)
private static void syncEntityHandler(ServerPlayer player, SyncEntityPayload payload) {
if (!LeavesConfig.protocol.pca.enable) {
return;
}
MinecraftServer server = MinecraftServer.getServer();
int entityId = payload.entityId;
ServerLevel world = player.serverLevel();
@@ -154,12 +140,12 @@ public class PcaSyncProtocol {
}
}
case OPS -> {
if (!(entity instanceof ServerBot) && server.getPlayerList().isOp(player.gameProfile)) {
if (!(entity instanceof ServerBot) && !server.getPlayerList().isOp(player.gameProfile)) {
return;
}
}
case OPS_AND_SELF -> {
if (!(entity instanceof ServerBot) && server.getPlayerList().isOp(player.gameProfile) && entity != player) {
if (!(entity instanceof ServerBot) && !server.getPlayerList().isOp(player.gameProfile) && entity != player) {
return;
}
}
@@ -191,13 +177,13 @@ public class PcaSyncProtocol {
}
public static void enablePcaSyncProtocol(@NotNull ServerPlayer player) {
ProtocolUtils.sendEmptyPayloadPacket(player, ENABLE_PCA_SYNC_PROTOCOL);
ProtocolUtils.sendEmptyPacket(player, ENABLE_PCA_SYNC_PROTOCOL);
lock.lock();
lock.unlock();
}
public static void disablePcaSyncProtocol(@NotNull ServerPlayer player) {
ProtocolUtils.sendEmptyPayloadPacket(player, DISABLE_PCA_SYNC_PROTOCOL);
ProtocolUtils.sendEmptyPacket(player, DISABLE_PCA_SYNC_PROTOCOL);
}
public static void updateEntity(@NotNull ServerPlayer player, @NotNull Entity entity) {
@@ -347,87 +333,62 @@ public class PcaSyncProtocol {
PcaSyncProtocol.clearPlayerWatchEntity(player);
}
public record UpdateEntityPayload(ResourceLocation dimension, int entityId, CompoundTag tag) implements LeavesCustomPayload<UpdateEntityPayload> {
@Override
public boolean isActive() {
return LeavesConfig.protocol.pca.enable;
}
public record UpdateEntityPayload(ResourceLocation dimension, int entityId, CompoundTag tag) implements LeavesCustomPayload {
@ID
public static final ResourceLocation UPDATE_ENTITY = PcaSyncProtocol.id("update_entity");
@New
public UpdateEntityPayload(ResourceLocation location, FriendlyByteBuf byteBuf) {
this(byteBuf.readResourceLocation(), byteBuf.readInt(), byteBuf.readNbt());
}
@Override
public void write(@NotNull FriendlyByteBuf buf) {
buf.writeResourceLocation(this.dimension);
buf.writeInt(this.entityId);
buf.writeNbt(this.tag);
}
@Override
public ResourceLocation id() {
return UPDATE_ENTITY;
}
@Codec
public static final StreamCodec<FriendlyByteBuf, UpdateEntityPayload> CODEC = StreamCodec.composite(
ResourceLocation.STREAM_CODEC,
UpdateEntityPayload::dimension,
ByteBufCodecs.INT,
UpdateEntityPayload::entityId,
ByteBufCodecs.COMPOUND_TAG,
UpdateEntityPayload::tag,
UpdateEntityPayload::new
);
}
public record UpdateBlockEntityPayload(ResourceLocation dimension, BlockPos blockPos, CompoundTag tag) implements LeavesCustomPayload<UpdateBlockEntityPayload> {
public record UpdateBlockEntityPayload(ResourceLocation dimension, BlockPos blockPos, CompoundTag tag) implements LeavesCustomPayload {
@ID
private static final ResourceLocation UPDATE_BLOCK_ENTITY = PcaSyncProtocol.id("update_block_entity");
@New
public UpdateBlockEntityPayload(ResourceLocation location, @NotNull FriendlyByteBuf byteBuf) {
this(byteBuf.readResourceLocation(), byteBuf.readBlockPos(), byteBuf.readNbt());
}
@Override
public void write(@NotNull FriendlyByteBuf buf) {
buf.writeResourceLocation(this.dimension);
buf.writeBlockPos(this.blockPos);
buf.writeNbt(this.tag);
}
@Override
public ResourceLocation id() {
return UPDATE_BLOCK_ENTITY;
}
@Codec
private static final StreamCodec<FriendlyByteBuf, UpdateBlockEntityPayload> CODEC = StreamCodec.composite(
ResourceLocation.STREAM_CODEC,
UpdateBlockEntityPayload::dimension,
BlockPos.STREAM_CODEC,
UpdateBlockEntityPayload::blockPos,
ByteBufCodecs.COMPOUND_TAG,
UpdateBlockEntityPayload::tag,
UpdateBlockEntityPayload::new
);
}
public record SyncBlockEntityPayload(BlockPos pos) implements LeavesCustomPayload<SyncBlockEntityPayload> {
public record SyncBlockEntityPayload(BlockPos pos) implements LeavesCustomPayload {
@ID
public static final ResourceLocation SYNC_BLOCK_ENTITY = PcaSyncProtocol.id("sync_block_entity");
@New
public SyncBlockEntityPayload(ResourceLocation id, @NotNull FriendlyByteBuf buf) {
this(buf.readBlockPos());
}
@Override
public void write(@NotNull FriendlyByteBuf buf) {
buf.writeBlockPos(pos);
}
@Override
public @NotNull ResourceLocation id() {
return SYNC_BLOCK_ENTITY;
}
@Codec
private static final StreamCodec<FriendlyByteBuf, SyncBlockEntityPayload> CODEC = StreamCodec.composite(
BlockPos.STREAM_CODEC, SyncBlockEntityPayload::pos, SyncBlockEntityPayload::new
);
}
public record SyncEntityPayload(int entityId) implements LeavesCustomPayload<SyncEntityPayload> {
public record SyncEntityPayload(int entityId) implements LeavesCustomPayload {
@ID
public static final ResourceLocation SYNC_ENTITY = PcaSyncProtocol.id("sync_entity");
@New
public SyncEntityPayload(ResourceLocation id, @NotNull FriendlyByteBuf buf) {
this(buf.readInt());
}
@Override
public void write(@NotNull FriendlyByteBuf buf) {
buf.writeInt(entityId);
}
@Override
public @NotNull ResourceLocation id() {
return SYNC_ENTITY;
}
@Codec
private static final StreamCodec<FriendlyByteBuf, SyncEntityPayload> CODEC = StreamCodec.composite(
ByteBufCodecs.INT, SyncEntityPayload::entityId, SyncEntityPayload::new
);
}
}
}

View File

@@ -8,8 +8,8 @@ import org.leavesmc.leaves.LeavesConfig;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
@LeavesProtocol(namespace = {"xaerominimap", "xaeroworldmap"})
public class XaeroMapProtocol {
@LeavesProtocol.Register(namespace = "xaerominimap_or_xaeroworldmap_i_dont_care")
public class XaeroMapProtocol implements LeavesProtocol {
public static final String PROTOCOL_ID_MINI = "xaerominimap";
public static final String PROTOCOL_ID_WORLD = "xaeroworldmap";
@@ -29,14 +29,19 @@ public class XaeroMapProtocol {
public static void onSendWorldInfo(@NotNull ServerPlayer player) {
if (LeavesConfig.protocol.xaeroMapProtocol) {
ProtocolUtils.sendPayloadPacket(player, MINIMAP_KEY, buf -> {
ProtocolUtils.sendBytebufPacket(player, MINIMAP_KEY, buf -> {
buf.writeByte(0);
buf.writeInt(LeavesConfig.protocol.xaeroMapServerID);
});
ProtocolUtils.sendPayloadPacket(player, WORLDMAP_KEY, buf -> {
ProtocolUtils.sendBytebufPacket(player, WORLDMAP_KEY, buf -> {
buf.writeByte(0);
buf.writeInt(LeavesConfig.protocol.xaeroMapServerID);
});
}
}
@Override
public boolean isActive() {
return LeavesConfig.protocol.xaeroMapProtocol;
}
}

View File

@@ -2,6 +2,7 @@ package org.leavesmc.leaves.protocol.bladeren;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Contract;
@@ -17,15 +18,12 @@ import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
@LeavesProtocol(namespace = "bladeren")
public class BladerenProtocol {
@LeavesProtocol.Register(namespace = "bladeren")
public class BladerenProtocol implements LeavesProtocol {
public static final String PROTOCOL_ID = "bladeren";
public static final String PROTOCOL_VERSION = ProtocolUtils.buildProtocolVersion(PROTOCOL_ID);
private static final ResourceLocation HELLO_ID = id("hello");
private static final ResourceLocation FEATURE_MODIFY_ID = id("feature_modify");
private static final Map<String, BiConsumer<ServerPlayer, CompoundTag>> registeredFeatures = new HashMap<>();
@Contract("_ -> new")
@@ -33,50 +31,49 @@ public class BladerenProtocol {
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
}
@ProtocolHandler.PayloadReceiver(payload = BladerenHelloPayload.class, payloadId = "hello")
@ProtocolHandler.PayloadReceiver(payload = BladerenHelloPayload.class)
private static void handleHello(@NotNull ServerPlayer player, @NotNull BladerenHelloPayload payload) {
if (LeavesConfig.protocol.bladeren.enable) {
String clientVersion = payload.version;
CompoundTag tag = payload.nbt;
String clientVersion = payload.version;
CompoundTag tag = payload.nbt;
LeavesLogger.LOGGER.info("Player " + player.getScoreboardName() + " joined with bladeren " + clientVersion);
LeavesLogger.LOGGER.info("Player " + player.getScoreboardName() + " joined with bladeren " + clientVersion);
if (tag != null) {
CompoundTag featureNbt = tag.getCompound("Features");
for (String name : featureNbt.getAllKeys()) {
if (registeredFeatures.containsKey(name)) {
registeredFeatures.get(name).accept(player, featureNbt.getCompound(name));
}
if (tag != null) {
CompoundTag featureNbt = tag.getCompound("Features").orElseThrow();
for (String name : featureNbt.keySet()) {
if (registeredFeatures.containsKey(name)) {
registeredFeatures.get(name).accept(player, featureNbt.getCompound(name).orElseThrow());
}
}
}
}
@ProtocolHandler.PayloadReceiver(payload = BladerenFeatureModifyPayload.class, payloadId = "feature_modify")
@ProtocolHandler.PayloadReceiver(payload = BladerenFeatureModifyPayload.class)
private static void handleModify(@NotNull ServerPlayer player, @NotNull BladerenFeatureModifyPayload payload) {
if (LeavesConfig.protocol.bladeren.enable) {
String name = payload.name;
CompoundTag tag = payload.nbt;
String name = payload.name;
CompoundTag tag = payload.nbt;
if (registeredFeatures.containsKey(name)) {
registeredFeatures.get(name).accept(player, tag);
}
if (registeredFeatures.containsKey(name)) {
registeredFeatures.get(name).accept(player, tag);
}
}
@ProtocolHandler.PlayerJoin
public static void onPlayerJoin(@NotNull ServerPlayer player) {
if (LeavesConfig.protocol.bladeren.enable) {
CompoundTag tag = new CompoundTag();
LeavesFeatureSet.writeNBT(tag);
ProtocolUtils.sendPayloadPacket(player, new BladerenHelloPayload(PROTOCOL_VERSION, tag));
}
CompoundTag tag = new CompoundTag();
LeavesFeatureSet.writeNBT(tag);
ProtocolUtils.sendPayloadPacket(player, new BladerenHelloPayload(PROTOCOL_VERSION, tag));
}
public static void registerFeature(String name, BiConsumer<ServerPlayer, CompoundTag> consumer) {
registeredFeatures.put(name, consumer);
}
@Override
public boolean isActive() {
return LeavesConfig.protocol.bladeren.enable;
}
public static class LeavesFeatureSet {
private static final Map<String, LeavesFeature> features = new HashMap<>();
@@ -108,43 +105,33 @@ public class BladerenProtocol {
}
}
public record BladerenFeatureModifyPayload(String name, CompoundTag nbt) implements LeavesCustomPayload<BladerenFeatureModifyPayload> {
public record BladerenFeatureModifyPayload(String name, CompoundTag nbt) implements LeavesCustomPayload {
@New
public BladerenFeatureModifyPayload(ResourceLocation location, FriendlyByteBuf buf) {
this(buf.readUtf(), buf.readNbt());
}
@ID
private static final ResourceLocation FEATURE_MODIFY_ID = id("feature_modify");
@Override
public void write(@NotNull FriendlyByteBuf buf) {
buf.writeUtf(name);
buf.writeNbt(nbt);
}
@Override
@NotNull
public ResourceLocation id() {
return FEATURE_MODIFY_ID;
}
@Codec
private static final StreamCodec<FriendlyByteBuf, BladerenFeatureModifyPayload> CODEC = StreamCodec.of(
(buf, payload) -> {
buf.writeUtf(payload.name());
buf.writeNbt(payload.nbt());
},
buffer -> new BladerenFeatureModifyPayload(buffer.readUtf(), buffer.readNbt())
);
}
public record BladerenHelloPayload(String version, CompoundTag nbt) implements LeavesCustomPayload<BladerenHelloPayload> {
public record BladerenHelloPayload(String version, CompoundTag nbt) implements LeavesCustomPayload {
@New
public BladerenHelloPayload(ResourceLocation location, @NotNull FriendlyByteBuf buf) {
this(buf.readUtf(64), buf.readNbt());
}
@ID
private static final ResourceLocation HELLO_ID = id("hello");
@Override
public void write(@NotNull FriendlyByteBuf buf) {
buf.writeUtf(version);
buf.writeNbt(nbt);
}
@Override
@NotNull
public ResourceLocation id() {
return HELLO_ID;
}
@Codec
private static final StreamCodec<FriendlyByteBuf, BladerenHelloPayload> CODEC = StreamCodec.of(
(buf, payload) -> {
buf.writeUtf(payload.version());
buf.writeNbt(payload.nbt());
},
buffer -> new BladerenHelloPayload(buffer.readUtf(64), buffer.readNbt())
);
}
}
}

View File

@@ -15,13 +15,11 @@ import java.util.Arrays;
import java.util.List;
import java.util.OptionalDouble;
@LeavesProtocol(namespace = "bladeren")
public class MsptSyncProtocol {
@LeavesProtocol.Register(namespace = "bladeren")
public class MsptSyncProtocol implements LeavesProtocol {
public static final String PROTOCOL_ID = "bladeren";
private static final ResourceLocation MSPT_SYNC = id("mspt_sync");
private static final List<ServerPlayer> players = new ArrayList<>();
@Contract("_ -> new")
@@ -32,7 +30,7 @@ public class MsptSyncProtocol {
@ProtocolHandler.Init
public static void init() {
BladerenProtocol.registerFeature("mspt_sync", (player, compoundTag) -> {
if (compoundTag.getString("Value").equals("true")) {
if (compoundTag.getStringOr("Value", "").equals("true")) {
onPlayerSubmit(player);
} else {
onPlayerLoggedOut(player);
@@ -42,36 +40,37 @@ public class MsptSyncProtocol {
@ProtocolHandler.PlayerLeave
public static void onPlayerLoggedOut(@NotNull ServerPlayer player) {
if (LeavesConfig.protocol.bladeren.msptSyncProtocol) {
players.remove(player);
}
players.remove(player);
}
@ProtocolHandler.Ticker
public static void tick() {
if (LeavesConfig.protocol.bladeren.msptSyncProtocol) {
if (players.isEmpty()) {
return;
}
MinecraftServer server = MinecraftServer.getServer();
if (server.getTickCount() % LeavesConfig.protocol.bladeren.msptSyncTickInterval == 0) {
OptionalDouble msptArr = Arrays.stream(server.getTickTimesNanos()).average();
if (msptArr.isPresent()) {
double mspt = msptArr.getAsDouble() * 1.0E-6D;
double tps = 1000.0D / Math.max(mspt, 50);
players.forEach(player -> ProtocolUtils.sendPayloadPacket(player, MSPT_SYNC, buf -> {
buf.writeDouble(mspt);
buf.writeDouble(tps);
}));
}
}
if (players.isEmpty()) {
return;
}
MinecraftServer server = MinecraftServer.getServer();
OptionalDouble msptArr = Arrays.stream(server.getTickTimesNanos()).average();
if (msptArr.isPresent()) {
double mspt = msptArr.getAsDouble() * 1.0E-6D;
double tps = 1000.0D / Math.max(mspt, 50);
players.forEach(player -> ProtocolUtils.sendBytebufPacket(player, MSPT_SYNC, buf -> {
buf.writeDouble(mspt);
buf.writeDouble(tps);
}));
}
}
public static void onPlayerSubmit(@NotNull ServerPlayer player) {
if (LeavesConfig.protocol.bladeren.msptSyncProtocol) {
players.add(player);
}
players.add(player);
}
@Override
public int tickerInterval(String tickerID) {
return LeavesConfig.protocol.bladeren.msptSyncTickInterval;
}
@Override
public boolean isActive() {
return LeavesConfig.protocol.bladeren.msptSyncProtocol;
}
}

View File

@@ -1,16 +0,0 @@
package org.leavesmc.leaves.protocol.chatimage;
public class ChatImageIndex {
public int index;
public int total;
public String url;
public String bytes;
public ChatImageIndex(int index, int total, String url, String bytes) {
this.index = index;
this.total = total;
this.url = url;
this.bytes = bytes;
}
}

View File

@@ -16,8 +16,8 @@ import java.util.UUID;
import static org.leavesmc.leaves.protocol.chatimage.ServerBlockCache.SERVER_BLOCK_CACHE;
@LeavesProtocol(namespace = "chatimage")
public class ChatImageProtocol {
@LeavesProtocol.Register(namespace = "chatimage")
public class ChatImageProtocol implements LeavesProtocol {
public static final String PROTOCOL_ID = "chatimage";
public static final Gson gson = new Gson();
@@ -27,12 +27,8 @@ public class ChatImageProtocol {
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
}
@ProtocolHandler.PayloadReceiver(payload = FileChannelPayload.class, payloadId = "get_file_channel")
public static void serverFileChannelReceived(ServerPlayer player, FileChannelPayload payload) {
if (!LeavesConfig.protocol.chatImageProtocol) {
return;
}
@ProtocolHandler.PayloadReceiver(payload = FileChannelPayload.class)
public void serverFileChannelReceived(ServerPlayer player, FileChannelPayload payload) {
MinecraftServer server = MinecraftServer.getServer();
String res = payload.message();
ChatImageIndex title = gson.fromJson(res, ChatImageIndex.class);
@@ -52,12 +48,8 @@ public class ChatImageProtocol {
}
}
@ProtocolHandler.PayloadReceiver(payload = FileInfoChannelPayload.class, payloadId = "file_info")
public static void serverGetFileChannelReceived(ServerPlayer player, FileInfoChannelPayload packet) {
if (!LeavesConfig.protocol.chatImageProtocol) {
return;
}
@ProtocolHandler.PayloadReceiver(payload = FileInfoChannelPayload.class)
public void serverGetFileChannelReceived(ServerPlayer player, FileInfoChannelPayload packet) {
String url = packet.message();
Map<Integer, String> list = SERVER_BLOCK_CACHE.getBlock(url);
if (list == null) {
@@ -69,4 +61,12 @@ public class ChatImageProtocol {
ProtocolUtils.sendPayloadPacket(player, new DownloadFileChannelPayload(entry.getValue()));
}
}
@Override
public boolean isActive() {
return LeavesConfig.protocol.chatImageProtocol;
}
public record ChatImageIndex(int index, int total, String url, String bytes) {
}
}

View File

@@ -1,32 +1,18 @@
package org.leavesmc.leaves.protocol.chatimage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
public record DownloadFileChannelPayload(String message) implements LeavesCustomPayload<DownloadFileChannelPayload> {
public record DownloadFileChannelPayload(String message) implements LeavesCustomPayload {
@ID
private static final ResourceLocation ID = ChatImageProtocol.id("download_file_channel");
private static final StreamCodec<RegistryFriendlyByteBuf, DownloadFileChannelPayload> CODEC =
StreamCodec.composite(ByteBufCodecs.STRING_UTF8, DownloadFileChannelPayload::message, DownloadFileChannelPayload::new);
@Override
public void write(FriendlyByteBuf buf) {
CODEC.encode(ProtocolUtils.decorate(buf), this);
}
@Override
public ResourceLocation id() {
return ID;
}
@New
public static DownloadFileChannelPayload create(ResourceLocation location, FriendlyByteBuf buf) {
return CODEC.decode(ProtocolUtils.decorate(buf));
}
@Codec
private static final StreamCodec<RegistryFriendlyByteBuf, DownloadFileChannelPayload> CODEC = StreamCodec.composite(
ByteBufCodecs.STRING_UTF8, DownloadFileChannelPayload::message, DownloadFileChannelPayload::new
);
}

View File

@@ -1,32 +1,18 @@
package org.leavesmc.leaves.protocol.chatimage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
public record FileChannelPayload(String message) implements LeavesCustomPayload<FileChannelPayload> {
public record FileChannelPayload(String message) implements LeavesCustomPayload {
@ID
private static final ResourceLocation ID = ChatImageProtocol.id("get_file_channel");
private static final StreamCodec<RegistryFriendlyByteBuf, FileChannelPayload> CODEC =
StreamCodec.composite(ByteBufCodecs.STRING_UTF8, FileChannelPayload::message, FileChannelPayload::new);
@Override
public void write(FriendlyByteBuf buf) {
CODEC.encode(ProtocolUtils.decorate(buf), this);
}
@Override
public ResourceLocation id() {
return ID;
}
@New
public static FileChannelPayload create(ResourceLocation location, FriendlyByteBuf buf) {
return CODEC.decode(ProtocolUtils.decorate(buf));
}
@Codec
private static final StreamCodec<RegistryFriendlyByteBuf, FileChannelPayload> CODEC = StreamCodec.composite(
ByteBufCodecs.STRING_UTF8, FileChannelPayload::message, FileChannelPayload::new
);
}

View File

@@ -1,32 +1,18 @@
package org.leavesmc.leaves.protocol.chatimage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
public record FileInfoChannelPayload(String message) implements LeavesCustomPayload<FileInfoChannelPayload> {
public record FileInfoChannelPayload(String message) implements LeavesCustomPayload {
@ID
private static final ResourceLocation ID = ChatImageProtocol.id("file_info");
private static final StreamCodec<RegistryFriendlyByteBuf, FileInfoChannelPayload> CODEC =
StreamCodec.composite(ByteBufCodecs.STRING_UTF8, FileInfoChannelPayload::message, FileInfoChannelPayload::new);
@Override
public void write(FriendlyByteBuf buf) {
CODEC.encode(ProtocolUtils.decorate(buf), this);
}
@Override
public ResourceLocation id() {
return ID;
}
@New
public static FileInfoChannelPayload create(ResourceLocation location, FriendlyByteBuf buf) {
return CODEC.decode(ProtocolUtils.decorate(buf));
}
@Codec
private static final StreamCodec<RegistryFriendlyByteBuf, FileInfoChannelPayload> CODEC = StreamCodec.composite(
ByteBufCodecs.STRING_UTF8, FileInfoChannelPayload::message, FileInfoChannelPayload::new
);
}

View File

@@ -19,15 +19,15 @@ public class ServerBlockCache {
public Cache<String, Map<Integer, String>> blockCache = CacheBuilder.newBuilder().expireAfterAccess(60, TimeUnit.SECONDS).build();
public Cache<String, Integer> fileCount = CacheBuilder.newBuilder().expireAfterAccess(60, TimeUnit.SECONDS).build();
public Map<Integer, String> createBlock(ChatImageIndex title, String imgBytes) {
public Map<Integer, String> createBlock(ChatImageProtocol.ChatImageIndex title, String imgBytes) {
try {
Map<Integer, String> blocks = this.blockCache.get(title.url, HashMap::new);
blocks.put(title.index, imgBytes);
this.blockCache.put(title.url, blocks);
this.fileCount.put(title.url, title.total);
Map<Integer, String> blocks = this.blockCache.get(title.url(), HashMap::new);
blocks.put(title.index(), imgBytes);
this.blockCache.put(title.url(), blocks);
this.fileCount.put(title.url(), title.total());
return blocks;
} catch (Exception e) {
LeavesLogger.LOGGER.warning("Failed to create block for title " + title.url + ": " + e);
LeavesLogger.LOGGER.warning("Failed to create block for title " + title.url() + ": " + e);
return null;
}
}

View File

@@ -1,6 +1,5 @@
package org.leavesmc.leaves.protocol.core;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
@@ -10,20 +9,22 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public interface LeavesCustomPayload<T extends LeavesCustomPayload<T>> extends CustomPacketPayload {
public interface LeavesCustomPayload extends CustomPacketPayload {
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@interface New {
}
void write(FriendlyByteBuf buf);
ResourceLocation id();
Type<? extends CustomPacketPayload> LEAVES_TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath("leaves", "custom_payload"));
@Override
@NotNull
default Type<T> type() {
return new CustomPacketPayload.Type<>(id());
default @NotNull Type<? extends CustomPacketPayload> type() {
return LEAVES_TYPE;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface ID {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Codec {
}
}

View File

@@ -5,8 +5,17 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface LeavesProtocol {
String[] namespace();
}
public interface LeavesProtocol {
boolean isActive();
default int tickerInterval(String tickerID) {
return 1;
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Register {
String namespace();
}
}

View File

@@ -1,20 +1,23 @@
package org.leavesmc.leaves.protocol.core;
import com.google.common.collect.ImmutableSet;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import org.apache.commons.lang.ArrayUtils;
import org.bukkit.event.player.PlayerKickEvent;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.LeavesLogger;
import org.leavesmc.leaves.protocol.core.invoker.BytebufReceiverInvokerHolder;
import org.leavesmc.leaves.protocol.core.invoker.EmptyInvokerHolder;
import org.leavesmc.leaves.protocol.core.invoker.InitInvokerHolder;
import org.leavesmc.leaves.protocol.core.invoker.MinecraftRegisterInvokerHolder;
import org.leavesmc.leaves.protocol.core.invoker.PayloadReceiverInvokerHolder;
import org.leavesmc.leaves.protocol.core.invoker.PlayerInvokerHolder;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.JarURLConnection;
@@ -22,8 +25,6 @@ import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
@@ -35,266 +36,288 @@ import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
// TODO refactor
public class LeavesProtocolManager {
private static final Class<?>[] PAYLOAD_PARAMETER_TYPES = {ResourceLocation.class, FriendlyByteBuf.class};
private static final LeavesLogger LOGGER = LeavesLogger.LOGGER;
private static final Map<LeavesProtocol, Map<ProtocolHandler.PayloadReceiver, Executable>> KNOWN_TYPES = new HashMap<>();
private static final Map<LeavesProtocol, Map<ProtocolHandler.PayloadReceiver, Method>> KNOW_RECEIVERS = new HashMap<>();
private static Set<ResourceLocation> ALL_KNOWN_ID = new HashSet<>();
private static final Map<Class<? extends LeavesCustomPayload>, PayloadReceiverInvokerHolder> PAYLOAD_RECEIVERS = new HashMap<>();
private static final Map<Class<? extends LeavesCustomPayload>, ResourceLocation> IDS = new HashMap<>();
private static final Map<Class<? extends LeavesCustomPayload>, StreamCodec<? super RegistryFriendlyByteBuf, LeavesCustomPayload>> CODECS = new HashMap<>();
private static final Map<ResourceLocation, StreamCodec<? super RegistryFriendlyByteBuf, LeavesCustomPayload>> ID2CODEC = new HashMap<>();
private static final List<Method> TICKERS = new ArrayList<>();
private static final List<Method> PLAYER_JOIN = new ArrayList<>();
private static final List<Method> PLAYER_LEAVE = new ArrayList<>();
private static final List<Method> RELOAD_SERVER = new ArrayList<>();
private static final Map<LeavesProtocol, Map<ProtocolHandler.MinecraftRegister, Method>> MINECRAFT_REGISTER = new HashMap<>();
private static final Map<String, BytebufReceiverInvokerHolder> STRICT_BYTEBUF_RECEIVERS = new HashMap<>();
private static final Map<String, BytebufReceiverInvokerHolder> NAMESPACED_BYTEBUF_RECEIVERS = new HashMap<>();
private static final List<BytebufReceiverInvokerHolder> GENERIC_BYTEBUF_RECEIVERS = new ArrayList<>();
private static final Map<String, MinecraftRegisterInvokerHolder> STRICT_MINECRAFT_REGISTER = new HashMap<>();
private static final Map<String, MinecraftRegisterInvokerHolder> NAMESPACED_MINECRAFT_REGISTER = new HashMap<>();
private static final List<MinecraftRegisterInvokerHolder> WILD_MINECRAFT_REGISTER = new ArrayList<>();
private static final List<EmptyInvokerHolder<ProtocolHandler.Ticker>> TICKERS = new ArrayList<>();
private static final List<PlayerInvokerHolder<ProtocolHandler.PlayerJoin>> PLAYER_JOIN = new ArrayList<>();
private static final List<PlayerInvokerHolder<ProtocolHandler.PlayerLeave>> PLAYER_LEAVE = new ArrayList<>();
private static final List<EmptyInvokerHolder<ProtocolHandler.ReloadServer>> RELOAD_SERVER = new ArrayList<>();
private static final List<EmptyInvokerHolder<ProtocolHandler.ReloadDataPack>> RELOAD_DATAPACK = new ArrayList<>();
@SuppressWarnings("unchecked")
public static void init() {
for (Class<?> clazz : getClasses("org.leavesmc.leaves.protocol")) {
final LeavesProtocol protocol = clazz.getAnnotation(LeavesProtocol.class);
if (protocol != null) {
Set<Method> methods;
try {
Method[] publicMethods = clazz.getMethods();
Method[] privateMethods = clazz.getDeclaredMethods();
methods = new HashSet<>(publicMethods.length + privateMethods.length, 1.0f);
Collections.addAll(methods, publicMethods);
Collections.addAll(methods, privateMethods);
} catch (NoClassDefFoundError error) {
LOGGER.severe("Failed to load class " + clazz.getName() + " due to missing dependencies, " + error.getCause() + ": " + error.getMessage());
return;
}
Map<ProtocolHandler.PayloadReceiver, Executable> map = KNOWN_TYPES.getOrDefault(protocol, new HashMap<>());
for (final Method method : methods) {
if (method.isBridge() || method.isSynthetic() || !Modifier.isStatic(method.getModifiers())) {
if (LeavesCustomPayload.class.isAssignableFrom(clazz) && !clazz.equals(LeavesCustomPayload.class)) {
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
if (!Modifier.isStatic(field.getModifiers())) {
continue;
}
method.setAccessible(true);
final ProtocolHandler.Init init = method.getAnnotation(ProtocolHandler.Init.class);
if (init != null) {
try {
method.invoke(null);
} catch (InvocationTargetException | IllegalAccessException exception) {
LOGGER.severe("Failed to invoke init method " + method.getName() + " in " + clazz.getName() + ", " + exception.getCause() + ": " + exception.getMessage());
}
continue;
}
final ProtocolHandler.PayloadReceiver receiver = method.getAnnotation(ProtocolHandler.PayloadReceiver.class);
if (receiver != null) {
try {
boolean found = false;
for (Method payloadMethod : receiver.payload().getDeclaredMethods()) {
if (payloadMethod.isAnnotationPresent(LeavesCustomPayload.New.class)) {
if (Arrays.equals(payloadMethod.getParameterTypes(), PAYLOAD_PARAMETER_TYPES) && payloadMethod.getReturnType() == receiver.payload() && Modifier.isStatic(payloadMethod.getModifiers())) {
payloadMethod.setAccessible(true);
map.put(receiver, payloadMethod);
found = true;
break;
}
}
}
if (!found) {
Constructor<? extends LeavesCustomPayload<?>> constructor = receiver.payload().getConstructor(PAYLOAD_PARAMETER_TYPES);
if (constructor.isAnnotationPresent(LeavesCustomPayload.New.class)) {
constructor.setAccessible(true);
map.put(receiver, constructor);
} else {
throw new NoSuchMethodException();
}
}
} catch (NoSuchMethodException exception) {
LOGGER.severe("Failed to find constructor for " + receiver.payload().getName() + ", " + exception.getCause() + ": " + exception.getMessage());
continue;
}
if (!KNOW_RECEIVERS.containsKey(protocol)) {
KNOW_RECEIVERS.put(protocol, new HashMap<>());
}
KNOW_RECEIVERS.get(protocol).put(receiver, method);
continue;
}
final ProtocolHandler.Ticker ticker = method.getAnnotation(ProtocolHandler.Ticker.class);
if (ticker != null) {
TICKERS.add(method);
continue;
}
final ProtocolHandler.PlayerJoin playerJoin = method.getAnnotation(ProtocolHandler.PlayerJoin.class);
if (playerJoin != null) {
PLAYER_JOIN.add(method);
continue;
}
final ProtocolHandler.PlayerLeave playerLeave = method.getAnnotation(ProtocolHandler.PlayerLeave.class);
if (playerLeave != null) {
PLAYER_LEAVE.add(method);
continue;
}
final ProtocolHandler.ReloadServer reloadServer = method.getAnnotation(ProtocolHandler.ReloadServer.class);
if (reloadServer != null) {
RELOAD_SERVER.add(method);
continue;
}
final ProtocolHandler.MinecraftRegister minecraftRegister = method.getAnnotation(ProtocolHandler.MinecraftRegister.class);
if (minecraftRegister != null) {
if (!MINECRAFT_REGISTER.containsKey(protocol)) {
MINECRAFT_REGISTER.put(protocol, new HashMap<>());
}
MINECRAFT_REGISTER.get(protocol).put(minecraftRegister, method);
}
}
KNOWN_TYPES.put(protocol, map);
}
}
for (LeavesProtocol protocol : KNOWN_TYPES.keySet()) {
Map<ProtocolHandler.PayloadReceiver, Executable> map = KNOWN_TYPES.get(protocol);
for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) {
if (receiver.sendFabricRegister() && !receiver.ignoreId()) {
for (String payloadId : receiver.payloadId()) {
for (String namespace : protocol.namespace()) {
ALL_KNOWN_ID.add(ResourceLocation.tryBuild(namespace, payloadId));
}
}
}
}
}
ALL_KNOWN_ID = ImmutableSet.copyOf(ALL_KNOWN_ID);
}
public static LeavesCustomPayload<?> decode(ResourceLocation id, FriendlyByteBuf buf) {
for (LeavesProtocol protocol : KNOWN_TYPES.keySet()) {
if (!ArrayUtils.contains(protocol.namespace(), id.getNamespace())) {
continue;
}
Map<ProtocolHandler.PayloadReceiver, Executable> map = KNOWN_TYPES.get(protocol);
for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) {
if (receiver.ignoreId() || ArrayUtils.contains(receiver.payloadId(), id.getPath())) {
try {
if (map.get(receiver) instanceof Constructor<?> constructor) {
return (LeavesCustomPayload<?>) constructor.newInstance(id, buf);
} else if (map.get(receiver) instanceof Method method) {
return (LeavesCustomPayload<?>) method.invoke(null, id, buf);
final LeavesCustomPayload.ID id = field.getAnnotation(LeavesCustomPayload.ID.class);
if (id != null && field.getType().equals(ResourceLocation.class)) {
IDS.put((Class<? extends LeavesCustomPayload>) clazz, (ResourceLocation) field.get(null));
}
} catch (InvocationTargetException | InstantiationException | IllegalAccessException exception) {
LOGGER.warning("Failed to create payload for " + id + " in " + ArrayUtils.toString(protocol.namespace()) + ", " + exception.getCause() + ": " + exception.getMessage());
buf.readBytes(buf.readableBytes());
return new ErrorPayload(id, protocol.namespace(), receiver.payloadId());
final LeavesCustomPayload.Codec codec = field.getAnnotation(LeavesCustomPayload.Codec.class);
if (codec != null && field.getType().equals(StreamCodec.class)) {
CODECS.put((Class<? extends LeavesCustomPayload>) clazz, (StreamCodec<? super RegistryFriendlyByteBuf, LeavesCustomPayload>) field.get(null));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
return null;
}
public static void handlePayload(ServerPlayer player, LeavesCustomPayload<?> payload) {
if (payload instanceof ErrorPayload errorPayload) {
player.connection.disconnect(Component.literal("Payload " + Arrays.toString(errorPayload.packetID) + " from " + Arrays.toString(errorPayload.protocolID) + " error"), PlayerKickEvent.Cause.INVALID_PAYLOAD);
return;
}
for (LeavesProtocol protocol : KNOW_RECEIVERS.keySet()) {
if (!ArrayUtils.contains(protocol.namespace(), payload.type().id().getNamespace())) {
continue;
}
Map<ProtocolHandler.PayloadReceiver, Method> map = KNOW_RECEIVERS.get(protocol);
for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) {
if (payload.getClass() == receiver.payload()) {
if (receiver.ignoreId() || ArrayUtils.contains(receiver.payloadId(), payload.type().id().getPath())) {
try {
map.get(receiver).invoke(null, player, payload);
} catch (InvocationTargetException | IllegalAccessException exception) {
LOGGER.warning("Failed to handle payload " + payload.type().id() + " in " + ArrayUtils.toString(protocol.namespace()) + ", " + exception.getCause() + ": " + exception.getMessage());
final LeavesProtocol.Register register = clazz.getAnnotation(LeavesProtocol.Register.class);
if (register == null) {
continue;
}
LeavesProtocol protocol;
try {
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
protocol = (LeavesProtocol) constructor.newInstance();
} catch (Throwable throwable) {
LOGGER.severe("Failed to load class " + clazz.getName() + ". " + throwable);
return;
}
for (final Method method : clazz.getDeclaredMethods()) {
if (method.isBridge() || method.isSynthetic()) {
continue;
}
method.setAccessible(true);
final ProtocolHandler.Init init = method.getAnnotation(ProtocolHandler.Init.class);
if (init != null) {
InitInvokerHolder holder = new InitInvokerHolder(protocol, method, init);
try {
holder.invoke();
} catch (RuntimeException exception) {
LOGGER.severe("Failed to invoke init method " + method.getName() + " in " + clazz.getName() + ", " + exception.getCause() + ": " + exception.getMessage());
}
continue;
}
final ProtocolHandler.PayloadReceiver payloadReceiver = method.getAnnotation(ProtocolHandler.PayloadReceiver.class);
if (payloadReceiver != null) {
PAYLOAD_RECEIVERS.put(payloadReceiver.payload(), new PayloadReceiverInvokerHolder(protocol, method, payloadReceiver));
continue;
}
final ProtocolHandler.BytebufReceiver bytebufReceiver = method.getAnnotation(ProtocolHandler.BytebufReceiver.class);
if (bytebufReceiver != null) {
String key = bytebufReceiver.key();
BytebufReceiverInvokerHolder holder = new BytebufReceiverInvokerHolder(protocol, method, bytebufReceiver);
if (bytebufReceiver.onlyNamespace()) {
NAMESPACED_BYTEBUF_RECEIVERS.put(key.isEmpty() ? register.namespace() : key, holder);
} else {
if (key.isEmpty()) {
GENERIC_BYTEBUF_RECEIVERS.add(holder);
} else {
if (key.contains(":")) {
STRICT_BYTEBUF_RECEIVERS.put(key, holder);
} else {
STRICT_BYTEBUF_RECEIVERS.put(register.namespace() + ":" + key, holder);
}
}
}
continue;
}
final ProtocolHandler.Ticker ticker = method.getAnnotation(ProtocolHandler.Ticker.class);
if (ticker != null) {
TICKERS.add(new EmptyInvokerHolder<>(protocol, method, ticker));
continue;
}
final ProtocolHandler.PlayerJoin playerJoin = method.getAnnotation(ProtocolHandler.PlayerJoin.class);
if (playerJoin != null) {
PLAYER_JOIN.add(new PlayerInvokerHolder<>(protocol, method, playerJoin));
continue;
}
final ProtocolHandler.PlayerLeave playerLeave = method.getAnnotation(ProtocolHandler.PlayerLeave.class);
if (playerLeave != null) {
PLAYER_LEAVE.add(new PlayerInvokerHolder<>(protocol, method, playerLeave));
continue;
}
final ProtocolHandler.ReloadServer reloadServer = method.getAnnotation(ProtocolHandler.ReloadServer.class);
if (reloadServer != null) {
RELOAD_SERVER.add(new EmptyInvokerHolder<>(protocol, method, reloadServer));
continue;
}
final ProtocolHandler.ReloadDataPack reloadDataPack = method.getAnnotation(ProtocolHandler.ReloadDataPack.class);
if (reloadDataPack != null) {
RELOAD_DATAPACK.add(new EmptyInvokerHolder<>(protocol, method, reloadDataPack));
continue;
}
final ProtocolHandler.MinecraftRegister minecraftRegister = method.getAnnotation(ProtocolHandler.MinecraftRegister.class);
if (minecraftRegister != null) {
String key = minecraftRegister.key();
MinecraftRegisterInvokerHolder holder = new MinecraftRegisterInvokerHolder(protocol, method, minecraftRegister);
if (minecraftRegister.onlyNamespace()) {
NAMESPACED_MINECRAFT_REGISTER.put(key.isEmpty() ? register.namespace() : key, holder);
} else {
if (key.isEmpty()) {
WILD_MINECRAFT_REGISTER.add(holder);
} else {
if (key.contains(":")) {
STRICT_MINECRAFT_REGISTER.put(key, holder);
} else {
STRICT_MINECRAFT_REGISTER.put(register.namespace() + ":" + key, holder);
}
}
}
}
}
}
for (var idInfo : IDS.entrySet()) {
var codec = CODECS.get(idInfo.getKey());
if (codec == null) {
throw new IllegalArgumentException("Payload " + idInfo.getKey() + " is not configured correctly");
}
ID2CODEC.put(idInfo.getValue(), codec);
}
}
public static void handleTick() {
if (!TICKERS.isEmpty()) {
try {
for (Method method : TICKERS) {
method.invoke(null);
}
} catch (InvocationTargetException | IllegalAccessException exception) {
LOGGER.warning("Failed to tick, " + exception.getCause() + ": " + exception.getMessage());
public static LeavesCustomPayload decode(ResourceLocation location, FriendlyByteBuf buf) {
var codec = ID2CODEC.get(location);
if (codec == null) {
return null;
}
return codec.decode(ProtocolUtils.decorate(buf));
}
public static void encode(FriendlyByteBuf buf, LeavesCustomPayload payload) {
var location = IDS.get(payload.getClass());
var codec = CODECS.get(payload.getClass());
if (location == null || codec == null) {
throw new IllegalArgumentException("Payload " + payload.getClass() + " is not configured correctly " + location + " " + codec);
}
buf.writeResourceLocation(location);
codec.encode(ProtocolUtils.decorate(buf), payload);
}
public static void handlePayload(ServerPlayer player, LeavesCustomPayload payload) {
PayloadReceiverInvokerHolder holder;
if ((holder = PAYLOAD_RECEIVERS.get(payload.getClass())) != null) {
holder.invoke(player, payload);
}
}
public static boolean handleBytebuf(ServerPlayer player, ResourceLocation location, ByteBuf buf) {
RegistryFriendlyByteBuf buf1 = ProtocolUtils.decorate(buf);
BytebufReceiverInvokerHolder holder;
if ((holder = STRICT_BYTEBUF_RECEIVERS.get(location.toString())) != null) {
holder.invoke(player, buf1);
return true;
}
if ((holder = NAMESPACED_BYTEBUF_RECEIVERS.get(location.getNamespace())) != null) {
if (holder.invoke(player, buf1)) {
return true;
}
}
for (var holder1 : GENERIC_BYTEBUF_RECEIVERS) {
if (holder1.invoke(player, buf1)) {
return true;
}
}
return false;
}
public static void handleTick(long tickCount) {
for (var tickerInfo : TICKERS) {
if (tickCount % tickerInfo.owner().tickerInterval(tickerInfo.handler().tickerId()) == 0) {
tickerInfo.invoke();
}
}
}
public static void handlePlayerJoin(ServerPlayer player) {
if (!PLAYER_JOIN.isEmpty()) {
try {
for (Method method : PLAYER_JOIN) {
method.invoke(null, player);
}
} catch (InvocationTargetException | IllegalAccessException exception) {
LOGGER.warning("Failed to handle player join, " + exception.getCause() + ": " + exception.getMessage());
}
sendKnownId(player);
for (var join : PLAYER_JOIN) {
join.invoke(player);
}
ProtocolUtils.sendPayloadPacket(player, new FabricRegisterPayload(ALL_KNOWN_ID));
}
public static void handlePlayerLeave(ServerPlayer player) {
if (!PLAYER_LEAVE.isEmpty()) {
try {
for (Method method : PLAYER_LEAVE) {
method.invoke(null, player);
}
} catch (InvocationTargetException | IllegalAccessException exception) {
LOGGER.warning("Failed to handle player leave, " + exception.getCause() + ": " + exception.getMessage());
}
for (var leave : PLAYER_LEAVE) {
leave.invoke(player);
}
}
public static void handleServerReload() {
if (!RELOAD_SERVER.isEmpty()) {
try {
for (Method method : RELOAD_SERVER) {
method.invoke(null);
}
} catch (InvocationTargetException | IllegalAccessException exception) {
LOGGER.warning("Failed to handle server reload, " + exception.getCause() + ": " + exception.getMessage());
}
for (var reload : RELOAD_SERVER) {
reload.invoke();
}
}
public static void handleDataPackReload() {
for (var reload : RELOAD_DATAPACK) {
reload.invoke();
}
}
public static void handleMinecraftRegister(String channelId, ServerPlayer player) {
for (LeavesProtocol protocol : MINECRAFT_REGISTER.keySet()) {
String[] channel = channelId.split(":");
if (!ArrayUtils.contains(protocol.namespace(), channel[0])) {
continue;
}
Map<ProtocolHandler.MinecraftRegister, Method> map = MINECRAFT_REGISTER.get(protocol);
for (ProtocolHandler.MinecraftRegister register : map.keySet()) {
if (register.ignoreId() || ArrayUtils.contains(register.channelId(), channel[1])) {
try {
map.get(register).invoke(null, player, channel[1]);
} catch (InvocationTargetException | IllegalAccessException exception) {
LOGGER.warning("Failed to handle minecraft register, " + exception.getCause() + ": " + exception.getMessage());
}
}
}
ResourceLocation location = ResourceLocation.tryParse(channelId);
if (location == null) {
return;
}
for (var wildHolder : WILD_MINECRAFT_REGISTER) {
wildHolder.invoke(player, location);
}
MinecraftRegisterInvokerHolder holder;
if ((holder = STRICT_MINECRAFT_REGISTER.get(location.toString())) != null) {
holder.invoke(player, location);
}
if ((holder = NAMESPACED_MINECRAFT_REGISTER.get(location.getNamespace())) != null) {
holder.invoke(player, location);
}
}
private static void sendKnownId(ServerPlayer player) {
Set<String> set = new HashSet<>();
PAYLOAD_RECEIVERS.forEach((clazz, holder) -> {
if (holder.owner().isActive()) {
set.add(IDS.get(clazz).toString());
}
});
STRICT_BYTEBUF_RECEIVERS.forEach((key, holder) -> {
if (holder.owner().isActive()) {
set.add(key);
}
});
ProtocolUtils.sendBytebufPacket(player, ResourceLocation.fromNamespaceAndPath("minecraft", "register"), buf -> {
ResourceLocation channel;
for (Iterator<String> var3 = set.iterator(); var3.hasNext(); buf.writeBytes(channel.toString().getBytes(StandardCharsets.US_ASCII))) {
channel = ResourceLocation.parse(var3.next());
buf.writeByte(0);
}
buf.writerIndex(Math.max(buf.writerIndex() - 1, 0));
});
}
public static Set<Class<?>> getClasses(String pack) {
@@ -371,63 +394,4 @@ public class LeavesProtocolManager {
}
}
}
public record ErrorPayload(ResourceLocation id, String[] protocolID, String[] packetID) implements LeavesCustomPayload<ErrorPayload> {
@Override
public void write(@NotNull FriendlyByteBuf buf) {
}
}
public record EmptyPayload(ResourceLocation id) implements LeavesCustomPayload<EmptyPayload> {
@New
public EmptyPayload(ResourceLocation location, FriendlyByteBuf buf) {
this(location);
}
@Override
public void write(@NotNull FriendlyByteBuf buf) {
}
}
public record LeavesPayload(FriendlyByteBuf data, ResourceLocation id) implements LeavesCustomPayload<LeavesPayload> {
@New
public LeavesPayload(ResourceLocation location, FriendlyByteBuf buf) {
this(new FriendlyByteBuf(buf.readBytes(buf.readableBytes())), location);
}
@Override
public void write(FriendlyByteBuf buf) {
buf.writeBytes(data);
}
}
public record FabricRegisterPayload(Set<ResourceLocation> channels) implements LeavesCustomPayload<FabricRegisterPayload> {
public static final ResourceLocation CHANNEL = ResourceLocation.withDefaultNamespace("register");
@New
public FabricRegisterPayload(ResourceLocation location, FriendlyByteBuf buf) {
this(buf.readCollection(HashSet::new, FriendlyByteBuf::readResourceLocation));
}
@Override
public void write(FriendlyByteBuf buf) {
boolean first = true;
ResourceLocation channel;
for (Iterator<ResourceLocation> var3 = this.channels.iterator(); var3.hasNext(); buf.writeBytes(channel.toString().getBytes(StandardCharsets.US_ASCII))) {
channel = var3.next();
if (first) {
first = false;
} else {
buf.writeByte(0);
}
}
}
@Override
public ResourceLocation id() {
return CHANNEL;
}
}
}
}

View File

@@ -15,19 +15,21 @@ public class ProtocolHandler {
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PayloadReceiver {
Class<? extends LeavesCustomPayload<?>> payload();
Class<? extends LeavesCustomPayload> payload();
}
String[] payloadId() default "";
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BytebufReceiver {
String key() default "";
boolean ignoreId() default false;
boolean sendFabricRegister() default true;
boolean onlyNamespace() default false;
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ticker {
int delay() default 0;
String tickerId() default "";
}
@Target(ElementType.METHOD)
@@ -48,8 +50,13 @@ public class ProtocolHandler {
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MinecraftRegister {
String[] channelId() default "";
String key() default "";
boolean ignoreId() default false;
boolean onlyNamespace() default false;
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReloadDataPack {
}
}

View File

@@ -1,11 +1,13 @@
package org.leavesmc.leaves.protocol.core;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.papermc.paper.ServerBuildInfo;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
@@ -16,34 +18,24 @@ import java.util.function.Function;
public class ProtocolUtils {
private static final Function<ByteBuf, RegistryFriendlyByteBuf> bufDecorator = RegistryFriendlyByteBuf.decorator(MinecraftServer.getServer().registryAccess());
private static final Function<ByteBuf, RegistryFriendlyByteBuf> bufDecorator = buf -> buf instanceof RegistryFriendlyByteBuf registry ? registry : new RegistryFriendlyByteBuf(buf, MinecraftServer.getServer().registryAccess());
public static String buildProtocolVersion(String protocol) {
return protocol + "-leaves-" + ServerBuildInfo.buildInfo().asString(ServerBuildInfo.StringRepresentation.VERSION_SIMPLE);
}
public static void sendEmptyPayloadPacket(ServerPlayer player, ResourceLocation id) {
player.connection.send(new ClientboundCustomPayloadPacket(new LeavesProtocolManager.EmptyPayload(id)));
public static void sendEmptyPacket(ServerPlayer player, ResourceLocation id) {
player.internalConnection.send(new ClientboundCustomPayloadPacket(new DiscardedPayload(id, null)));
}
@SuppressWarnings("all")
public static void sendPayloadPacket(@NotNull ServerPlayer player, ResourceLocation id, Consumer<FriendlyByteBuf> consumer) {
player.connection.send(new ClientboundCustomPayloadPacket(new LeavesCustomPayload() {
@Override
public void write(@NotNull FriendlyByteBuf buf) {
consumer.accept(buf);
}
@Override
@NotNull
public ResourceLocation id() {
return id;
}
}));
public static void sendBytebufPacket(@NotNull ServerPlayer player, ResourceLocation id, Consumer<? super RegistryFriendlyByteBuf> consumer) {
RegistryFriendlyByteBuf buf = decorate(Unpooled.buffer());
consumer.accept(buf);
player.internalConnection.send(new ClientboundCustomPayloadPacket(new DiscardedPayload(id, ByteBufUtil.getBytes(buf))));
}
public static void sendPayloadPacket(ServerPlayer player, CustomPacketPayload payload) {
player.connection.send(new ClientboundCustomPayloadPacket(payload));
player.internalConnection.send(new ClientboundCustomPayloadPacket(payload));
}
public static RegistryFriendlyByteBuf decorate(ByteBuf buf) {

View File

@@ -0,0 +1,70 @@
package org.leavesmc.leaves.protocol.core.invoker;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public abstract class AbstractInvokerHolder<T> {
protected final LeavesProtocol owner;
protected final Method invoker;
protected final T handler;
protected final Class<?> returnType;
protected final Class<?>[] parameterTypes;
protected AbstractInvokerHolder(LeavesProtocol owner, Method invoker, T handler, @Nullable Class<?> returnType, @NotNull Class<?>... parameterTypes) {
this.owner = owner;
this.invoker = invoker;
this.handler = handler;
this.returnType = returnType;
this.parameterTypes = parameterTypes;
validateMethodSignature();
}
protected void validateMethodSignature() {
if (returnType != null && !returnType.isAssignableFrom(invoker.getReturnType())) {
throw new IllegalArgumentException("Return type mismatch in " + owner.getClass().getName() + "#" + invoker.getName() +
": expected " + returnType.getName() + " but found " + invoker.getReturnType().getName());
}
Class<?>[] methodParamTypes = invoker.getParameterTypes();
if (methodParamTypes.length != parameterTypes.length) {
throw new IllegalArgumentException("Parameter count mismatch in " + owner.getClass().getName() + "#" + invoker.getName() +
": expected " + parameterTypes.length + " but found " + methodParamTypes.length);
}
for (int i = 0; i < parameterTypes.length; i++) {
if (!parameterTypes[i].isAssignableFrom(methodParamTypes[i])) {
throw new IllegalArgumentException("Parameter type mismatch in " + owner.getClass().getName() + "#" + invoker.getName() +
" at index " + i + ": expected " + parameterTypes[i].getName() + " but found " + methodParamTypes[i].getName());
}
}
}
public LeavesProtocol owner() {
return owner;
}
public T handler() {
return handler;
}
protected Object invoke0(boolean force, Object... args) {
if (!force && !owner.isActive()) {
return null;
}
try {
if (Modifier.isStatic(invoker.getModifiers())) {
return invoker.invoke(null, args);
} else {
return invoker.invoke(owner, args);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,18 @@
package org.leavesmc.leaves.protocol.core.invoker;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import java.lang.reflect.Method;
public class BytebufReceiverInvokerHolder extends AbstractInvokerHolder<ProtocolHandler.BytebufReceiver> {
public BytebufReceiverInvokerHolder(LeavesProtocol owner, Method invoker, ProtocolHandler.BytebufReceiver handler) {
super(owner, invoker, handler, null, ServerPlayer.class, FriendlyByteBuf.class);
}
public boolean invoke(ServerPlayer player, FriendlyByteBuf buf) {
return invoke0(false, player, buf) instanceof Boolean b && b;
}
}

View File

@@ -0,0 +1,15 @@
package org.leavesmc.leaves.protocol.core.invoker;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import java.lang.reflect.Method;
public class EmptyInvokerHolder<T> extends AbstractInvokerHolder<T> {
public EmptyInvokerHolder(LeavesProtocol owner, Method invoker, T handler) {
super(owner, invoker, handler, null);
}
public void invoke() {
invoke0(false);
}
}

View File

@@ -0,0 +1,16 @@
package org.leavesmc.leaves.protocol.core.invoker;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import java.lang.reflect.Method;
public class InitInvokerHolder extends AbstractInvokerHolder<ProtocolHandler.Init> {
public InitInvokerHolder(LeavesProtocol owner, Method invoker, ProtocolHandler.Init handler) {
super(owner, invoker, handler, null);
}
public void invoke() {
invoke0(true);
}
}

View File

@@ -0,0 +1,18 @@
package org.leavesmc.leaves.protocol.core.invoker;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import java.lang.reflect.Method;
public class MinecraftRegisterInvokerHolder extends AbstractInvokerHolder<ProtocolHandler.MinecraftRegister> {
public MinecraftRegisterInvokerHolder(LeavesProtocol owner, Method invoker, ProtocolHandler.MinecraftRegister handler) {
super(owner, invoker, handler, null, ServerPlayer.class, ResourceLocation.class);
}
public void invoke(ServerPlayer player, ResourceLocation id) {
invoke0(false, player, id);
}
}

View File

@@ -0,0 +1,18 @@
package org.leavesmc.leaves.protocol.core.invoker;
import net.minecraft.server.level.ServerPlayer;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import java.lang.reflect.Method;
public class PayloadReceiverInvokerHolder extends AbstractInvokerHolder<ProtocolHandler.PayloadReceiver> {
public PayloadReceiverInvokerHolder(LeavesProtocol owner, Method invoker, ProtocolHandler.PayloadReceiver handler) {
super(owner, invoker, handler, null, ServerPlayer.class, handler.payload());
}
public void invoke(ServerPlayer player, LeavesCustomPayload payload) {
invoke0(false, player, payload);
}
}

View File

@@ -0,0 +1,16 @@
package org.leavesmc.leaves.protocol.core.invoker;
import net.minecraft.server.level.ServerPlayer;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import java.lang.reflect.Method;
public class PlayerInvokerHolder<T> extends AbstractInvokerHolder<T> {
public PlayerInvokerHolder(LeavesProtocol owner, Method invoker, T handler) {
super(owner, invoker, handler, null, ServerPlayer.class);
}
public void invoke(ServerPlayer player) {
invoke0(false, player);
}
}

View File

@@ -18,6 +18,7 @@ import net.minecraft.world.entity.animal.Chicken;
import net.minecraft.world.entity.animal.allay.Allay;
import net.minecraft.world.entity.animal.armadillo.Armadillo;
import net.minecraft.world.entity.animal.frog.Tadpole;
import net.minecraft.world.entity.animal.sniffer.Sniffer;
import net.minecraft.world.entity.monster.ZombieVillager;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
@@ -55,6 +56,7 @@ import org.leavesmc.leaves.protocol.jade.provider.IServerExtensionProvider;
import org.leavesmc.leaves.protocol.jade.provider.ItemStorageExtensionProvider;
import org.leavesmc.leaves.protocol.jade.provider.ItemStorageProvider;
import org.leavesmc.leaves.protocol.jade.provider.block.BeehiveProvider;
import org.leavesmc.leaves.protocol.jade.provider.block.BlockNameProvider;
import org.leavesmc.leaves.protocol.jade.provider.block.BrewingStandProvider;
import org.leavesmc.leaves.protocol.jade.provider.block.CampfireProvider;
import org.leavesmc.leaves.protocol.jade.provider.block.ChiseledBookshelfProvider;
@@ -64,7 +66,6 @@ import org.leavesmc.leaves.protocol.jade.provider.block.HopperLockProvider;
import org.leavesmc.leaves.protocol.jade.provider.block.JukeboxProvider;
import org.leavesmc.leaves.protocol.jade.provider.block.LecternProvider;
import org.leavesmc.leaves.protocol.jade.provider.block.MobSpawnerCooldownProvider;
import org.leavesmc.leaves.protocol.jade.provider.block.ObjectNameProvider;
import org.leavesmc.leaves.protocol.jade.provider.block.RedstoneProvider;
import org.leavesmc.leaves.protocol.jade.provider.entity.AnimalOwnerProvider;
import org.leavesmc.leaves.protocol.jade.provider.entity.MobBreedingProvider;
@@ -78,26 +79,26 @@ import org.leavesmc.leaves.protocol.jade.util.LootTableMineableCollector;
import org.leavesmc.leaves.protocol.jade.util.PairHierarchyLookup;
import org.leavesmc.leaves.protocol.jade.util.PriorityStore;
import org.leavesmc.leaves.protocol.jade.util.WrappedHierarchyLookup;
import org.leavesmc.leaves.protocol.servux.litematics.utils.NbtUtils;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@LeavesProtocol(namespace = "jade")
public class JadeProtocol {
public static PriorityStore<ResourceLocation, IJadeProvider> priorities;
private static List<Block> shearableBlocks = null;
@LeavesProtocol.Register(namespace = "jade")
public class JadeProtocol implements LeavesProtocol {
public static final String PROTOCOL_ID = "jade";
public static final String PROTOCOL_VERSION = "7";
public static final HierarchyLookup<IServerDataProvider<EntityAccessor>> entityDataProviders = new HierarchyLookup<>(Entity.class);
public static final PairHierarchyLookup<IServerDataProvider<BlockAccessor>> blockDataProviders = new PairHierarchyLookup<>(new HierarchyLookup<>(Block.class), new HierarchyLookup<>(BlockEntity.class));
public static final WrappedHierarchyLookup<IServerExtensionProvider<ItemStack>> itemStorageProviders = WrappedHierarchyLookup.forAccessor();
private static final Set<ServerPlayer> enabledPlayers = new HashSet<>();
public static PriorityStore<ResourceLocation, IJadeProvider> priorities;
private static List<Block> shearableBlocks = null;
@Contract("_ -> new")
public static ResourceLocation id(String path) {
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
@@ -113,7 +114,7 @@ public class JadeProtocol {
priorities = new PriorityStore<>(IJadeProvider::getDefaultPriority, IJadeProvider::getUid);
// core plugin
blockDataProviders.register(BlockEntity.class, ObjectNameProvider.ForBlock.INSTANCE);
blockDataProviders.register(BlockEntity.class, BlockNameProvider.INSTANCE);
// universal plugin
entityDataProviders.register(Entity.class, ItemStorageProvider.getEntity());
@@ -133,6 +134,7 @@ public class JadeProtocol {
entityDataProviders.register(Chicken.class, NextEntityDropProvider.INSTANCE);
entityDataProviders.register(Armadillo.class, NextEntityDropProvider.INSTANCE);
entityDataProviders.register(Sniffer.class, NextEntityDropProvider.INSTANCE);
entityDataProviders.register(ZombieVillager.class, ZombieVillagerProvider.INSTANCE);
@@ -150,9 +152,10 @@ public class JadeProtocol {
blockDataProviders.register(ChiseledBookShelfBlockEntity.class, ChiseledBookshelfProvider.INSTANCE);
blockDataProviders.register(TrialSpawnerBlockEntity.class, MobSpawnerCooldownProvider.INSTANCE);
itemStorageProviders.register(CampfireBlock.class, CampfireProvider.INSTANCE);
blockDataProviders.idMapped();
entityDataProviders.idMapped();
itemStorageProviders.register(CampfireBlock.class, CampfireProvider.INSTANCE);
blockDataProviders.loadComplete(priorities);
entityDataProviders.loadComplete(priorities);
@@ -161,12 +164,8 @@ public class JadeProtocol {
rebuildShearableBlocks();
}
@ProtocolHandler.PayloadReceiver(payload = ClientHandshakePayload.class, payloadId = "client_handshake")
@ProtocolHandler.PayloadReceiver(payload = ClientHandshakePayload.class)
public static void clientHandshake(ServerPlayer player, ClientHandshakePayload payload) {
if (!LeavesConfig.protocol.jadeProtocol) {
return;
}
if (!payload.protocolVersion().equals(PROTOCOL_VERSION)) {
player.sendSystemMessage(Component.literal("You are using a different version of Jade than the server. Please update Jade or report to the server operator").withColor(0xff0000));
return;
@@ -180,12 +179,8 @@ public class JadeProtocol {
enabledPlayers.remove(player);
}
@ProtocolHandler.PayloadReceiver(payload = RequestEntityPayload.class, payloadId = "request_entity")
@ProtocolHandler.PayloadReceiver(payload = RequestEntityPayload.class)
public static void requestEntityData(ServerPlayer player, RequestEntityPayload payload) {
if (!LeavesConfig.protocol.jadeProtocol) {
return;
}
MinecraftServer.getServer().execute(() -> {
EntityAccessor accessor = payload.data().unpack(player);
if (accessor == null) {
@@ -220,12 +215,8 @@ public class JadeProtocol {
});
}
@ProtocolHandler.PayloadReceiver(payload = RequestBlockPayload.class, payloadId = "request_block")
@ProtocolHandler.PayloadReceiver(payload = RequestBlockPayload.class)
public static void requestBlockData(ServerPlayer player, RequestBlockPayload payload) {
if (!LeavesConfig.protocol.jadeProtocol) {
return;
}
MinecraftServer server = MinecraftServer.getServer();
server.execute(() -> {
BlockAccessor accessor = payload.data().unpack(player);
@@ -263,9 +254,7 @@ public class JadeProtocol {
LeavesLogger.LOGGER.warning("Error while saving data for block " + accessor.getBlockState());
}
}
tag.putInt("x", pos.getX());
tag.putInt("y", pos.getY());
tag.putInt("z", pos.getZ());
NbtUtils.writeBlockPosToTag(pos, tag);
tag.putString("BlockId", BuiltInRegistries.BLOCK.getKey(block).toString());
ProtocolUtils.sendPayloadPacket(player, new ReceiveDataPayload(tag));
@@ -274,11 +263,9 @@ public class JadeProtocol {
@ProtocolHandler.ReloadServer
public static void onServerReload() {
if (LeavesConfig.protocol.jadeProtocol) {
rebuildShearableBlocks();
for (ServerPlayer player : enabledPlayers) {
ProtocolUtils.sendPayloadPacket(player, new ServerHandshakePayload(Collections.emptyMap(), shearableBlocks, blockDataProviders.mappedIds(), entityDataProviders.mappedIds()));
}
rebuildShearableBlocks();
for (ServerPlayer player : enabledPlayers) {
ProtocolUtils.sendPayloadPacket(player, new ServerHandshakePayload(Collections.emptyMap(), shearableBlocks, blockDataProviders.mappedIds(), entityDataProviders.mappedIds()));
}
}
@@ -293,4 +280,9 @@ public class JadeProtocol {
LeavesLogger.LOGGER.severe("Failed to collect shearable blocks");
}
}
@Override
public boolean isActive() {
return LeavesConfig.protocol.jadeProtocol;
}
}

View File

@@ -9,7 +9,6 @@ import net.minecraft.world.phys.HitResult;
import org.jetbrains.annotations.Nullable;
public interface Accessor<T extends HitResult> {
Level getLevel();
Player getPlayer();
@@ -18,15 +17,6 @@ public interface Accessor<T extends HitResult> {
T getHitResult();
/**
* @return {@code true} if the dedicated server has Jade installed.
*/
boolean isServerConnected();
boolean showDetails();
@Nullable
Object getTarget();
float tickRate();
}

View File

@@ -17,17 +17,13 @@ public abstract class AccessorImpl<T extends HitResult> implements Accessor<T> {
private final Level level;
private final Player player;
private final Supplier<T> hit;
private final boolean serverConnected;
private final boolean showDetails;
protected boolean verify;
private RegistryFriendlyByteBuf buffer;
public AccessorImpl(Level level, Player player, Supplier<T> hit, boolean serverConnected, boolean showDetails) {
public AccessorImpl(Level level, Player player, Supplier<T> hit) {
this.level = level;
this.player = player;
this.hit = hit;
this.serverConnected = serverConnected;
this.showDetails = showDetails;
}
@Override
@@ -61,22 +57,4 @@ public abstract class AccessorImpl<T extends HitResult> implements Accessor<T> {
public T getHitResult() {
return hit.get();
}
/**
* Returns true if dedicated server has Jade installed.
*/
@Override
public boolean isServerConnected() {
return serverConnected;
}
@Override
public boolean showDetails() {
return showDetails;
}
@Override
public float tickRate() {
return getLevel().tickRateManager().tickrate();
}
}

View File

@@ -1,7 +1,6 @@
package org.leavesmc.leaves.protocol.jade.accessor;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
@@ -22,16 +21,12 @@ public interface BlockAccessor extends Accessor<BlockHitResult> {
BlockPos getPosition();
Direction getSide();
@ApiStatus.NonExtendable
interface Builder {
Builder level(Level level);
Builder player(Player player);
Builder showDetails(boolean showDetails);
Builder hit(BlockHitResult hit);
Builder blockState(BlockState state);
@@ -46,5 +41,4 @@ public interface BlockAccessor extends Accessor<BlockHitResult> {
BlockAccessor build();
}
}

View File

@@ -2,7 +2,6 @@ package org.leavesmc.leaves.protocol.jade.accessor;
import com.google.common.base.Suppliers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
@@ -30,7 +29,7 @@ public class BlockAccessorImpl extends AccessorImpl<BlockHitResult> implements B
private final Supplier<BlockEntity> blockEntity;
private BlockAccessorImpl(Builder builder) {
super(builder.level, builder.player, Suppliers.ofInstance(builder.hit), builder.connected, builder.showDetails);
super(builder.level, builder.player, Suppliers.ofInstance(builder.hit));
blockState = builder.blockState;
blockEntity = builder.blockEntity;
}
@@ -55,11 +54,6 @@ public class BlockAccessorImpl extends AccessorImpl<BlockHitResult> implements B
return getHitResult().getBlockPos();
}
@Override
public Direction getSide() {
return getHitResult().getDirection();
}
@Nullable
@Override
public Object getTarget() {
@@ -67,11 +61,8 @@ public class BlockAccessorImpl extends AccessorImpl<BlockHitResult> implements B
}
public static class Builder implements BlockAccessor.Builder {
private Level level;
private Player player;
private boolean connected;
private boolean showDetails;
private BlockHitResult hit;
private BlockState blockState = Blocks.AIR.defaultBlockState();
private Supplier<BlockEntity> blockEntity;
@@ -88,12 +79,6 @@ public class BlockAccessorImpl extends AccessorImpl<BlockHitResult> implements B
return this;
}
@Override
public Builder showDetails(boolean showDetails) {
this.showDetails = showDetails;
return this;
}
@Override
public Builder hit(BlockHitResult hit) {
this.hit = hit;
@@ -116,8 +101,6 @@ public class BlockAccessorImpl extends AccessorImpl<BlockHitResult> implements B
public Builder from(BlockAccessor accessor) {
level = accessor.getLevel();
player = accessor.getPlayer();
connected = accessor.isServerConnected();
showDetails = accessor.showDetails();
hit = accessor.getHitResult();
blockEntity = accessor::getBlockEntity;
blockState = accessor.getBlockState();
@@ -132,15 +115,15 @@ public class BlockAccessorImpl extends AccessorImpl<BlockHitResult> implements B
public record SyncData(boolean showDetails, BlockHitResult hit, BlockState blockState, ItemStack fakeBlock) {
public static final StreamCodec<RegistryFriendlyByteBuf, SyncData> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.BOOL,
SyncData::showDetails,
StreamCodec.of(FriendlyByteBuf::writeBlockHitResult, FriendlyByteBuf::readBlockHitResult),
SyncData::hit,
ByteBufCodecs.idMapper(Block.BLOCK_STATE_REGISTRY),
SyncData::blockState,
ItemStack.OPTIONAL_STREAM_CODEC,
SyncData::fakeBlock,
SyncData::new
ByteBufCodecs.BOOL,
SyncData::showDetails,
StreamCodec.of(FriendlyByteBuf::writeBlockHitResult, FriendlyByteBuf::readBlockHitResult),
SyncData::hit,
ByteBufCodecs.idMapper(Block.BLOCK_STATE_REGISTRY),
SyncData::blockState,
ItemStack.OPTIONAL_STREAM_CODEC,
SyncData::fakeBlock,
SyncData::new
);
public BlockAccessor unpack(ServerPlayer player) {
@@ -149,13 +132,12 @@ public class BlockAccessorImpl extends AccessorImpl<BlockHitResult> implements B
blockEntity = Suppliers.memoize(() -> player.level().getBlockEntity(hit.getBlockPos()));
}
return new Builder()
.level(player.level())
.player(player)
.showDetails(showDetails)
.hit(hit)
.blockState(blockState)
.blockEntity(blockEntity)
.build();
.level(player.level())
.player(player)
.hit(hit)
.blockState(blockState)
.blockEntity(blockEntity)
.build();
}
}
}

View File

@@ -23,8 +23,6 @@ public interface EntityAccessor extends Accessor<EntityHitResult> {
Builder player(Player player);
Builder showDetails(boolean showDetails);
default Builder hit(EntityHitResult hit) {
return hit(() -> hit);
}

View File

@@ -20,7 +20,7 @@ public class EntityAccessorImpl extends AccessorImpl<EntityHitResult> implements
private final Supplier<Entity> entity;
public EntityAccessorImpl(Builder builder) {
super(builder.level, builder.player, builder.hit, builder.connected, builder.showDetails);
super(builder.level, builder.player, builder.hit);
entity = builder.entity;
}
@@ -41,11 +41,8 @@ public class EntityAccessorImpl extends AccessorImpl<EntityHitResult> implements
}
public static class Builder implements EntityAccessor.Builder {
public boolean showDetails;
private Level level;
private Player player;
private boolean connected;
private Supplier<EntityHitResult> hit;
private Supplier<Entity> entity;
@@ -61,11 +58,6 @@ public class EntityAccessorImpl extends AccessorImpl<EntityHitResult> implements
return this;
}
@Override
public Builder showDetails(boolean showDetails) {
this.showDetails = showDetails;
return this;
}
@Override
public Builder hit(Supplier<EntityHitResult> hit) {
@@ -83,8 +75,6 @@ public class EntityAccessorImpl extends AccessorImpl<EntityHitResult> implements
public Builder from(EntityAccessor accessor) {
level = accessor.getLevel();
player = accessor.getPlayer();
connected = accessor.isServerConnected();
showDetails = accessor.showDetails();
hit = accessor::getHitResult;
entity = accessor::getEntity;
return this;
@@ -98,26 +88,25 @@ public class EntityAccessorImpl extends AccessorImpl<EntityHitResult> implements
public record SyncData(boolean showDetails, int id, int partIndex, Vec3 hitVec) {
public static final StreamCodec<RegistryFriendlyByteBuf, SyncData> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.BOOL,
SyncData::showDetails,
ByteBufCodecs.VAR_INT,
SyncData::id,
ByteBufCodecs.VAR_INT,
SyncData::partIndex,
ByteBufCodecs.VECTOR3F.map(Vec3::new, Vec3::toVector3f),
SyncData::hitVec,
SyncData::new
ByteBufCodecs.BOOL,
SyncData::showDetails,
ByteBufCodecs.VAR_INT,
SyncData::id,
ByteBufCodecs.VAR_INT,
SyncData::partIndex,
ByteBufCodecs.VECTOR3F.map(Vec3::new, Vec3::toVector3f),
SyncData::hitVec,
SyncData::new
);
public EntityAccessor unpack(ServerPlayer player) {
Supplier<Entity> entity = Suppliers.memoize(() -> CommonUtil.getPartEntity(player.level().getEntity(id), partIndex));
return new EntityAccessorImpl.Builder()
.level(player.level())
.player(player)
.showDetails(showDetails)
.entity(entity)
.hit(Suppliers.memoize(() -> new EntityHitResult(entity.get(), hitVec)))
.build();
.level(player.level())
.player(player)
.entity(entity)
.hit(Suppliers.memoize(() -> new EntityHitResult(entity.get(), hitVec)))
.build();
}
}
}

View File

@@ -1,35 +1,19 @@
package org.leavesmc.leaves.protocol.jade.payload;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
public record ClientHandshakePayload(String protocolVersion) implements LeavesCustomPayload<ClientHandshakePayload> {
public record ClientHandshakePayload(String protocolVersion) implements LeavesCustomPayload {
@ID
private static final ResourceLocation PACKET_CLIENT_HANDSHAKE = JadeProtocol.id("client_handshake");
@Codec
private static final StreamCodec<RegistryFriendlyByteBuf, ClientHandshakePayload> CODEC = StreamCodec.composite(
ByteBufCodecs.STRING_UTF8,
ClientHandshakePayload::protocolVersion,
ClientHandshakePayload::new);
@Override
public void write(FriendlyByteBuf buf) {
CODEC.encode(ProtocolUtils.decorate(buf), this);
}
@Override
public ResourceLocation id() {
return PACKET_CLIENT_HANDSHAKE;
}
@New
public static ClientHandshakePayload create(ResourceLocation location, FriendlyByteBuf buf) {
return CODEC.decode(ProtocolUtils.decorate(buf));
}
}
ByteBufCodecs.STRING_UTF8, ClientHandshakePayload::protocolVersion, ClientHandshakePayload::new
);
}

View File

@@ -2,27 +2,19 @@ package org.leavesmc.leaves.protocol.jade.payload;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
public record ReceiveDataPayload(CompoundTag tag) implements LeavesCustomPayload<ReceiveDataPayload> {
public record ReceiveDataPayload(CompoundTag tag) implements LeavesCustomPayload {
@ID
private static final ResourceLocation PACKET_RECEIVE_DATA = JadeProtocol.id("receive_data");
@New
public ReceiveDataPayload(ResourceLocation id, FriendlyByteBuf buf) {
this(buf.readNbt());
}
@Override
public void write(@NotNull FriendlyByteBuf buf) {
buf.writeNbt(tag);
}
@Override
public ResourceLocation id() {
return PACKET_RECEIVE_DATA;
}
@Codec
private static final StreamCodec<FriendlyByteBuf, ReceiveDataPayload> CODEC = StreamCodec.composite(
ByteBufCodecs.COMPOUND_TAG, ReceiveDataPayload::tag, ReceiveDataPayload::new
);
}

View File

@@ -1,15 +1,12 @@
package org.leavesmc.leaves.protocol.jade.payload;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessorImpl;
@@ -20,9 +17,12 @@ import java.util.Objects;
import static org.leavesmc.leaves.protocol.jade.JadeProtocol.blockDataProviders;
public record RequestBlockPayload(BlockAccessorImpl.SyncData data, List<@Nullable IServerDataProvider<BlockAccessor>> dataProviders) implements LeavesCustomPayload<RequestBlockPayload> {
public record RequestBlockPayload(BlockAccessorImpl.SyncData data, List<@Nullable IServerDataProvider<BlockAccessor>> dataProviders) implements LeavesCustomPayload {
@ID
private static final ResourceLocation PACKET_REQUEST_BLOCK = JadeProtocol.id("request_block");
@Codec
private static final StreamCodec<RegistryFriendlyByteBuf, RequestBlockPayload> CODEC = StreamCodec.composite(
BlockAccessorImpl.SyncData.STREAM_CODEC,
RequestBlockPayload::data,
@@ -32,20 +32,4 @@ public record RequestBlockPayload(BlockAccessorImpl.SyncData data, List<@Nullabl
$ -> Objects.requireNonNull(blockDataProviders.idMapper()).getIdOrThrow($))),
RequestBlockPayload::dataProviders,
RequestBlockPayload::new);
@Override
public void write(FriendlyByteBuf buf) {
CODEC.encode(ProtocolUtils.decorate(buf), this);
}
@Override
@NotNull
public ResourceLocation id() {
return PACKET_REQUEST_BLOCK;
}
@New
public static RequestBlockPayload create(ResourceLocation location, FriendlyByteBuf buf) {
return CODEC.decode(ProtocolUtils.decorate(buf));
}
}

View File

@@ -1,15 +1,12 @@
package org.leavesmc.leaves.protocol.jade.payload;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessorImpl;
@@ -20,9 +17,12 @@ import java.util.Objects;
import static org.leavesmc.leaves.protocol.jade.JadeProtocol.entityDataProviders;
public record RequestEntityPayload(EntityAccessorImpl.SyncData data, List<@Nullable IServerDataProvider<EntityAccessor>> dataProviders) implements LeavesCustomPayload<RequestEntityPayload> {
public record RequestEntityPayload(EntityAccessorImpl.SyncData data, List<@Nullable IServerDataProvider<EntityAccessor>> dataProviders) implements LeavesCustomPayload {
@ID
private static final ResourceLocation PACKET_REQUEST_ENTITY = JadeProtocol.id("request_entity");
@Codec
private static final StreamCodec<RegistryFriendlyByteBuf, RequestEntityPayload> CODEC = StreamCodec.composite(
EntityAccessorImpl.SyncData.STREAM_CODEC,
RequestEntityPayload::data,
@@ -33,21 +33,4 @@ public record RequestEntityPayload(EntityAccessorImpl.SyncData data, List<@Nulla
)),
RequestEntityPayload::dataProviders,
RequestEntityPayload::new);
@Override
public void write(FriendlyByteBuf buf) {
CODEC.encode(ProtocolUtils.decorate(buf), this);
}
@Override
@NotNull
public ResourceLocation id() {
return PACKET_REQUEST_ENTITY;
}
@New
public static RequestEntityPayload create(ResourceLocation location, FriendlyByteBuf buf) {
return CODEC.decode(ProtocolUtils.decorate(buf));
}
}

View File

@@ -4,14 +4,12 @@ package org.leavesmc.leaves.protocol.jade.payload;
import com.google.common.collect.Maps;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import java.util.List;
@@ -19,9 +17,12 @@ import java.util.Map;
import static org.leavesmc.leaves.protocol.jade.util.JadeCodec.PRIMITIVE_STREAM_CODEC;
public record ServerHandshakePayload(Map<ResourceLocation, Object> serverConfig, List<Block> shearableBlocks, List<ResourceLocation> blockProviderIds, List<ResourceLocation> entityProviderIds) implements LeavesCustomPayload<ServerHandshakePayload> {
public record ServerHandshakePayload(Map<ResourceLocation, Object> serverConfig, List<Block> shearableBlocks, List<ResourceLocation> blockProviderIds, List<ResourceLocation> entityProviderIds) implements LeavesCustomPayload {
@ID
private static final ResourceLocation PACKET_SERVER_HANDSHAKE = JadeProtocol.id("server_handshake");
@Codec
private static final StreamCodec<RegistryFriendlyByteBuf, ServerHandshakePayload> CODEC = StreamCodec.composite(
ByteBufCodecs.map(Maps::newHashMapWithExpectedSize, ResourceLocation.STREAM_CODEC, PRIMITIVE_STREAM_CODEC),
ServerHandshakePayload::serverConfig,
@@ -31,21 +32,6 @@ public record ServerHandshakePayload(Map<ResourceLocation, Object> serverConfig,
ServerHandshakePayload::blockProviderIds,
ByteBufCodecs.<ByteBuf, ResourceLocation>list().apply(ResourceLocation.STREAM_CODEC),
ServerHandshakePayload::entityProviderIds,
ServerHandshakePayload::new);
@Override
public void write(FriendlyByteBuf buf) {
CODEC.encode(ProtocolUtils.decorate(buf), this);
}
@Override
public ResourceLocation id() {
return PACKET_SERVER_HANDSHAKE;
}
@New
public static ServerHandshakePayload create(ResourceLocation location, FriendlyByteBuf buf) {
return CODEC.decode(ProtocolUtils.decorate(buf));
}
}
ServerHandshakePayload::new
);
}

View File

@@ -37,52 +37,6 @@ public enum ItemStorageExtensionProvider implements IServerExtensionProvider<Ite
private static final ResourceLocation UNIVERSAL_ITEM_STORAGE = JadeProtocol.mc_id("item_storage.default");
@Override
public List<ViewGroup<ItemStack>> getGroups(Accessor<?> request) {
Object target = request.getTarget();
switch (target) {
case null -> {
return createItemCollector(request).update(request);
}
case RandomizableContainer te when te.getLootTable() != null -> {
return List.of();
}
case ContainerEntity containerEntity when containerEntity.getContainerLootTable() != null -> {
return List.of();
}
default -> {
}
}
Player player = request.getPlayer();
if (!player.isCreative() && !player.isSpectator() && target instanceof BaseContainerBlockEntity te) {
if (te.lockKey != LockCode.NO_LOCK) {
return List.of();
}
}
if (target instanceof EnderChestBlockEntity) {
PlayerEnderChestContainer inventory = player.getEnderChestInventory();
return new ItemCollector<>(new ItemIterator.ContainerItemIterator(x -> inventory, 0)).update(request);
}
ItemCollector<?> itemCollector;
try {
itemCollector = targetCache.get(target, () -> createItemCollector(request));
} catch (ExecutionException e) {
LeavesLogger.LOGGER.severe("Failed to get item collector for " + target);
return null;
}
return itemCollector.update(request);
}
@Override
public ResourceLocation getUid() {
return UNIVERSAL_ITEM_STORAGE;
}
public static ItemCollector<?> createItemCollector(Accessor<?> request) {
if (request.getTarget() instanceof AbstractHorse) {
return new ItemCollector<>(new ItemIterator.ContainerItemIterator(o -> {
@@ -131,8 +85,54 @@ public enum ItemStorageExtensionProvider implements IServerExtensionProvider<Ite
return null;
}
@Override
public List<ViewGroup<ItemStack>> getGroups(Accessor<?> request) {
Object target = request.getTarget();
switch (target) {
case null -> {
return createItemCollector(request).update(request);
}
case RandomizableContainer te when te.getLootTable() != null -> {
return List.of();
}
case ContainerEntity containerEntity when containerEntity.getContainerLootTable() != null -> {
return List.of();
}
default -> {
}
}
Player player = request.getPlayer();
if (!player.isCreative() && !player.isSpectator() && target instanceof BaseContainerBlockEntity te) {
if (te.lockKey != LockCode.NO_LOCK) {
return List.of();
}
}
if (target instanceof EnderChestBlockEntity) {
PlayerEnderChestContainer inventory = player.getEnderChestInventory();
return new ItemCollector<>(new ItemIterator.ContainerItemIterator(x -> inventory, 0)).update(request);
}
ItemCollector<?> itemCollector;
try {
itemCollector = targetCache.get(target, () -> createItemCollector(request));
} catch (ExecutionException e) {
LeavesLogger.LOGGER.severe("Failed to get item collector for " + target);
return null;
}
return itemCollector.update(request);
}
@Override
public ResourceLocation getUid() {
return UNIVERSAL_ITEM_STORAGE;
}
@Override
public int getDefaultPriority() {
return IServerExtensionProvider.super.getDefaultPriority() + 1000;
return 9999;
}
}

View File

@@ -24,8 +24,7 @@ import java.util.Map;
public abstract class ItemStorageProvider<T extends Accessor<?>> implements IServerDataProvider<T> {
private static final StreamCodec<RegistryFriendlyByteBuf, Map.Entry<ResourceLocation, List<ViewGroup<ItemStack>>>> STREAM_CODEC = ViewGroup.listCodec(
ItemStack.OPTIONAL_STREAM_CODEC);
private static final StreamCodec<RegistryFriendlyByteBuf, Map.Entry<ResourceLocation, List<ViewGroup<ItemStack>>>> STREAM_CODEC = ViewGroup.listCodec(ItemStack.OPTIONAL_STREAM_CODEC);
private static final ResourceLocation UNIVERSAL_ITEM_STORAGE = JadeProtocol.mc_id("item_storage");
@@ -37,14 +36,6 @@ public abstract class ItemStorageProvider<T extends Accessor<?>> implements ISer
return ForEntity.INSTANCE;
}
public static class ForBlock extends ItemStorageProvider<BlockAccessor> {
private static final ForBlock INSTANCE = new ForBlock();
}
public static class ForEntity extends ItemStorageProvider<EntityAccessor> {
private static final ForEntity INSTANCE = new ForEntity();
}
public static void putData(CompoundTag tag, @NotNull Accessor<?> accessor) {
Object target = accessor.getTarget();
Player player = accessor.getPlayer();
@@ -83,6 +74,14 @@ public abstract class ItemStorageProvider<T extends Accessor<?>> implements ISer
@Override
public int getDefaultPriority() {
return 9999;
return 1000;
}
public static class ForBlock extends ItemStorageProvider<BlockAccessor> {
private static final ForBlock INSTANCE = new ForBlock();
}
public static class ForEntity extends ItemStorageProvider<EntityAccessor> {
private static final ForEntity INSTANCE = new ForEntity();
}
}

View File

@@ -0,0 +1,60 @@
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentSerialization;
import net.minecraft.network.chat.contents.TranslatableContents;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.Nameable;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.state.properties.ChestType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum BlockNameProvider implements StreamServerDataProvider<BlockAccessor, Component> {
INSTANCE;
private static final ResourceLocation CORE_OBJECT_NAME = JadeProtocol.id("object_name");
@Override
@Nullable
public Component streamData(@NotNull BlockAccessor accessor) {
if (!(accessor.getBlockEntity() instanceof Nameable nameable)) {
return null;
}
if (nameable instanceof ChestBlockEntity && accessor.getBlock() instanceof ChestBlock && accessor.getBlockState().getValue(ChestBlock.TYPE) != ChestType.SINGLE) {
MenuProvider menuProvider = accessor.getBlockState().getMenuProvider(accessor.getLevel(), accessor.getPosition());
if (menuProvider != null) {
Component name = menuProvider.getDisplayName();
if (!(name.getContents() instanceof TranslatableContents contents) || !"container.chestDouble".equals(contents.getKey())) {
return name;
}
}
} else if (nameable.hasCustomName()) {
return nameable.getDisplayName();
}
return accessor.getBlockEntity().components().get(DataComponents.ITEM_NAME);
}
@Override
public StreamCodec<RegistryFriendlyByteBuf, Component> streamCodec() {
return ComponentSerialization.STREAM_CODEC;
}
@Override
public ResourceLocation getUid() {
return CORE_OBJECT_NAME;
}
@Override
public int getDefaultPriority() {
return -10100;
}
}

View File

@@ -34,10 +34,10 @@ public enum BrewingStandProvider implements StreamServerDataProvider<BlockAccess
public record Data(int fuel, int time) {
public static final StreamCodec<ByteBuf, Data> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.VAR_INT,
Data::fuel,
ByteBufCodecs.VAR_INT,
Data::time,
Data::new);
ByteBufCodecs.VAR_INT,
Data::fuel,
ByteBufCodecs.VAR_INT,
Data::time,
Data::new);
}
}

View File

@@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.ItemStorageProvider;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public enum ChiseledBookshelfProvider implements StreamServerDataProvider<BlockAccessor, ItemStack> {
@@ -31,9 +32,13 @@ public enum ChiseledBookshelfProvider implements StreamServerDataProvider<BlockA
return ItemStack.OPTIONAL_STREAM_CODEC;
}
@Override
public ResourceLocation getUid() {
return MC_CHISELED_BOOKSHELF;
}
@Override
public int getDefaultPriority() {
return ItemStorageProvider.getBlock().getDefaultPriority() + 1;
}
}

View File

@@ -1,6 +1,5 @@
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
@@ -8,7 +7,6 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
@@ -21,20 +19,13 @@ public enum FurnaceProvider implements StreamServerDataProvider<BlockAccessor, F
private static final ResourceLocation MC_FURNACE = JadeProtocol.mc_id("furnace");
@Override
public @Nullable Data streamData(@NotNull BlockAccessor accessor) {
if (!(accessor.getTarget() instanceof AbstractFurnaceBlockEntity furnace)) {
return null;
}
if (furnace.isEmpty()) {
return null;
}
CompoundTag furnaceTag = furnace.saveWithoutMetadata(accessor.getLevel().registryAccess());
public @NotNull Data streamData(@NotNull BlockAccessor accessor) {
AbstractFurnaceBlockEntity furnace = (AbstractFurnaceBlockEntity) accessor.getBlockEntity();
return new Data(
furnaceTag.getInt("CookTime"),
furnaceTag.getInt("CookTimeTotal"),
List.of(furnace.getItem(0), furnace.getItem(1), furnace.getItem(2)));
furnace.cookingTimer,
furnace.cookingTotalTime,
List.of(furnace.getItem(0), furnace.getItem(1), furnace.getItem(2))
);
}
@Override
@@ -49,12 +40,12 @@ public enum FurnaceProvider implements StreamServerDataProvider<BlockAccessor, F
public record Data(int progress, int total, List<ItemStack> inventory) {
public static final StreamCodec<RegistryFriendlyByteBuf, Data> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.VAR_INT,
Data::progress,
ByteBufCodecs.VAR_INT,
Data::total,
ItemStack.OPTIONAL_LIST_STREAM_CODEC,
Data::inventory,
Data::new);
ByteBufCodecs.VAR_INT,
Data::progress,
ByteBufCodecs.VAR_INT,
Data::total,
ItemStack.OPTIONAL_LIST_STREAM_CODEC,
Data::inventory,
Data::new);
}
}

View File

@@ -29,4 +29,9 @@ public enum HopperLockProvider implements StreamServerDataProvider<BlockAccessor
public ResourceLocation getUid() {
return MC_HOPPER_LOCK;
}
@Override
public int getDefaultPriority() {
return BlockNameProvider.INSTANCE.getDefaultPriority() + 10;
}
}

View File

@@ -1,63 +0,0 @@
package org.leavesmc.leaves.protocol.jade.provider.block;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentSerialization;
import net.minecraft.network.chat.contents.TranslatableContents;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.Nameable;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.state.properties.ChestType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.BlockAccessor;
import org.leavesmc.leaves.protocol.jade.provider.StreamServerDataProvider;
public abstract class ObjectNameProvider implements StreamServerDataProvider<BlockAccessor, Component> {
private static final ResourceLocation CORE_OBJECT_NAME = JadeProtocol.id("object_name");
public static class ForBlock extends ObjectNameProvider implements StreamServerDataProvider<BlockAccessor, Component> {
public static final ForBlock INSTANCE = new ForBlock();
@Override
@Nullable
public Component streamData(@NotNull BlockAccessor accessor) {
if (!(accessor.getBlockEntity() instanceof Nameable nameable)) {
return null;
}
if (nameable instanceof ChestBlockEntity && accessor.getBlock() instanceof ChestBlock && accessor.getBlockState().getValue(ChestBlock.TYPE) != ChestType.SINGLE) {
MenuProvider menuProvider = accessor.getBlockState().getMenuProvider(accessor.getLevel(), accessor.getPosition());
if (menuProvider != null) {
Component name = menuProvider.getDisplayName();
if (!(name.getContents() instanceof TranslatableContents contents) || !"container.chestDouble".equals(contents.getKey())) {
return name;
}
}
} else if (nameable.hasCustomName()) {
return nameable.getDisplayName();
}
return accessor.getBlockEntity().components().get(DataComponents.ITEM_NAME);
}
@Override
public StreamCodec<RegistryFriendlyByteBuf, Component> streamCodec() {
return ComponentSerialization.STREAM_CODEC;
}
}
@Override
public ResourceLocation getUid() {
return CORE_OBJECT_NAME;
}
@Override
public int getDefaultPriority() {
return -10100;
}
}

View File

@@ -5,6 +5,8 @@ import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityReference;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.OwnableEntity;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
@@ -19,6 +21,16 @@ public enum AnimalOwnerProvider implements StreamServerDataProvider<EntityAccess
private static final ResourceLocation MC_ANIMAL_OWNER = JadeProtocol.mc_id("animal_owner");
public static UUID getOwnerUUID(Entity entity) {
if (entity instanceof OwnableEntity ownableEntity) {
EntityReference<LivingEntity> reference = ownableEntity.getOwnerReference();
if (reference != null) {
return reference.getUUID();
}
}
return null;
}
@Override
public String streamData(@NotNull EntityAccessor accessor) {
return CommonUtil.getLastKnownUsername(getOwnerUUID(accessor.getEntity()));
@@ -29,13 +41,6 @@ public enum AnimalOwnerProvider implements StreamServerDataProvider<EntityAccess
return ByteBufCodecs.STRING_UTF8.cast();
}
public static UUID getOwnerUUID(Entity entity) {
if (entity instanceof OwnableEntity ownableEntity) {
return ownableEntity.getOwnerUUID();
}
return null;
}
@Override
public ResourceLocation getUid() {
return MC_ANIMAL_OWNER;

View File

@@ -2,8 +2,10 @@ package org.leavesmc.leaves.protocol.jade.provider.entity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.ai.memory.MemoryModuleType;
import net.minecraft.world.entity.animal.Chicken;
import net.minecraft.world.entity.animal.armadillo.Armadillo;
import net.minecraft.world.entity.animal.sniffer.Sniffer;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.accessor.EntityAccessor;
@@ -25,6 +27,11 @@ public enum NextEntityDropProvider implements IServerDataProvider<EntityAccessor
if (!armadillo.isBaby() && armadillo.scuteTime < max) {
tag.putInt("NextScuteIn", armadillo.scuteTime);
}
} else if (accessor.getEntity() instanceof Sniffer sniffer) {
long time = sniffer.getBrain().getTimeUntilExpiry(MemoryModuleType.SNIFF_COOLDOWN);
if (time > 0 && time < max) {
tag.putInt("NextSniffIn", (int) time);
}
}
}

View File

@@ -1,32 +1,37 @@
package org.leavesmc.leaves.protocol.jade.tool;
import net.minecraft.core.BlockPos;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.item.component.Tool;
import net.minecraft.world.level.block.state.BlockState;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import java.util.List;
public class ShearsToolHandler extends SimpleToolHandler {
public class ShearsToolHandler {
private static final ShearsToolHandler INSTANCE = new ShearsToolHandler();
private final List<ItemStack> tools;
public ShearsToolHandler() {
this.tools = List.of(Items.SHEARS.getDefaultInstance());
}
public static ShearsToolHandler getInstance() {
return INSTANCE;
}
public ShearsToolHandler() {
super(JadeProtocol.id("shears"), List.of(Items.SHEARS.getDefaultInstance()), true);
}
@Override
public ItemStack test(BlockState state, Level world, BlockPos pos) {
if (state.is(Blocks.TRIPWIRE)) {
return tools.getFirst();
public ItemStack test(BlockState state) {
for (ItemStack toolItem : tools) {
if (toolItem.isCorrectToolForDrops(state)) {
return toolItem;
}
Tool tool = toolItem.get(DataComponents.TOOL);
if (tool != null && tool.getMiningSpeed(state) > tool.defaultMiningSpeed()) {
return toolItem;
}
}
return super.test(state, world, pos);
return ItemStack.EMPTY;
}
}

View File

@@ -1,71 +0,0 @@
package org.leavesmc.leaves.protocol.jade.tool;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import net.minecraft.core.BlockPos;
import net.minecraft.core.component.DataComponents;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.Tool;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class SimpleToolHandler implements ToolHandler {
protected final List<ItemStack> tools = Lists.newArrayList();
private final ResourceLocation uid;
private final boolean skipInstaBreakingBlock;
protected SimpleToolHandler(ResourceLocation uid, @NotNull List<ItemStack> tools, boolean skipInstaBreakingBlock) {
this.uid = uid;
Preconditions.checkArgument(!tools.isEmpty(), "tools cannot be empty");
this.tools.addAll(tools);
this.skipInstaBreakingBlock = skipInstaBreakingBlock;
}
@Contract("_, _ -> new")
public static @NotNull SimpleToolHandler create(ResourceLocation uid, List<Item> tools) {
return create(uid, tools, true);
}
@Contract("_, _, _ -> new")
public static @NotNull SimpleToolHandler create(ResourceLocation uid, List<Item> tools, boolean skipInstaBreakingBlock) {
return new SimpleToolHandler(uid, Lists.transform(tools, Item::getDefaultInstance), skipInstaBreakingBlock);
}
@Override
public ItemStack test(BlockState state, Level world, BlockPos pos) {
if (skipInstaBreakingBlock && !state.requiresCorrectToolForDrops() && state.getDestroySpeed(world, pos) == 0) {
return ItemStack.EMPTY;
}
return test(state);
}
public ItemStack test(BlockState state) {
for (ItemStack toolItem : tools) {
if (toolItem.isCorrectToolForDrops(state)) {
return toolItem;
}
Tool tool = toolItem.get(DataComponents.TOOL);
if (tool != null && tool.getMiningSpeed(state) > tool.defaultMiningSpeed()) {
return toolItem;
}
}
return ItemStack.EMPTY;
}
@Override
public List<ItemStack> getTools() {
return tools;
}
@Override
public ResourceLocation getUid() {
return uid;
}
}

View File

@@ -1,17 +0,0 @@
package org.leavesmc.leaves.protocol.jade.tool;
import net.minecraft.core.BlockPos;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import org.leavesmc.leaves.protocol.jade.provider.IJadeProvider;
import java.util.List;
public interface ToolHandler extends IJadeProvider {
ItemStack test(BlockState state, Level world, BlockPos pos);
List<ItemStack> getTools();
}

View File

@@ -1,14 +1,11 @@
package org.leavesmc.leaves.protocol.jade.util;
import com.mojang.authlib.GameProfile;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.SkullBlockEntity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.LeavesLogger;
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
@@ -21,10 +18,6 @@ import java.util.UUID;
public class CommonUtil {
public static @NotNull ResourceLocation getId(Block block) {
return BuiltInRegistries.BLOCK.getKey(block);
}
public static Entity wrapPartEntityParent(Entity target) {
if (target instanceof EnderDragonPart part) {
return part.parentMob;

View File

@@ -73,7 +73,7 @@ public class HierarchyLookup<T extends IJadeProvider> implements IHierarchyLooku
return resultCache.get(clazz, () -> {
List<T> list = Lists.newArrayList();
getInternal(clazz, list);
list = ImmutableList.sortedCopyOf(Comparator.comparingInt(JadeProtocol.priorities::byValue), list);
list = ImmutableList.sortedCopyOf(COMPARATOR, list);
if (singleton && !list.isEmpty()) {
return ImmutableList.of(list.getFirst());
}
@@ -135,5 +135,4 @@ public class HierarchyLookup<T extends IJadeProvider> implements IHierarchyLooku
idMapper = createIdMapper();
}
}
}

View File

@@ -4,15 +4,20 @@ import com.google.common.collect.Streams;
import net.minecraft.core.IdMapper;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.jade.JadeProtocol;
import org.leavesmc.leaves.protocol.jade.provider.IJadeProvider;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
public interface IHierarchyLookup<T extends IJadeProvider> {
Comparator<IJadeProvider> COMPARATOR = Comparator.comparingInt(provider -> JadeProtocol.priorities.byValue(provider));
default IHierarchyLookup<? extends T> cast() {
return this;
}

View File

@@ -7,6 +7,7 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.item.component.TooltipDisplay;
import org.leavesmc.leaves.protocol.jade.accessor.Accessor;
import java.util.List;
@@ -17,15 +18,17 @@ import java.util.function.Predicate;
public class ItemCollector<T> {
public static final int MAX_SIZE = 54;
public static final ItemCollector<?> EMPTY = new ItemCollector<>(null);
private static final Predicate<ItemStack> NON_EMPTY = stack -> {
private static final Predicate<ItemStack> SHOWN = stack -> {
if (stack.isEmpty()) {
return false;
}
CustomData customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);
if (customData.contains("CustomModelData")) {
CompoundTag tag = customData.copyTag();
for (String key : tag.getAllKeys()) {
if (key.toLowerCase(Locale.ENGLISH).endsWith("clear") && tag.getBoolean(key)) {
if (stack.getOrDefault(DataComponents.TOOLTIP_DISPLAY, TooltipDisplay.DEFAULT).hideTooltip()) {
return false;
}
if (stack.hasNonDefault(DataComponents.CUSTOM_MODEL_DATA) || stack.hasNonDefault(DataComponents.ITEM_MODEL)) {
CompoundTag tag = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag();
for (String key : tag.keySet()) {
if (key.toLowerCase(Locale.ENGLISH).endsWith("clear") && tag.getBooleanOr(key, true)) {
return false;
}
}
@@ -65,7 +68,7 @@ public class ItemCollector<T> {
AtomicInteger count = new AtomicInteger();
iterator.populate(container).forEach(stack -> {
count.incrementAndGet();
if (NON_EMPTY.test(stack)) {
if (SHOWN.test(stack)) {
ItemDefinition def = new ItemDefinition(stack);
items.addTo(def, stack.getCount());
}

View File

@@ -50,6 +50,25 @@ public class LootTableMineableCollector {
return list;
}
public static boolean isCorrectConditions(@NotNull List<LootItemCondition> conditions, ItemStack toolItem) {
if (conditions.size() != 1) {
return false;
}
LootItemCondition condition = conditions.getFirst();
if (condition instanceof MatchTool(Optional<ItemPredicate> predicate)) {
ItemPredicate itemPredicate = predicate.orElse(null);
return itemPredicate != null && itemPredicate.test(toolItem);
} else if (condition instanceof AnyOfCondition anyOfCondition) {
for (LootItemCondition child : anyOfCondition.terms) {
if (isCorrectConditions(List.of(child), toolItem)) {
return true;
}
}
}
return false;
}
private boolean doLootTable(LootTable lootTable) {
if (lootTable == null || lootTable == LootTable.EMPTY) {
return false;
@@ -87,23 +106,4 @@ public class LootTableMineableCollector {
}
return false;
}
public static boolean isCorrectConditions(@NotNull List<LootItemCondition> conditions, ItemStack toolItem) {
if (conditions.size() != 1) {
return false;
}
LootItemCondition condition = conditions.getFirst();
if (condition instanceof MatchTool(Optional<ItemPredicate> predicate)) {
ItemPredicate itemPredicate = predicate.orElse(null);
return itemPredicate != null && itemPredicate.test(toolItem);
} else if (condition instanceof AnyOfCondition anyOfCondition) {
for (LootItemCondition child : anyOfCondition.terms) {
if (isCorrectConditions(List.of(child), toolItem)) {
return true;
}
}
}
return false;
}
}

Some files were not shown because too many files have changed in this diff Show More