Public Access
This commit is contained in:
1 parent
aed8d6a5c3
commit
3e8cbf6bc5
12 files changed
+233
-14
No files matched your search
@@ -0,0 +1,52 @@
|
||||
package net.dnorberg.rivencraft.book;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import net.minecraft.core.GlobalPos;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
/**
|
||||
* Temporary Milestone 2 destination resolver.
|
||||
*
|
||||
* <p>Until Ages own generated dimensions, an Age UUID deterministically selects
|
||||
* one of the three vanilla dimensions. Milestone 3 will replace this with the
|
||||
* persistent Age-to-dimension mapping without changing book identities.</p>
|
||||
*/
|
||||
public final class VanillaAgeResolver {
|
||||
private static final List<ResourceKey<Level>> VANILLA_DIMENSIONS = List.of(
|
||||
Level.OVERWORLD,
|
||||
Level.NETHER,
|
||||
Level.END
|
||||
);
|
||||
private static final List<AgeReference> DEMO_AGES = List.of(
|
||||
new AgeReference(new UUID(0L, 0L), "Overworld Age"),
|
||||
new AgeReference(new UUID(0L, 1L), "Nether Age"),
|
||||
new AgeReference(new UUID(0L, 2L), "End Age")
|
||||
);
|
||||
|
||||
private VanillaAgeResolver() {
|
||||
}
|
||||
|
||||
public static Optional<LinkTarget> resolve(ServerPlayer player, AgeReference age) {
|
||||
ResourceKey<Level> dimension = VANILLA_DIMENSIONS.get(Math.floorMod(age.id().hashCode(), VANILLA_DIMENSIONS.size()));
|
||||
ServerLevel level = player.getServer().getLevel(dimension);
|
||||
if (level == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(new LinkTarget(
|
||||
GlobalPos.of(dimension, level.getSharedSpawnPos()),
|
||||
level.getSharedSpawnAngle(),
|
||||
0.0F
|
||||
));
|
||||
}
|
||||
|
||||
/** Written sample books exposed in the creative tab until the writing system exists. */
|
||||
public static List<AgeReference> demoAges() {
|
||||
return DEMO_AGES;
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public final class ClientBookInteractionHandler {
|
||||
if (linkingBook) {
|
||||
Minecraft.getInstance().setScreen(new LinkingBookScreen(event.getHand(), event.getItemStack().copy()));
|
||||
} else {
|
||||
Minecraft.getInstance().setScreen(new DescriptiveBookScreen(event.getItemStack().copy()));
|
||||
Minecraft.getInstance().setScreen(new DescriptiveBookScreen(event.getHand(), event.getItemStack().copy()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public final class ClientBookInteractionHandler {
|
||||
if (book.is(ModItems.LINKING_BOOK.get())) {
|
||||
Minecraft.getInstance().setScreen(new LinkingBookScreen(event.getPos(), book.copy()));
|
||||
} else if (book.is(ModItems.DESCRIPTIVE_BOOK.get())) {
|
||||
Minecraft.getInstance().setScreen(new DescriptiveBookScreen(book.copy()));
|
||||
Minecraft.getInstance().setScreen(new DescriptiveBookScreen(event.getPos(), book.copy()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,43 @@
|
||||
package net.dnorberg.rivencraft.client.screen;
|
||||
|
||||
import net.dnorberg.rivencraft.book.AgeReference;
|
||||
import net.dnorberg.rivencraft.network.UseDescriptiveBookPayload;
|
||||
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.neoforged.neoforge.network.PacketDistributor;
|
||||
|
||||
public final class DescriptiveBookScreen extends Screen {
|
||||
private final AgeReference age;
|
||||
private final InteractionHand hand;
|
||||
private final BlockPos bookStandPos;
|
||||
|
||||
public DescriptiveBookScreen(ItemStack book) {
|
||||
public DescriptiveBookScreen(InteractionHand hand, ItemStack book) {
|
||||
super(book.getHoverName());
|
||||
age = book.get(ModDataComponents.AGE_REFERENCE.get());
|
||||
this.hand = hand;
|
||||
bookStandPos = null;
|
||||
}
|
||||
|
||||
public DescriptiveBookScreen(BlockPos bookStandPos, ItemStack book) {
|
||||
super(book.getHoverName());
|
||||
age = book.get(ModDataComponents.AGE_REFERENCE.get());
|
||||
hand = InteractionHand.MAIN_HAND;
|
||||
this.bookStandPos = bookStandPos.immutable();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
if (age != null) {
|
||||
addRenderableWidget(Button.builder(Component.translatable("screen.rivencraft.book.link"), button -> link())
|
||||
.bounds(width / 2 - 75, height / 2 + 30, 150, 20)
|
||||
.build());
|
||||
}
|
||||
addRenderableWidget(Button.builder(Component.translatable("gui.done"), button -> onClose())
|
||||
.bounds(width / 2 - 75, height / 2 + 55, 150, 20)
|
||||
.build());
|
||||
@@ -36,4 +56,13 @@ public final class DescriptiveBookScreen extends Screen {
|
||||
graphics.drawCenteredString(font, Component.literal(age.id().toString()), centerX, height / 2 + 4, 0xFF777777);
|
||||
}
|
||||
}
|
||||
|
||||
private void link() {
|
||||
PacketDistributor.sendToServer(new UseDescriptiveBookPayload(
|
||||
bookStandPos != null,
|
||||
bookStandPos == null && hand == InteractionHand.OFF_HAND,
|
||||
bookStandPos == null ? BlockPos.ZERO : bookStandPos
|
||||
));
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package net.dnorberg.rivencraft.gametest;
|
||||
import net.dnorberg.rivencraft.Rivencraft;
|
||||
import net.dnorberg.rivencraft.book.LinkTarget;
|
||||
import net.dnorberg.rivencraft.book.LinkingService;
|
||||
import net.dnorberg.rivencraft.item.DescriptiveBookItem;
|
||||
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||
import net.dnorberg.rivencraft.registry.ModItems;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -44,6 +45,23 @@ public final class LinkingGameTests {
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
@GameTest(template = "empty")
|
||||
public static void returnBookRetainsSourceLocation(GameTestHelper helper) {
|
||||
LinkTarget source = new LinkTarget(
|
||||
GlobalPos.of(helper.getLevel().dimension(), helper.absolutePos(ORIGIN)),
|
||||
45.0F,
|
||||
-12.0F
|
||||
);
|
||||
|
||||
ItemStack returnBook = DescriptiveBookItem.createReturnBook(source);
|
||||
|
||||
helper.assertTrue(returnBook.is(ModItems.LINKING_BOOK.get()),
|
||||
"Descriptive Book travel must create a Linking Book");
|
||||
helper.assertValueEqual(returnBook.get(ModDataComponents.LINK_TARGET.get()), source,
|
||||
"The Return Link must retain the source dimension, location, and orientation");
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
@GameTest(template = "empty")
|
||||
public static void travelUsesLeafyDestinationWithoutFloor(GameTestHelper helper) {
|
||||
helper.setBlock(ORIGIN.below(), Blocks.AIR);
|
||||
|
||||
@@ -2,10 +2,18 @@ package net.dnorberg.rivencraft.item;
|
||||
|
||||
import java.util.List;
|
||||
import net.dnorberg.rivencraft.book.AgeReference;
|
||||
import net.dnorberg.rivencraft.book.LinkTarget;
|
||||
import net.dnorberg.rivencraft.book.LinkingService;
|
||||
import net.dnorberg.rivencraft.book.VanillaAgeResolver;
|
||||
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||
import net.dnorberg.rivencraft.registry.ModItems;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.TooltipFlag;
|
||||
@@ -27,6 +35,42 @@ public final class DescriptiveBookItem extends Item {
|
||||
return stack.has(ModDataComponents.AGE_REFERENCE.get()) || super.isFoil(stack);
|
||||
}
|
||||
|
||||
public static boolean useWrittenBook(ServerPlayer player, ItemStack stack) {
|
||||
AgeReference age = stack.get(ModDataComponents.AGE_REFERENCE.get());
|
||||
if (age == null) {
|
||||
player.displayClientMessage(Component.translatable("message.rivencraft.descriptive_book.unwritten"), true);
|
||||
return false;
|
||||
}
|
||||
|
||||
LinkTarget returnTarget = LinkTarget.from(player);
|
||||
LinkTarget ageTarget = VanillaAgeResolver.resolve(player, age).orElse(null);
|
||||
if (ageTarget == null) {
|
||||
player.displayClientMessage(Component.translatable("message.rivencraft.descriptive_book.unavailable"), true);
|
||||
return false;
|
||||
}
|
||||
|
||||
LinkingService.LinkResult result = LinkingService.travel(player, ageTarget);
|
||||
if (result != LinkingService.LinkResult.SUCCESS) {
|
||||
player.displayClientMessage(Component.translatable("message.rivencraft.descriptive_book.unsafe"), true);
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemStack returnBook = createReturnBook(returnTarget);
|
||||
if (!player.getInventory().add(returnBook)) {
|
||||
player.drop(returnBook, false);
|
||||
}
|
||||
player.level().playSound(null, player.blockPosition(), SoundEvents.PORTAL_TRAVEL, SoundSource.PLAYERS, 0.35F, 1.0F);
|
||||
player.displayClientMessage(Component.translatable("message.rivencraft.descriptive_book.linked", age.name()), true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static ItemStack createReturnBook(LinkTarget returnTarget) {
|
||||
ItemStack returnBook = new ItemStack(ModItems.LINKING_BOOK.get());
|
||||
returnBook.set(ModDataComponents.LINK_TARGET.get(), returnTarget);
|
||||
returnBook.set(DataComponents.CUSTOM_NAME, Component.translatable("item.rivencraft.linking_book.return"));
|
||||
return returnBook;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendHoverText(
|
||||
ItemStack stack,
|
||||
|
||||
@@ -3,6 +3,7 @@ package net.dnorberg.rivencraft.network;
|
||||
import net.dnorberg.rivencraft.book.LinkTarget;
|
||||
import net.dnorberg.rivencraft.block.entity.BookStandBlockEntity;
|
||||
import net.dnorberg.rivencraft.client.ClientLinkPreviewHandler;
|
||||
import net.dnorberg.rivencraft.item.DescriptiveBookItem;
|
||||
import net.dnorberg.rivencraft.item.LinkingBookItem;
|
||||
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||
import net.dnorberg.rivencraft.registry.ModItems;
|
||||
@@ -30,9 +31,11 @@ public final class ModNetworking {
|
||||
}
|
||||
|
||||
public static void register(RegisterPayloadHandlersEvent event) {
|
||||
PayloadRegistrar registrar = event.registrar("3");
|
||||
PayloadRegistrar registrar = event.registrar("4");
|
||||
registrar.playToServer(BindLinkingBookPayload.TYPE, BindLinkingBookPayload.STREAM_CODEC, ModNetworking::bindBook);
|
||||
registrar.playToServer(UseLinkingBookPayload.TYPE, UseLinkingBookPayload.STREAM_CODEC, ModNetworking::useBook);
|
||||
registrar.playToServer(UseDescriptiveBookPayload.TYPE, UseDescriptiveBookPayload.STREAM_CODEC,
|
||||
ModNetworking::useDescriptiveBook);
|
||||
registrar.playToServer(UseBookStandPayload.TYPE, UseBookStandPayload.STREAM_CODEC, ModNetworking::useBookStand);
|
||||
registrar.playToServer(RequestLinkPreviewPayload.TYPE, RequestLinkPreviewPayload.STREAM_CODEC,
|
||||
ModNetworking::requestLinkPreview);
|
||||
@@ -85,6 +88,27 @@ public final class ModNetworking {
|
||||
}
|
||||
}
|
||||
|
||||
private static void useDescriptiveBook(UseDescriptiveBookPayload payload, IPayloadContext context) {
|
||||
if (!(context.player() instanceof ServerPlayer player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack stack;
|
||||
if (payload.fromStand()) {
|
||||
if (player.distanceToSqr(Vec3.atCenterOf(payload.standPos())) > 64.0D
|
||||
|| !(player.level().getBlockEntity(payload.standPos()) instanceof BookStandBlockEntity stand)) {
|
||||
return;
|
||||
}
|
||||
stack = stand.getBook();
|
||||
} else {
|
||||
stack = player.getItemInHand(hand(payload.offhand()));
|
||||
}
|
||||
|
||||
if (stack.is(ModItems.DESCRIPTIVE_BOOK.get())) {
|
||||
DescriptiveBookItem.useWrittenBook(player, stack);
|
||||
}
|
||||
}
|
||||
|
||||
private static void requestLinkPreview(RequestLinkPreviewPayload payload, IPayloadContext context) {
|
||||
if (!(context.player() instanceof ServerPlayer player)) {
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package net.dnorberg.rivencraft.network;
|
||||
|
||||
import net.dnorberg.rivencraft.Rivencraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
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;
|
||||
|
||||
/** Requests a server-validated Descriptive Book link from a hand or Book Stand. */
|
||||
public record UseDescriptiveBookPayload(boolean fromStand, boolean offhand, BlockPos standPos)
|
||||
implements CustomPacketPayload {
|
||||
public static final Type<UseDescriptiveBookPayload> TYPE = new Type<>(
|
||||
ResourceLocation.fromNamespaceAndPath(Rivencraft.MOD_ID, "use_descriptive_book")
|
||||
);
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, UseDescriptiveBookPayload> STREAM_CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.BOOL, UseDescriptiveBookPayload::fromStand,
|
||||
ByteBufCodecs.BOOL, UseDescriptiveBookPayload::offhand,
|
||||
BlockPos.STREAM_CODEC, UseDescriptiveBookPayload::standPos,
|
||||
UseDescriptiveBookPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public Type<? extends CustomPacketPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package net.dnorberg.rivencraft.registry;
|
||||
|
||||
import net.dnorberg.rivencraft.Rivencraft;
|
||||
import net.dnorberg.rivencraft.book.VanillaAgeResolver;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.item.CreativeModeTab;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
@@ -18,6 +21,12 @@ public final class ModCreativeTabs {
|
||||
.icon(() -> ModItems.DESCRIPTIVE_BOOK.get().getDefaultInstance())
|
||||
.displayItems((parameters, output) -> {
|
||||
output.accept(ModItems.DESCRIPTIVE_BOOK.get());
|
||||
VanillaAgeResolver.demoAges().forEach(age -> {
|
||||
ItemStack book = new ItemStack(ModItems.DESCRIPTIVE_BOOK.get());
|
||||
book.set(ModDataComponents.AGE_REFERENCE.get(), age);
|
||||
book.set(DataComponents.CUSTOM_NAME, Component.literal(age.name()));
|
||||
output.accept(book);
|
||||
});
|
||||
output.accept(ModItems.LINKING_BOOK.get());
|
||||
})
|
||||
.build());
|
||||
|
||||
@@ -5,12 +5,17 @@
|
||||
"item.rivencraft.linking_book": "Linking Book",
|
||||
"item.rivencraft.linking_book.unbound": "Unbound — sneak-use to bind",
|
||||
"item.rivencraft.linking_book.destination": "%s (%s, %s, %s)",
|
||||
"item.rivencraft.linking_book.return": "Return Link",
|
||||
"message.rivencraft.linking_book.bind_hint": "Sneak-use to bind this book to your current location",
|
||||
"message.rivencraft.linking_book.unbound": "This Linking Book is unbound",
|
||||
"message.rivencraft.linking_book.bound": "Linking Book bound as %s",
|
||||
"message.rivencraft.linking_book.linked": "Linked",
|
||||
"message.rivencraft.linking_book.missing_dimension": "That dimension is unavailable",
|
||||
"message.rivencraft.linking_book.unsafe": "No safe destination was found near the link",
|
||||
"message.rivencraft.descriptive_book.unwritten": "This Descriptive Book is unwritten",
|
||||
"message.rivencraft.descriptive_book.unavailable": "That Age is unavailable",
|
||||
"message.rivencraft.descriptive_book.unsafe": "No safe destination was found in that Age",
|
||||
"message.rivencraft.descriptive_book.linked": "Linked to %s — a Return Link was created",
|
||||
"screen.rivencraft.book.title": "Book title",
|
||||
"screen.rivencraft.book.title_hint": "Name this Linking Book",
|
||||
"screen.rivencraft.book.name_prompt": "Name the book before binding it to this location",
|
||||
|
||||
Reference in new issue
Block a user