Jump to content

Recommended Posts

Posted

I'm trying to figure out how to get a player's Doggy Talents dogs and have all of their information saved on a separate file on the machine; and finally, when the player use the whistle item to call their dogs, it should (basically) recreate the dog using the data that was saved previously and have the 'new' dog spawn around the either the dog's owner or whoever used the whistle. However, there is some issues i need addressed, which are the following below:

  • What would be the best and most reasonable way of doing this
  • How can I actually save what is inside the dog's inventory, as the dogs do possess the ability to carry things (if they have the talent of course), in the same manner as the vanilla Donkey/Horse does.
  • How often this should save, as to avoid data lost in case a player accidentally calls the dog before the data was saved or if the dog despawns
  • How should this method deal with non-owner players (those whom do not own the dog, but the owner has allowed the dog to be manipulated by others.

I did in a previous attempt make a way to call the dogs, but it did not account for despawning due to the chunk where the dog is located at being despawned. Here is the repo in 1.12.2: https://github.com/ProPercivalalb/DoggyTalents/tree/master/src-mc-universal

Main Developer and Owner of Zero Quest

Visit the Wiki for more information

If I helped anyone, please give me a applaud and a thank you!

Posted

Well, Minecraft is just Java so all the file I/O libraries are available. So you could just come up with your own file format. But you could also hook into some of the built-in formats to help you. For example, if you have the data in NBT already then there are already methods for writing and reading NBT.

 

But do you really need to store these in a separate file? Because you can also put all this stuff into a world save data instance and have that saved together with the regular game saves. Thanks to diesieben07 for this tip.

 

There is are built in classes for saving data: WorldSavedData. How you do it depends if you want to store it per-dimension (different data for e.g. nether and overworld, these are two World instances in the code) or per save-file (same data for all dimensions).  For the former use world.perWorldStorage, otherwise use world.mapStorage.

Warning: Be sure to always call markDirty() on your data when you change a value, otherwise Minecraft will not save it.
 

You can see diesieben07's simple example here: WorldSaveData example cod

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted
16 minutes ago, jabelar said:

Well, Minecraft is just Java so all the file I/O libraries are available. So you could just come up with your own file format. But you could also hook into some of the built-in formats to help you. For example, if you have the data in NBT already then there are already methods for writing and reading NBT.

 

But do you really need to store these in a separate file? Because you can also put all this stuff into a world save data instance and have that saved together with the regular game saves. Thanks to diesieben07 for this tip.

 

There is are built in classes for saving data: WorldSavedData. How you do it depends if you want to store it per-dimension (different data for e.g. nether and overworld, these are two World instances in the code) or per save-file (same data for all dimensions).  For the former use world.perWorldStorage, otherwise use world.mapStorage.

Warning: Be sure to always call markDirty() on your data when you change a value, otherwise Minecraft will not save it.
 

You can see diesieben07's simple example here: WorldSaveData example cod

I feel the best way to store the data per-dimension basis.

Main Developer and Owner of Zero Quest

Visit the Wiki for more information

If I helped anyone, please give me a applaud and a thank you!

Posted
3 minutes ago, NovaViper said:

I feel the best way to store the data per-dimension basis.

Well, it depends on how your mod works. If you store it per-dimension then if you whistle for your dog while in the Nether you will only get dogs saved in the Nether.

 

In any case, the point is that is it very simple to save additional custom data to the world save file. NBT format is quite suitable for that. So then you just need to write the methods to take the data and put it into NBT and read it back. For the most part it should be fairly easy, although sometimes using NBT with lots of compounds and lists can be a bit confusing. So just work through it carefully. The concept is simple, but the details are important when dealing with files.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

  • 6 months later...
Posted

I know it has been a while since I've commented on this, but this is what the main developer has came up with so far. However, he hasn't been able to finish the implementation of the WorldSave data class, so I'm trying to pick up on where he left off. So far, he got the dog's location to actually save, but I haven't been able to figure out how the remove the location data when the dog dies or the owner untames it. I'm also trying to figure out what would be the best possible way to basically create a dog that contains all of the data of the original dog (with all the talent data, and the data that's within the talent, PackPuppy since it actually creates a Container to store items like a chest).

This gist contains all of the files of the current progress so far: https://gist.github.com/NovaViper/5364aebd06bb9c2f704e89f1a2878512

Main Developer and Owner of Zero Quest

Visit the Wiki for more information

If I helped anyone, please give me a applaud and a thank you!

Posted (edited)
9 hours ago, diesieben07 said:

Death: EntityLivingBase#onDeath.

Untaming: Well, just do it when you run the untaming code, I don't know where you do that.

I mean I'm not sure how to actually remove the data out of the storage all together, I did create a piece of code, but because I never really messed around with storing data and such in Minecraft, I'm not exactly sure where to start off with it:

 

	public void removeDog(EntityDog dog) {
		DogLocation temp = new DogLocation(dog);

/*		for (int i = 0; i < this.locations.size(); i++) {
			DogLocation loc = this.locations.get(i);

			if (loc.equals(temp)) {
				this.locations.remove(i);
				this.markDirty();
				return;
			}
		}*/

		this.locations.remove(temp);
		this.markDirty();
	}

 

--Edit--

Nevermind, I just got the code working. Now my issue is trying to figure out how to actually store all of the data that the dog has in the NBT (from the talents to its storage containers that is created from the talents).

Edited by NovaViper

Main Developer and Owner of Zero Quest

Visit the Wiki for more information

If I helped anyone, please give me a applaud and a thank you!

Posted (edited)

Hm.. strangely enough, whenever the dog dies, it doesn't get removed from the World Storage.. as the debug item I'm using (the radar item) checks the queue size (it should start off as 0) seems to have it stored (it says 1 entry is stored instead of 0) while when I untame the dog, the dog gets purged from the storage

 

https://gist.github.com/NovaViper/5364aebd06bb9c2f704e89f1a2878512

 

--Edit--

I figured out it. I discovered that because the LivingUpdate method is almost constantly running, even when the dog died, the LivingUpdate section ran (which loaded the dog's data again)

Edited by NovaViper

Main Developer and Owner of Zero Quest

Visit the Wiki for more information

If I helped anyone, please give me a applaud and a thank you!

Posted

@diesieben07 I made several changes (following your advice with using the uniqueID method instead of the former) since earlier today and this warning starting coming up.

[17:32:04] [Server thread/WARN] [minecraft/EntityDataManager]: defineId called for: class doggytalents.entity.EntityDog from class doggytalents.entity.DogDataTracker

 

Also, whenever the dogs end up being sent to another dimension (say the nether or the end) while I remain in the original dimension they were in, I get this error when trying to use the radar item to locate them:

 

[17:42:42] [Server thread/FATAL] [minecraft/MinecraftServer]: Error executing task
java.util.concurrent.ExecutionException: java.lang.NullPointerException
	at java.util.concurrent.FutureTask.report(FutureTask.java:122) ~[?:1.8.0_191]
	at java.util.concurrent.FutureTask.get(FutureTask.java:192) ~[?:1.8.0_191]
	at net.minecraft.util.Util.runTask(Util.java:54) [Util.class:?]
	at net.minecraft.server.MinecraftServer.updateTimeLightAndEntities(MinecraftServer.java:798) [MinecraftServer.class:?]
	at net.minecraft.server.MinecraftServer.tick(MinecraftServer.java:743) [MinecraftServer.class:?]
	at net.minecraft.server.integrated.IntegratedServer.tick(IntegratedServer.java:192) [IntegratedServer.class:?]
	at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:592) [MinecraftServer.class:?]
	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_191]
Caused by: java.lang.NullPointerException
	at doggytalents.helper.DogLocationManager$DogLocation.getDog(DogLocationManager.java:173) ~[DogLocationManager$DogLocation.class:?]
	at doggytalents.item.ItemRadar.onItemRightClick(ItemRadar.java:43) ~[ItemRadar.class:?]
	at net.minecraft.item.ItemStack.useItemRightClick(ItemStack.java:234) ~[ItemStack.class:?]
	at net.minecraft.server.management.PlayerInteractionManager.processRightClick(PlayerInteractionManager.java:384) ~[PlayerInteractionManager.class:?]
	at net.minecraft.network.NetHandlerPlayServer.processTryUseItem(NetHandlerPlayServer.java:796) ~[NetHandlerPlayServer.class:?]
	at net.minecraft.network.play.client.CPacketPlayerTryUseItem.processPacket(CPacketPlayerTryUseItem.java:43) ~[CPacketPlayerTryUseItem.class:?]
	at net.minecraft.network.play.client.CPacketPlayerTryUseItem.processPacket(CPacketPlayerTryUseItem.java:9) ~[CPacketPlayerTryUseItem.class:?]
	at net.minecraft.network.PacketThreadUtil$1.run(PacketThreadUtil.java:21) ~[PacketThreadUtil$1.class:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[?:1.8.0_191]
	at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[?:1.8.0_191]
	at net.minecraft.util.Util.runTask(Util.java:53) ~[Util.class:?]
	... 5 more

 

 

Here are the changes I made: https://gist.github.com/NovaViper/5364aebd06bb9c2f704e89f1a2878512/51d0950e50f956d4db5b6e2ce49965e42c7961d1

Main Developer and Owner of Zero Quest

Visit the Wiki for more information

If I helped anyone, please give me a applaud and a thank you!

Posted
2 minutes ago, diesieben07 said:

Your code is not even syntax-highlighted... But for something complicated like this I would need a working Git repository to actually debug this. Which you should also do, by the way. The debugger works wonders.

Alrighty, here's the link to the specific commit I just added to Github: https://github.com/percivalalb/DoggyTalents/commit/6590b944d4656f0e284d336a838d1cf4f752fc38

Main Developer and Owner of Zero Quest

Visit the Wiki for more information

If I helped anyone, please give me a applaud and a thank you!

Posted

I noticed another strange issue, whenever the dog gets sent to another dimension, it ends up adding itself twice to the location manager list, and its AI seems to respond at a slowed rate than before (sometimes its AI doesn't execute at all)

Main Developer and Owner of Zero Quest

Visit the Wiki for more information

If I helped anyone, please give me a applaud and a thank you!

Posted

Have you stepped through the execution with the debugger?

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Posted
1 hour ago, Cadiboo said:

Have you stepped through the execution with the debugger?

Uh.. I'm not sure how to do that

Main Developer and Owner of Zero Quest

Visit the Wiki for more information

If I helped anyone, please give me a applaud and a thank you!

Posted

The debugger is an essential and amazingly powerful tool.

http://www.vogella.com/tutorials/EclipseDebugging/article.html

place a breakpoint and then step through the execution

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Posted

Hm.. I am noticing that the debugger is throwing this error after removing one of the dogs:

 

locationManager.locations.get(1) = {IndexOutOfBoundsException@10226} Method threw 'java.lang.IndexOutOfBoundsException' exception.
 detailMessage = "Index: 1, Size: 1"
 cause = {IndexOutOfBoundsException@10226} "java.lang.IndexOutOfBoundsException: Index: 1, Size: 1"
 stackTrace = {StackTraceElement[18]@10389} 
 suppressedExceptions = {Collections$UnmodifiableRandomAccessList@10386}  size = 0

 And this one whenever I use the radar item at all:

Cannot find local variable 'loc'

 

Main Developer and Owner of Zero Quest

Visit the Wiki for more information

If I helped anyone, please give me a applaud and a thank you!

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

    • It is 1.12.2 - I have no idea if there is a 1.12 pack
    • Okay, but does the modpack works with 1.12 or just with 1.12.2, because I need the Forge client specifically for Minecraft 1.12, not 1.12.2
    • 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() ); } }  
  • Topics

×
×
  • Create New...

Important Information

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