Public Access
This commit is contained in:
1 parent
aed8d6a5c3
commit
3e8cbf6bc5
12 files changed
+233
-14
No files matched your search
@@ -4,11 +4,10 @@ Rivencraft is a clean, incremental port of the ideas and gameplay of
|
|||||||
[Mystcraft Legacy](https://github.com/Mystcraft/Mystcraft-Legacy) to Minecraft
|
[Mystcraft Legacy](https://github.com/Mystcraft/Mystcraft-Legacy) to Minecraft
|
||||||
1.21.1 on NeoForge.
|
1.21.1 on NeoForge.
|
||||||
|
|
||||||
The bootstrap milestone is complete and books/linking is in progress. The
|
The bootstrap and books/linking milestones are complete. The project currently
|
||||||
project currently provides an unwritten Descriptive Book and a functional
|
provides functional Linking Books and temporary vanilla-dimension Descriptive
|
||||||
Linking Book with persistent, dimension-aware destinations and safe landing
|
Book destinations with generated Return Links. Age generation, symbols,
|
||||||
checks. Age generation, symbols, instability, and writing mechanics have not
|
instability, and writing mechanics have not been ported yet.
|
||||||
been ported yet.
|
|
||||||
|
|
||||||
## Linking Book
|
## Linking Book
|
||||||
|
|
||||||
@@ -17,7 +16,7 @@ air. An unbound book opens a screen where it can be named and bound to the
|
|||||||
current location. A bound book opens its destination screen and travels when
|
current location. A bound book opens its destination screen and travels when
|
||||||
its preview panel is clicked. The destination is stored on the item and
|
its preview panel is clicked. The destination is stored on the item and
|
||||||
survives inventory and world saves. Travel only succeeds if the target
|
survives inventory and world saves. Travel only succeeds if the target
|
||||||
dimension is loaded and a solid, collision-free landing position can be found
|
dimension is loaded and a non-fluid, passable landing position can be found
|
||||||
nearby.
|
nearby.
|
||||||
|
|
||||||
Right-click an empty vanilla lectern with a Rivencraft book to convert it into
|
Right-click an empty vanilla lectern with a Rivencraft book to convert it into
|
||||||
@@ -32,6 +31,13 @@ rotating Ponder scene on the left page, backed by the
|
|||||||
destination biome's sky color. The snapshot is visual only; the server
|
destination biome's sky color. The snapshot is visual only; the server
|
||||||
independently validates every link.
|
independently validates every link.
|
||||||
|
|
||||||
|
## Descriptive Book
|
||||||
|
|
||||||
|
Until generated Ages arrive in Milestone 3, written Descriptive Books resolve
|
||||||
|
deterministically to the Overworld, Nether, or End according to their Age UUID.
|
||||||
|
The Rivencraft creative tab includes one sample book for each. Linking creates
|
||||||
|
a bound Return Link to the exact source position and orientation.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- A Java 21 JDK
|
- A Java 21 JDK
|
||||||
|
|||||||
+7
-3
@@ -10,7 +10,7 @@ split into independently testable milestones.
|
|||||||
1. **Bootstrap** — NeoForge 1.21.1 build, mod metadata, item registration, and
|
1. **Bootstrap** — NeoForge 1.21.1 build, mod metadata, item registration, and
|
||||||
CI. **Complete.**
|
CI. **Complete.**
|
||||||
2. **Books and linking** — descriptive/linking book data components, lectern-like
|
2. **Books and linking** — descriptive/linking book data components, lectern-like
|
||||||
interaction, safe teleportation, and return links. **In progress.**
|
interaction, safe teleportation, and return links. **Complete.**
|
||||||
3. **Ages** — dimension definitions backed by data packs, deterministic seeds,
|
3. **Ages** — dimension definitions backed by data packs, deterministic seeds,
|
||||||
lifecycle management, and persistence.
|
lifecycle management, and persistence.
|
||||||
4. **Writing system** — pages, symbols, notebooks, desks, and book assembly.
|
4. **Writing system** — pages, symbols, notebooks, desks, and book assembly.
|
||||||
@@ -42,10 +42,10 @@ the symbol grammar is introduced.
|
|||||||
- [x] Persistent, network-synchronized link-target data component.
|
- [x] Persistent, network-synchronized link-target data component.
|
||||||
- [x] Linking Book binding with dimension, block position, yaw, and pitch.
|
- [x] Linking Book binding with dimension, block position, yaw, and pitch.
|
||||||
- [x] Server-authoritative cross-dimension travel with world-border, collision,
|
- [x] Server-authoritative cross-dimension travel with world-border, collision,
|
||||||
fluid, and floor checks.
|
and fluid checks.
|
||||||
- [x] Nearby landing search and clear failure messages.
|
- [x] Nearby landing search and clear failure messages.
|
||||||
- [x] Descriptive Book identity and Age reference components.
|
- [x] Descriptive Book identity and Age reference components.
|
||||||
- [ ] Return-link creation as part of Descriptive Book travel.
|
- [x] Temporary vanilla-dimension Descriptive Book travel with return-link creation.
|
||||||
- [x] Lectern-to-Book-Stand conversion, displayed book, activation, removal,
|
- [x] Lectern-to-Book-Stand conversion, displayed book, activation, removal,
|
||||||
persistence, and drops.
|
persistence, and drops.
|
||||||
- [x] Book-screen foundation with server-authoritative naming, binding, and
|
- [x] Book-screen foundation with server-authoritative naming, binding, and
|
||||||
@@ -145,6 +145,10 @@ unchanged.
|
|||||||
Version 0.1.21 treats air and leaf blocks as passable linking destinations;
|
Version 0.1.21 treats air and leaf blocks as passable linking destinations;
|
||||||
links no longer require a solid floor beneath the player.
|
links no longer require a solid floor beneath the player.
|
||||||
|
|
||||||
|
Version 0.2.0 completes Milestone 2. Until Milestone 3 provides generated Age
|
||||||
|
dimensions, Descriptive Book UUIDs deterministically resolve to vanilla
|
||||||
|
dimensions and create a bound Return Link to the source on successful travel.
|
||||||
|
|
||||||
The preview code is separated behind a renderer interface and uses an isolated
|
The preview code is separated behind a renderer interface and uses an isolated
|
||||||
Ponder level populated from server-authoritative destination snapshots.
|
Ponder level populated from server-authoritative destination snapshots.
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ mod_name=Rivencraft
|
|||||||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||||
mod_license=LGPL-3.0-or-later
|
mod_license=LGPL-3.0-or-later
|
||||||
# The mod version. See https://semver.org/
|
# The mod version. See https://semver.org/
|
||||||
mod_version=0.1.21
|
mod_version=0.2.0
|
||||||
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||||
# This should match the base package used for the mod sources.
|
# This should match the base package used for the mod sources.
|
||||||
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||||
|
|||||||
@@ -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) {
|
if (linkingBook) {
|
||||||
Minecraft.getInstance().setScreen(new LinkingBookScreen(event.getHand(), event.getItemStack().copy()));
|
Minecraft.getInstance().setScreen(new LinkingBookScreen(event.getHand(), event.getItemStack().copy()));
|
||||||
} else {
|
} 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())) {
|
if (book.is(ModItems.LINKING_BOOK.get())) {
|
||||||
Minecraft.getInstance().setScreen(new LinkingBookScreen(event.getPos(), book.copy()));
|
Minecraft.getInstance().setScreen(new LinkingBookScreen(event.getPos(), book.copy()));
|
||||||
} else if (book.is(ModItems.DESCRIPTIVE_BOOK.get())) {
|
} 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;
|
package net.dnorberg.rivencraft.client.screen;
|
||||||
|
|
||||||
import net.dnorberg.rivencraft.book.AgeReference;
|
import net.dnorberg.rivencraft.book.AgeReference;
|
||||||
|
import net.dnorberg.rivencraft.network.UseDescriptiveBookPayload;
|
||||||
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.components.Button;
|
import net.minecraft.client.gui.components.Button;
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.neoforged.neoforge.network.PacketDistributor;
|
||||||
|
|
||||||
public final class DescriptiveBookScreen extends Screen {
|
public final class DescriptiveBookScreen extends Screen {
|
||||||
private final AgeReference age;
|
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());
|
super(book.getHoverName());
|
||||||
age = book.get(ModDataComponents.AGE_REFERENCE.get());
|
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
|
@Override
|
||||||
protected void init() {
|
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())
|
addRenderableWidget(Button.builder(Component.translatable("gui.done"), button -> onClose())
|
||||||
.bounds(width / 2 - 75, height / 2 + 55, 150, 20)
|
.bounds(width / 2 - 75, height / 2 + 55, 150, 20)
|
||||||
.build());
|
.build());
|
||||||
@@ -36,4 +56,13 @@ public final class DescriptiveBookScreen extends Screen {
|
|||||||
graphics.drawCenteredString(font, Component.literal(age.id().toString()), centerX, height / 2 + 4, 0xFF777777);
|
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.Rivencraft;
|
||||||
import net.dnorberg.rivencraft.book.LinkTarget;
|
import net.dnorberg.rivencraft.book.LinkTarget;
|
||||||
import net.dnorberg.rivencraft.book.LinkingService;
|
import net.dnorberg.rivencraft.book.LinkingService;
|
||||||
|
import net.dnorberg.rivencraft.item.DescriptiveBookItem;
|
||||||
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||||
import net.dnorberg.rivencraft.registry.ModItems;
|
import net.dnorberg.rivencraft.registry.ModItems;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
@@ -44,6 +45,23 @@ public final class LinkingGameTests {
|
|||||||
helper.succeed();
|
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")
|
@GameTest(template = "empty")
|
||||||
public static void travelUsesLeafyDestinationWithoutFloor(GameTestHelper helper) {
|
public static void travelUsesLeafyDestinationWithoutFloor(GameTestHelper helper) {
|
||||||
helper.setBlock(ORIGIN.below(), Blocks.AIR);
|
helper.setBlock(ORIGIN.below(), Blocks.AIR);
|
||||||
|
|||||||
@@ -2,10 +2,18 @@ package net.dnorberg.rivencraft.item;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import net.dnorberg.rivencraft.book.AgeReference;
|
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.ModDataComponents;
|
||||||
|
import net.dnorberg.rivencraft.registry.ModItems;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.network.chat.MutableComponent;
|
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.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.TooltipFlag;
|
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);
|
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
|
@Override
|
||||||
public void appendHoverText(
|
public void appendHoverText(
|
||||||
ItemStack stack,
|
ItemStack stack,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package net.dnorberg.rivencraft.network;
|
|||||||
import net.dnorberg.rivencraft.book.LinkTarget;
|
import net.dnorberg.rivencraft.book.LinkTarget;
|
||||||
import net.dnorberg.rivencraft.block.entity.BookStandBlockEntity;
|
import net.dnorberg.rivencraft.block.entity.BookStandBlockEntity;
|
||||||
import net.dnorberg.rivencraft.client.ClientLinkPreviewHandler;
|
import net.dnorberg.rivencraft.client.ClientLinkPreviewHandler;
|
||||||
|
import net.dnorberg.rivencraft.item.DescriptiveBookItem;
|
||||||
import net.dnorberg.rivencraft.item.LinkingBookItem;
|
import net.dnorberg.rivencraft.item.LinkingBookItem;
|
||||||
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
import net.dnorberg.rivencraft.registry.ModDataComponents;
|
||||||
import net.dnorberg.rivencraft.registry.ModItems;
|
import net.dnorberg.rivencraft.registry.ModItems;
|
||||||
@@ -30,9 +31,11 @@ public final class ModNetworking {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void register(RegisterPayloadHandlersEvent event) {
|
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(BindLinkingBookPayload.TYPE, BindLinkingBookPayload.STREAM_CODEC, ModNetworking::bindBook);
|
||||||
registrar.playToServer(UseLinkingBookPayload.TYPE, UseLinkingBookPayload.STREAM_CODEC, ModNetworking::useBook);
|
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(UseBookStandPayload.TYPE, UseBookStandPayload.STREAM_CODEC, ModNetworking::useBookStand);
|
||||||
registrar.playToServer(RequestLinkPreviewPayload.TYPE, RequestLinkPreviewPayload.STREAM_CODEC,
|
registrar.playToServer(RequestLinkPreviewPayload.TYPE, RequestLinkPreviewPayload.STREAM_CODEC,
|
||||||
ModNetworking::requestLinkPreview);
|
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) {
|
private static void requestLinkPreview(RequestLinkPreviewPayload payload, IPayloadContext context) {
|
||||||
if (!(context.player() instanceof ServerPlayer player)) {
|
if (!(context.player() instanceof ServerPlayer player)) {
|
||||||
return;
|
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;
|
package net.dnorberg.rivencraft.registry;
|
||||||
|
|
||||||
import net.dnorberg.rivencraft.Rivencraft;
|
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.core.registries.Registries;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.item.CreativeModeTab;
|
import net.minecraft.world.item.CreativeModeTab;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.neoforged.bus.api.IEventBus;
|
import net.neoforged.bus.api.IEventBus;
|
||||||
import net.neoforged.neoforge.registries.DeferredHolder;
|
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||||
@@ -18,6 +21,12 @@ public final class ModCreativeTabs {
|
|||||||
.icon(() -> ModItems.DESCRIPTIVE_BOOK.get().getDefaultInstance())
|
.icon(() -> ModItems.DESCRIPTIVE_BOOK.get().getDefaultInstance())
|
||||||
.displayItems((parameters, output) -> {
|
.displayItems((parameters, output) -> {
|
||||||
output.accept(ModItems.DESCRIPTIVE_BOOK.get());
|
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());
|
output.accept(ModItems.LINKING_BOOK.get());
|
||||||
})
|
})
|
||||||
.build());
|
.build());
|
||||||
|
|||||||
@@ -5,12 +5,17 @@
|
|||||||
"item.rivencraft.linking_book": "Linking Book",
|
"item.rivencraft.linking_book": "Linking Book",
|
||||||
"item.rivencraft.linking_book.unbound": "Unbound — sneak-use to bind",
|
"item.rivencraft.linking_book.unbound": "Unbound — sneak-use to bind",
|
||||||
"item.rivencraft.linking_book.destination": "%s (%s, %s, %s)",
|
"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.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.unbound": "This Linking Book is unbound",
|
||||||
"message.rivencraft.linking_book.bound": "Linking Book bound as %s",
|
"message.rivencraft.linking_book.bound": "Linking Book bound as %s",
|
||||||
"message.rivencraft.linking_book.linked": "Linked",
|
"message.rivencraft.linking_book.linked": "Linked",
|
||||||
"message.rivencraft.linking_book.missing_dimension": "That dimension is unavailable",
|
"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.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": "Book title",
|
||||||
"screen.rivencraft.book.title_hint": "Name this Linking Book",
|
"screen.rivencraft.book.title_hint": "Name this Linking Book",
|
||||||
"screen.rivencraft.book.name_prompt": "Name the book before binding it to this location",
|
"screen.rivencraft.book.name_prompt": "Name the book before binding it to this location",
|
||||||
|
|||||||
Reference in new issue
Block a user