Jump to content

[SOLVED][1.10+] Advice / Information on Safely Adding a Novel Biome Provider


Recommended Posts

Posted (edited)

Really looking more for general advice, info, or insight on this one...

 

I have a WIP biome placement mod (more of a biome zones mod than a biomes mod per se).  Basically it places biomes based on a quasi-realistic climate model, using a system I developed outside Minecraft in a tool I made for experimenting with procedural content generation (especially maps).  I've inserted my system into Minecraft as a custom world type.  The worlds it makes seems great at this point, but there is a serious problem I need to fix before I go further with things like configurability and updating:

 

I find the game using far more memory than I think it should, and exploring so that new terrain is generated causes the memory usage to increase without really going back down much (something I take as a sign memory is leaking).  Note that I don't use any genlayers at all, having completely replaced the vanilla system and simply returning biome arrays where needed.  Other than my own system (which I'm looking into, but I don't think has an internal problem), does anyone have any idea what might cause this?  Also, I see it producing save files for distant parts of the world, far from where a player have been (things like "r.16.-20.mca" when I've never been more than a couple thousand blocks from spawn), and suspect that may be a system of the same underlaying problem, probably based on not interfacing my biome provider correctly with the vanilla chunk provider and world in general.

 

I don't have a specific ease to nail down bug on this one, or specific code I can point to on this one, and don't really expect a specific answer, but if anyone has and potentially useful information or advice on doing things like this I'd be thankful to see it.

Edited by JaredBGreat

Developer of Doomlike Dungeons.

Posted

Without logs I can only take a guess, but I know from my experience one thing I had an issue with when modifying world generation was a "Cascading world generation" issue where each time you created a new chunk, regardless of it being in render distance, it would cause the next chunk after it to be created as well. When I was working on this issue for my mod this what was causing my lag during world generation and was fixed when I saw a logic hole in my code. Aside from that I can offer no guidance

  • Like 1
Posted

I don't know why people write code, watch it behave badly, and then start asking others what might be wrong when you can simply debug it yourself. Basically you simply need to trace the execution. I tend to do this by adding console statements (System.out.println()) statements at strategic locations of the code. I usually put such a statement in each method I want to confirm is being called (usually printing out the parameter values as well), and I also put statements inside any conditional statements to check that my if-statements and similar logic are executing as expected.

 

You can also use the debugger in Eclipse by setting breakpoints and such, but I personally find that less useful because much of what I want to watch is repeated operation during actual gameplay.

 

I'm pretty sure that by adding such traceability to your code it will be quickly apparent what is going wrong -- in this case you might see things being called more frequently than expected, or chunks being generated when not expected, and you can simply keep working backwards to find the source of the problem.

 

It is really hard to ask people on a forum to debug something like this because it is usually a very specific logic bug or typo/mistake that is really your responsibility to track down.

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

Posted (edited)
12 hours ago, daruskiy said:

Without logs I can only take a guess, but I know from my experience one thing I had an issue with when modifying world generation was a "Cascading world generation" issue where each time you created a new chunk, regardless of it being in render distance, it would cause the next chunk after it to be created as well. When I was working on this issue for my mod this what was causing my lag during world generation and was fixed when I saw a logic hole in my code. Aside from that I can offer no guidance

Thanks.  I don't think that exactly is the problem, as it doesn't become slow unless it runs out of memory completely and starts having lag spikes, and extra files are generally empty.  I'll take a look.

 

9 hours ago, jabelar said:

[...]

Thanks for accusing me of doing things I did not do, asking for favors I did not ask for, and telling me things I already know, and assuming I don't know how to (or won't) do something I've done many, many times.  I was simply asking if anyone had any good information on the sub-systems involved, not for anyone to debug anything.  Anyway, I think I'll pass on your suggestion to create a huge core mod that adds debugging code to a large portion of the game.

 

EDIT:  Nevermind -- I don't want to start a fight.  I was just hoping people who'd done similar things might have some general advice on pitfalls they may have encountered or know about, insights that might be useful.

 

I'm removing the link to the biome provider so no one will think I'm asking for someone to actually debug it -- for the curious its not hard to find.

Edited by JaredBGreat

Developer of Doomlike Dungeons.

Posted
1 hour ago, JaredBGreat said:

Thanks for accusing me of doing things I did not do, asking for favors I did not ask for, and telling me things I already know, and assuming I don't know how to (or won't) do something I've done many, many times.  I was simply asking if anyone had any good information on the sub-systems involved, not for anyone to debug anything.  Anyway, I think I'll pass on your suggestion to create a huge core mod that adds debugging code to a large portion of the game.

Yes, it probably sounds accusatory (sorry) but was meant more as "tough love". Teaching you to learn things yourself. I'm only suggesting this because I feel you're ready for pushing to the next level of self-sufficiency.

 

There are two types of problems -- common ones in an area of general modding interest, and esoteric ones that are mostly personal interest. There are also two types of people asking questions -- those who are weak in programming and those that are strong. You have an esoteric interest and are a strong programmer so proper help is to teach the skills to debug. If you were asking a common question and were weak programmer I'd spoon feed you the answer...

 

No core mod is required. So I think you misunderstood my suggestion. Even though you don't suspect your own code, it is still most likely that the problem originates there (or in the way that it hooks into the vanilla code). There are certainly possible bugs in vanilla code (found one yesterday), but even then your code must have exposed it.

 

So start with instrumenting your own code. By the way, this is a good habit to get into anyway -- a proper code validation needs to confirm every code path. With your own classes confirm that they are (a) being called as expected (b) not taking excessive time in their own right.

 

If it is an actual memory leak, your IDE has ability to monitor that. Depending on your development environment google some instructions on debugging Java memory leaks. For example, Eclipse has the Memory Analyzer Tool. https://eclipsesource.com/blogs/2013/01/21/10-tips-for-using-the-eclipse-memory-analyzer/

 

 Lastly, if you really need to modify the vanilla classes you just copy them and add your stuff -- replacing chunk providers is a standard modding activity. This is just standard debug practice and worth learning, especially since tracing the execution of the vanilla code teaches a LOT about how Minecraft is architected. But you can also use the debugger in your IDE and set breakpoints.

 

Also, there is a built-in profiler that times some sections in the vanilla code which can help tell you if things are taking longer than expected.

 

So again sorry I sounded rude, but intended to be more like a sports coach yelling at you to push harder to get to next level. You can do it! I promise if you do the above suggestions you'll feel significant advancement in your programming prowess.

  • Like 1

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

Posted

Well, I'd slept late and just rolled out of bed, so I was pretty grouchy myself.

 

Anyway, it may be that my own code is generating too many objects.  I started writing some memory management of my own in hopes it would be easier to profile by inserting debug code into that.  Then I realized I could add code to just count the objects, and got things like this:

 

[16:11:25] [Server thread/INFO] [STDOUT]: [jaredbgreat.climaticbiome.generation.BetterBiomeProvider:cleanupCache:98]: There are...
[16:11:25] [Server thread/INFO] [STDOUT]: [jaredbgreat.climaticbiome.generation.BetterBiomeProvider:cleanupCache:99]:      180511 Basin nodes
[16:11:25] [Server thread/INFO] [STDOUT]: [jaredbgreat.climaticbiome.generation.BetterBiomeProvider:cleanupCache:100]:      9284 BiomeBasins
[16:11:25] [Server thread/INFO] [STDOUT]: [jaredbgreat.climaticbiome.generation.BetterBiomeProvider:cleanupCache:101]:      37139 ChunkTiles
[16:11:25] [Server thread/INFO] [STDOUT]: [jaredbgreat.climaticbiome.generation.BetterBiomeProvider:cleanupCache:102]:      3341 Regions
[16:11:25] [Server thread/INFO] [STDOUT]: [jaredbgreat.climaticbiome.generation.BetterBiomeProvider:cleanupCache:103]:      10031 Spatial noise generators

 

The numbers do go up and down a bit, so apparently its not a leak, they do get garbage collected.  But apparently I'm creating far more of these than I ever imagined (especially noise generators, which I thought I had only one in, and basin nodes -- well, especially everything I guess).  So, I need to figure out (1) where these are all coming from and (2) and good way to manage them.

 

Thanks.  Sorry for getting snarky earlier.

Developer of Doomlike Dungeons.

Posted
1 hour ago, JaredBGreat said:

Well, I'd slept late and just rolled out of bed, so I was pretty grouchy myself.

 

Anyway, it may be that my own code is generating too many objects.  I started writing some memory management of my own in hopes it would be easier to profile by inserting debug code into that.  Then I realized I could add code to just count the objects, and got things like this:

 


[16:11:25] [Server thread/INFO] [STDOUT]: [jaredbgreat.climaticbiome.generation.BetterBiomeProvider:cleanupCache:98]: There are...
[16:11:25] [Server thread/INFO] [STDOUT]: [jaredbgreat.climaticbiome.generation.BetterBiomeProvider:cleanupCache:99]:      180511 Basin nodes
[16:11:25] [Server thread/INFO] [STDOUT]: [jaredbgreat.climaticbiome.generation.BetterBiomeProvider:cleanupCache:100]:      9284 BiomeBasins
[16:11:25] [Server thread/INFO] [STDOUT]: [jaredbgreat.climaticbiome.generation.BetterBiomeProvider:cleanupCache:101]:      37139 ChunkTiles
[16:11:25] [Server thread/INFO] [STDOUT]: [jaredbgreat.climaticbiome.generation.BetterBiomeProvider:cleanupCache:102]:      3341 Regions
[16:11:25] [Server thread/INFO] [STDOUT]: [jaredbgreat.climaticbiome.generation.BetterBiomeProvider:cleanupCache:103]:      10031 Spatial noise generators

 

The numbers do go up and down a bit, so apparently its not a leak, they do get garbage collected.  But apparently I'm creating far more of these than I ever imagined (especially noise generators, which I thought I had only one in, and basin nodes -- well, especially everything I guess).  So, I need to figure out (1) where these are all coming from and (2) and good way to manage them.

 

Thanks.  Sorry for getting snarky earlier.

 

Cool. See that is progress! Once you have observability pretty much every programming problem is easy to solve.

 

Just keep working back and testing your assumptions on how things are working.

 

With regards to creating too many of something, just check out every where the constructor is called and ask whether it is really needed. You'll find that either you do need that many references but maybe instead of creating a new one you can retain one for reuse. Or you'll find that the whole section of code that is constructing it is being called unnecessarily.

 

Sounds like you're on track. But keep the thread posted. And if you get truly stuck of course we'll pitch in to help.

  • Like 1

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

Posted (edited)

Well, I'm done with this for a couple days (I plan to visit my sister for Christmas tomorrow).

 

For now, I've added some memory management (caches with object pools for frequently used classes).  Most of my object are far less common than they were.  However, overall memory is still awfully high (not a big change), and I have found that some classes acquire new instances way too fast and don't let go of them -- notable the "Regions" (4096x4096 areas which each get landmass and climate nodes / attractors).  This leads me to think that my hunch that something is activating world gen way too far out or simply at incorrect coordinates.  I don't think my world gen is "rolling" and causing more on its own -- it only spreads while travelling and cause no lag (unless memory is depleted).  I'm not so much suspecting a bug in the vanilla code per se, as that I'm not interfacing with it correctly somewhere -- a case of playing with forces I don't understand.

 

Anyway, thanks for now.

 

EDIT: One of my experiments crashed with a null pointer in ChunkProviderServer, so maybe that's a good place to start looking for something that is asking my biome provider I wasn't expecting.

Edited by JaredBGreat

Developer of Doomlike Dungeons.

Posted (edited)

I've done more tests.  Applying some of the profiling out put in my original experimenting tool gives results like this when my biome system is run outside Minecraft:

 

Total memory usage: 
          USED MEMORY: 115 MB
     486 Basin nodes
     19449 BiomeBasins
     102781 ChunkTiles
     9 Regions
     4 Spatial noise generators

 

Where memory can spike to over 500 MB.  This has me confused as doing a few calculation on the objects it reports existing (based on the fields they contain and the size of the primitive types) only accounts for about 8 MB.  But it does appear my generation system may simply use far more memory than I realized.  Perhaps its too greedy for what it does, at least in the context of what most people would want and expect.

 

The I suspect the extra save files are from somewhere else.  I'm using a round-about way of getting data into the biomeCache, since its private in BiomeFinder; specifically ignoring the cache flag and always writing into the cache when it calls getBiomes(), since that is the only access I have (the way I wrote getBiomesForGeneration() requires this to work since it gets its data from the biomeCache instead of a genlayer like in vanilla).  What I suspect is happening is that something is calling this for distant locations (perhaps to check for possible structure locations of similar), and its not supposed to be cached when this happens.  Put differently, the problem comes from completely skipping the genlayers -- to fix this I'd need have my biome data written into a genlayer and accessed from there, so that I can leave the biomeCache to function as it was intended.  (Extra continental regions would also be a bi-product, as it would need the correct climate data for that location.)

 

I'm not sure I can get the memory usage down to an acceptable level -- not with this way of doing thing.  A system that was more memory efficient would simply not be at all the same system, and this system will never really be memory efficient; it just requires too much data (every chunk requires calculation on 48 fake chunks for one thing -- the calculation is fast but its a lot of extra proto-chunks being stored).

Edited by JaredBGreat

Developer of Doomlike Dungeons.

Posted (edited)

If you're going to take a different approach, what about considering the ReplaceBiomeBlocks event? That event gets the world so you could try to access the biomes array and let the normal generation continue with the revised biomes. However, I just tried and it is tough because you'd need to use the following crazy code event.getWorld().getBiomeProvider().getBiomes(event.getWorld().getWorldInfo().getTerrainType().getChunkGenerator().biomesForGeneration, event.getX() * 16, event.getZ() * 16, 16, 16) and the biomesForGeneration is private so you'd have to use Java reflection. I think I'll put a pull request in to have the event updated to provide the biomes array as well, so in the future this will be easier.

 

Alternatively, you could copy the generation code (based on world type) into your event handler since the event also gets the ChunkPrimer and cancel the vanilla generation. 

 

Doing it this way should solve any problems with architectural issues on how you hook into the generation. After that it really depends on how efficient your algorithm is for coming up with the replacement biomes. If you still have memory problems at that point, it must be your algorithm.

Edited by jabelar

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

Posted (edited)

I've been away a few days, and haven't really looked at the event yet.  I often don't think of events for things like this.  I do know that getBiomesForGeneration() returns an array representing a 10x10 of locations at every four blocks which is used by the chunk provider to generate height maps that average across biome boundaries, and that getBiomes() returns and array of biomes within a chunk on a one block resolution and is used by the biome decorator to place things like trees and dirt.  I also have found there is something else (maybe several location) in accessing the biome cache directly without going through the biome provider, and that this is used for placing structures (writing to the biome array everything is treated as plains, so no temples, plains village in the deserts, etc.) -- I have no idea where that is so far.

 

I don't know if using the event would prevent the extra save files.  I do suspect that the memory usage will be high no mater what I do -- at least as long as I use this kind of large-scale land and climate model.

 

In the mean time I have been thinking of making a better caching system for my data -- well, that and having my mind wander back to my other project (possibly Doomlike Dungeons II or something along those lines).

Edited by JaredBGreat

Developer of Doomlike Dungeons.

  • 2 weeks later...
Posted

One last update and then I'll let this die, since people were wondering what I'd figure out:

 

First, vanilla terrain gen in 1.10.x is more memory intensive than I realized at first.  The last time I looked at that it was for an older version (probably 1.6).  It seems my system may not actually use much if any more memory, and if a lot of terrain is generated quickly (e.g., flying around at high speed in spectator mode) with no modded world shows that vanilla will run out of memory and start to choke at least as fast (maybe faster, it seemed that way but I didn't time it).  My system also seems to generate terrain a bit faster than vanilla (though it has no rivers or beaches).  So maybe that isn't a problem.

 

Second, I think I've isolated the source of the extra save files, and know why they're so small.  It seems that the method areBiomesViable() in BiomeProvider is frequently run on random seeming, distant chunks.  The extra files also will appear without any modded world gen in 1.10, but do not appear in 1.12.  So I suspect these are being generated by an attempt to find a locations for large structures (woodland mansions?), and the generated region files is a side effect of generating these chunks which then get saved, a minor problem that was fixed in newer game versions --  and that it has nothing to do with the mod, and was never really an issue.

 

So that's it basically -- I was overly concerned about nothing, mistaking normal game behavior for a problem I'd caused; looking for solutions for problems that weren't really there because I didn't have a good understanding of how it was supposed to work.  If I get more done on this, the next post will probably be in a new thread in the mods section, and not too soon (I'm very busy again now).  Thanks everyone for all the help and advice.

  • Like 1

Developer of Doomlike Dungeons.

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

    • So me and a couple of friends are playing with a shitpost mod pack and one of the mods in the pack is corail tombstone and for some reason there is a problem with it, where on death to fire the player will get kicked out of the server and the tombstone will not spawn basically deleting an entire inventory, it doesn't matter what type of fire it is, whether it's from vanilla fire/lava, or from modded fire like ice&fire/lycanites and it's common enough to where everyone on the server has experienced at least once or twice and it doesn't give any crash log. a solution to this would be much appreciated thank you!
    • 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.