Jump to content

Recommended Posts

Posted

Hello! I have a book that writes down a players life information. I.e. how long they’ve been living, and when they die on that life, how long they lasted and what they died from.

My question is, is it possible to update content in a written book when it’s opened? Becuase the way it works now, you have to get the book with a command instead of it refreshing it’s content automatically.

Any help would be extremely helpful! Thank you.

Here’s a pastebin of the current class that writes in the book: https://pastebin.com/s6X05Nzz

  • 2 weeks later...
Posted (edited)

I'm assuming that it is just a vanilla book with custom NBT, not your own custom item?

If that is the case you could listen to the use item event (sorry, I can't remember the name of it off the top of my head), check for the book being used (opened) and sync the data with the item.

Edited by Alpvax
Posted
28 minutes ago, Alpvax said:

If that is the case you could listen to the use item event (sorry, I can't remember the name of it off the top of my head), check for the book being used (opened) and sync the data with the item.

you mean LivingEntityUseItemEvent.Start?

Posted
On 9/24/2021 at 2:45 PM, Luis_ST said:

you mean LivingEntityUseItemEvent.Start?

No, I actually meant PlayerInteractEvent.RightClickItem, books don't count as being "used" I don't think

  • 2 weeks later...
Posted (edited)
On 9/27/2021 at 7:21 AM, Alpvax said:

No, I actually meant PlayerInteractEvent.RightClickItem, books don't count as being "used" I don't think

sorry for such a late response! i actually tried that before, but it would always show the player who opened it info. like i would open your book, and it would display my players capability information instead of yours.

https://pastebin.com/3qpwP8BF

Edited by jayxo
Posted

yeah! i was saying that in reference to this

1 hour ago, diesieben07 said:
  • Why is this a real written book that you try to keep up to date instead of just using a custom book with a screen that just displays the correct info?
Posted
On 10/11/2021 at 4:21 PM, jayxo said:

it would always show the player who opened it info.

https://pastebin.com/3qpwP8BF

I assumed that was what you wanted. Currently you get the capability of the player opening the book:

@SubscribeEvent
public void onUse(PlayerInteractEvent.RightClickItem event) {
    PlayerEntity player = event.getPlayer();
    ...

You need to use the player who "owns" the book instead. So store the "owner's" id in the book somehow (NBT/capablility), then when the book is opened, get the player from the id (they may not exist/be offline, in which case you can't reasonably update), then update the data.

It might be a better idea to change to a level (overworld) capability / saveddata if you need to access the data for offline players, then retrieve that data for displaying in your book.

As die7 says, it will probably be better to create a custom gui (based on the vanilla book screen, you can possibly subclass it, although I'm not sure), then retrieve the latest data on demand, rather than updating the item nbt each time.

Posted
1 hour ago, Alpvax said:

I assumed that was what you wanted. Currently you get the capability of the player opening the book

this was actually what one of the issues i had was, i wanted to be able to look at any players book without it always showing my capability information.

1 hour ago, Alpvax said:

You need to use the player who "owns" the book instead. So store the "owner's" id in the book somehow (NBT/capablility), then when the book is opened, get the player from the id (they may not exist/be offline, in which case you can't reasonably update), then update the data.

i actually do have a bit that adds a “owner” nbt data in the book! it stores the players uuid if that would work? i just didn’t know how i would check if it’s connected to the players capability.

in response to the level/world capability, how would i actually do that? im not sure if it’s useful, also eventually wanted to be able to use a command to get a players book even if they’re offline.

Posted
18 minutes ago, jayxo said:

it stores the players uuid if that would work?

Yes, I think that is linked to the player's GameProfile, so should never change.

13 minutes ago, jayxo said:

in response to the level/world capability, how would i actually do that? im not sure if it’s useful, also eventually wanted to be able to use a command to get a players book even if they’re offline.

The same way you attach the capability to the player, but just attach a capability with a Map<player id, your current player capability data> to the overworld Level instead of attaching the data to the player directly. Then when you want to modify the data, get the capability from the overworld (which is always loaded) and get the data for the player with the given id.

Alternatively, you could use Saved Data (which used to be WorldSavedData), again using the overworld to access the player data.

That way it doesn't matter if the player is offline, because the data is saved to the overworld, so you just need to look up the data for the player by player id.

  • Thanks 1
Posted

this has actually helped out a lot! I definitely think the best option is using WorldSavedData.

On 10/13/2021 at 8:45 AM, Alpvax said:

Map<player id, your current player capability data>

I've now gotten Map<UUID, *>, but for the players capability data, what kind of information would I put there?

Posted
17 hours ago, jayxo said:

for the players capability data, what kind of information would I put there?

Whatever you used to have attached to each player (only now it doesn't need to be a capability). All the stuff you want to display in the book

Posted

Ohh alright! I've gotten a .dat file to load into a world, but I'm not sure where to go from here.

public class StatsManager extends WorldSavedData implements Supplier {
    public Map<UUID, DefaultStatsCapability> players = new HashMap<>();
    public PlayerEntity player = Minecraft.getInstance().player;

    public StatsManager() {
        super(XLife.MOD_ID);
    }

    public void load(CompoundNBT nbt) {
        players.put(player.getUUID(), StatsCapabilityProvider.stats);
    }

    public CompoundNBT save(CompoundNBT nbt) {
        ListNBT listNBT = new ListNBT();

        for (DefaultStatsCapability stats : players.values()) {
            stats.save(nbt);
            listNBT.add(nbt);
        }

        nbt.put("Players", listNBT);
        return nbt;
    }

    public static StatsManager onWorld(ServerWorld world) {
        DimensionSavedDataManager storage = world.getDataStorage();
        StatsManager sup = new StatsManager();
        StatsManager saver = (StatsManager) storage.computeIfAbsent(sup, XLife.MOD_ID);

        storage.set(saver);

        return saver;
    }

    public static void onSaved(WorldEvent.Save event) {
        if (!event.getWorld().isClientSide() && event.getWorld() instanceof ServerWorld) {
            StatsManager saver = StatsManager.onWorld((ServerWorld) event.getWorld());

            saver.setDirty();
        }
    }

    @Override
    public Object get() {
        return this;
    }

}

I've used RaidManager as somewhat of a template for this, and the forge forums you linked.

Posted
On 10/15/2021 at 3:55 PM, jayxo said:
public class StatsManager extends WorldSavedData implements Supplier

Why are you implementing Supplier? (Also, you should include the generic type of Supplier).

You probably want to add a getter for a specific player. something like the following:

public DefaultStatsCapability getStats(UUID playerID) {
    // Bear in mind this can return null if the player stats have not been added yet.
    // You might want to use computeIfAbsent instead.
    return players.get(playerID);
}

Then wherever you need the DefaultStatsCapability (for example in your book update function), get your instance of StatsManager, then get the DSC from it using the new getter.

On 10/15/2021 at 3:55 PM, jayxo said:
public PlayerEntity player = Minecraft.getInstance().player;

A) This will crash on servers (which is where loading and saving of data occurs)

B) What is this for? All the players should be saved in the map.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Version 1.19 - Forge 41.0.63 I want to create a wolf entity that I can ride, so far it seems to be working, but the problem is that when I get on the wolf, I can’t control it. I then discovered that the issue is that the server doesn’t detect that I’m riding the wolf, so I’m struggling with synchronization. However, it seems to not be working properly. As I understand it, the server receives the packet but doesn’t register it correctly. I’m a bit new to Java, and I’ll try to provide all the relevant code and prints *The comments and prints are translated by chatgpt since they were originally in Spanish* Thank you very much in advance No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. MountableWolfEntity package com.vals.valscraft.entity; import com.vals.valscraft.network.MountSyncPacket; import com.vals.valscraft.network.NetworkHandler; import net.minecraft.client.Minecraft; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.Entity; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.network.PacketDistributor; public class MountableWolfEntity extends Wolf { private boolean hasSaddle; private static final EntityDataAccessor<Byte> DATA_ID_FLAGS = SynchedEntityData.defineId(MountableWolfEntity.class, EntityDataSerializers.BYTE); public MountableWolfEntity(EntityType<? extends Wolf> type, Level level) { super(type, level); this.hasSaddle = false; } @Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(DATA_ID_FLAGS, (byte)0); } public static AttributeSupplier.Builder createAttributes() { return Wolf.createAttributes() .add(Attributes.MAX_HEALTH, 20.0) .add(Attributes.MOVEMENT_SPEED, 0.3); } @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack itemstack = player.getItemInHand(hand); if (itemstack.getItem() == Items.SADDLE && !this.hasSaddle()) { if (!player.isCreative()) { itemstack.shrink(1); } this.setSaddle(true); return InteractionResult.SUCCESS; } else if (!level.isClientSide && this.hasSaddle()) { player.startRiding(this); MountSyncPacket packet = new MountSyncPacket(true); // 'true' means the player is mounted NetworkHandler.CHANNEL.sendToServer(packet); // Ensure the server handles the packet return InteractionResult.SUCCESS; } return InteractionResult.PASS; } @Override public void travel(Vec3 travelVector) { if (this.isVehicle() && this.getControllingPassenger() instanceof Player) { System.out.println("The wolf has a passenger."); System.out.println("The passenger is a player."); Player player = (Player) this.getControllingPassenger(); // Ensure the player is the controller this.setYRot(player.getYRot()); this.yRotO = this.getYRot(); this.setXRot(player.getXRot() * 0.5F); this.setRot(this.getYRot(), this.getXRot()); this.yBodyRot = this.getYRot(); this.yHeadRot = this.yBodyRot; float forward = player.zza; float strafe = player.xxa; if (forward <= 0.0F) { forward *= 0.25F; } this.flyingSpeed = this.getSpeed() * 0.1F; this.setSpeed((float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 1.5F); this.setDeltaMovement(new Vec3(strafe, travelVector.y, forward).scale(this.getSpeed())); this.calculateEntityAnimation(this, false); } else { // The wolf does not have a passenger or the passenger is not a player System.out.println("No player is mounted, or the passenger is not a player."); super.travel(travelVector); } } public boolean hasSaddle() { return this.hasSaddle; } public void setSaddle(boolean hasSaddle) { this.hasSaddle = hasSaddle; } @Override protected void dropEquipment() { super.dropEquipment(); if (this.hasSaddle()) { this.spawnAtLocation(Items.SADDLE); this.setSaddle(false); } } @SubscribeEvent public static void onServerTick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) { MinecraftServer server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); if (server != null) { for (ServerPlayer player : server.getPlayerList().getPlayers()) { if (player.isPassenger() && player.getVehicle() instanceof MountableWolfEntity) { MountableWolfEntity wolf = (MountableWolfEntity) player.getVehicle(); System.out.println("Tick: " + player.getName().getString() + " is correctly mounted on " + wolf); } } } } } private boolean lastMountedState = false; @Override public void tick() { super.tick(); if (!this.level.isClientSide) { // Only on the server boolean isMounted = this.isVehicle() && this.getControllingPassenger() instanceof Player; // Only print if the state changed if (isMounted != lastMountedState) { if (isMounted) { Player player = (Player) this.getControllingPassenger(); // Verify the passenger is a player System.out.println("Server: Player " + player.getName().getString() + " is now mounted."); } else { System.out.println("Server: The wolf no longer has a passenger."); } lastMountedState = isMounted; } } } @Override public void addPassenger(Entity passenger) { super.addPassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(true)); } } } @Override public void removePassenger(Entity passenger) { super.removePassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is no longer mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(false)); } } } @Override public boolean isControlledByLocalInstance() { Entity entity = this.getControllingPassenger(); return entity instanceof Player; } @Override public void positionRider(Entity passenger) { if (this.hasPassenger(passenger)) { double xOffset = Math.cos(Math.toRadians(this.getYRot() + 90)) * 0.4; double zOffset = Math.sin(Math.toRadians(this.getYRot() + 90)) * 0.4; passenger.setPos(this.getX() + xOffset, this.getY() + this.getPassengersRidingOffset() + passenger.getMyRidingOffset(), this.getZ() + zOffset); } } } MountSyncPacket package com.vals.valscraft.network; import com.vals.valscraft.entity.MountableWolfEntity; import net.minecraft.network.FriendlyByteBuf; 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 net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class MountSyncPacket { private final boolean isMounted; public MountSyncPacket(boolean isMounted) { this.isMounted = isMounted; } public void encode(FriendlyByteBuf buffer) { buffer.writeBoolean(isMounted); } public static MountSyncPacket decode(FriendlyByteBuf buffer) { return new MountSyncPacket(buffer.readBoolean()); } public void handle(NetworkEvent.Context context) { context.enqueueWork(() -> { ServerPlayer player = context.getSender(); // Get the player from the context if (player != null) { // Verifies if the player has dismounted if (!isMounted) { Entity vehicle = player.getVehicle(); if (vehicle instanceof MountableWolfEntity wolf) { // Logic to remove the player as a passenger wolf.removePassenger(player); System.out.println("Server: Player " + player.getName().getString() + " is no longer mounted."); } } } }); context.setPacketHandled(true); // Marks the packet as handled } } networkHandler package com.vals.valscraft.network; import com.vals.valscraft.valscraft; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class NetworkHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel( new ResourceLocation(valscraft.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals ); public static void init() { int packetId = 0; // Register the mount synchronization packet CHANNEL.registerMessage( packetId++, MountSyncPacket.class, MountSyncPacket::encode, MountSyncPacket::decode, (msg, context) -> msg.handle(context.get()) // Get the context with context.get() ); } }  
    • Do you use features of inventory profiles next (ipnext) or is there a change without it?
    • Remove rubidium - you are already using embeddium, which is a fork of rubidium
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.