Jump to content

Recommended Posts

Posted (edited)

Hi,

 

I've got a little problem with my OpenGL code while drawing a rectangular texture:

  • thousands of little textures will be drawn instead of one texture
  • the texture to display is part of a larger texture, but the wrong part of this huge "spritesheet" is drawn
  • those thousands of little textures are drawn on the wrong place of the screen

 

  • `guiStartX` and `guiStartY`: the x and y positions where the GUI should start
  • `textureX` and `textureY`: the position in the "spritesheet" where the texture can be found
  • `width` and `height`: the width and height of the texture to draw

 

 

mc.getTextureManager().bindTexture(UI_PROGRESS_TEXTURE);
int guiStartX = (mc.displayWidth - 16) / 2;
	int guiStartY = (mc.displayHeight - 51) / 2;
	renderTexturedRect(guiStartX, guiStartY, 176, 51, 16, 51);

renderTexturedRect(int x, int y, int textureX, int textureY, int width, int height) {
	glBegin(GL_QUADS);
	    glTexCoord2f(textureX, textureY);
	    glVertex3i(0, 0, 0);
	    
	    glTexCoord2f(textureX, textureY + height);
	    glVertex3i(0, height + 400, 0);
	    
	    glTexCoord2f(textureX + width, textureY + height);
	    glVertex3i(width + 800, height + 400, 0);
	    
	    glTexCoord2f(textureX + width, textureY);
	    glVertex3i(width + 800, 0, 0);
	    glEnd();

 

  1. One may ask, why not using drawTexturedModalRect from Minecraft itself. The Reason for this is that it is not available in the place where I am drawing.

  2. And why am I using OpenGL directly instead of for example looking into how drawTexturedModalRect renders a rectangle and doing it the same way?:

    The answer to this is that I am rather using OpenGL directly instead of using undocumented code where I do not even know what it is doing and how the stuff is getting onto the screen with using this (like where and which OpenGL calls are being called etc.)

 

Note: I am not familiar with OpenGL 2.1 nor with LWJGL 2 as I've only worked with OpenGL 4.5 (in C++) and it's shaders so far (and very little with LWJGL 3).

 

Thx in advance.

Bektor

Edited by Bektor

Developer of Primeval Forest.

Posted
5 minutes ago, Bektor said:

One may ask, why not using drawTexturedModalRect from Minecraft itself. The Reason for this is that it is not available in the place where I am drawing.

Where are you doing this, then?

 

Also, you're not binding a texture.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted
1 minute ago, Draco18s said:

Where are you doing this, then?

 

RenderGameOverlayEvent.Post calls a custom method which would then execute the code for specific blocks.

 

2 minutes ago, Draco18s said:

, you're not binding a texture.

I'm binding the texture directly above the first line of code I posted. ^^

Developer of Primeval Forest.

Posted
1 hour ago, Bektor said:
  • thousands of little textures will be drawn instead of one texture
  • the texture to display is part of a larger texture, but the wrong part of this huge "spritesheet" is drawn

UVs must be within a range of [0-1] and you are passing a range of [0-16] x [0-51]. That won't work well. By default all MC textures have their wrap S/T set to repeat, and that's why you see "thousands of little textures" - it is actually your texture repeated 16 times on the x axis and 51 times on the y axis.

 

1 hour ago, Bektor said:

those thousands of little textures are drawn on the wrong place of the screen

Well, where are you expecting to see your quad? We can't tell what is wrong with the UI positioning just by looking at the code - although I can take a guess: you should subtract your position offsets after you've found the center of the screen.

 

1 hour ago, Bektor said:

One may ask, why not using drawTexturedModalRect from Minecraft itself. The Reason for this is that it is not available in the place where I am drawing.

There is absolutely nothing stopping you from using static methods in the GUI class. Or copying the method for that matter.

 

1 hour ago, Bektor said:

The answer to this is that I am rather using OpenGL directly instead of using undocumented code where I do not even know what it is doing and how the stuff is getting onto the screen with using this

I still suggest using BufferBuilder/VertexBuffer rather than OpenGL directly. There is a reason MC switched to it entirely. And well, some people might assume that all drawing is indeed done using methods from that class and abuse that.

Posted

Also, why don't use make use of x and y?

I. Stellarium for Minecraft: Configurable Universe for Minecraft! (WIP)

II. Stellar Sky, Better Star Rendering&Sky Utility mod, had separated from Stellarium.

Posted
8 hours ago, V0idWa1k3r said:

Well, where are you expecting to see your quad? We can't tell what is wrong with the UI positioning just by looking at the code - although I can take a guess: you should subtract your position offsets after you've found the center of the screen.

 

Well, I want to place it in the middle of the screen.

8 hours ago, Abastro said:

Also, why don't use make use of x and y?

Well, I used them, thought it didn't went that good. Couldn't see the image anymore, so I started fixing the image first to get it properly drawn as one big image is easier to see then thousands of small images.

 

9 hours ago, V0idWa1k3r said:

I still suggest using BufferBuilder/VertexBuffer rather than OpenGL directly. There is a reason MC switched to it entirely. And well, some people might assume that all drawing is indeed done using methods from that class and abuse that.

I don't see any reason why BufferBuilder/VertexBuffer is the prefered way, especially when I do not know how it is connected to the hole rendering system and how those two classes work internally.

 

9 hours ago, V0idWa1k3r said:

UVs must be within a range of [0-1] and you are passing a range of [0-16] x [0-51]. That won't work well. By default all MC textures have their wrap S/T set to repeat, and that's why you see "thousands of little textures" - it is actually your texture repeated 16 times on the x axis and 51 times on the y axis.

 

Hm, now I am wondering, where does MC the conversion? I mean, when calling drawTexturedModalRect I don't think I ever passed the width of the complete image to draw, only the position and width and height of the part of the image to be drawn nor is the texture passed.

Developer of Primeval Forest.

Posted
7 minutes ago, Bektor said:

Well, I want to place it in the middle of the screen.

Position it in the middle of the screen then. It would be screenWidth/2 - yourWidth/2, screenHeight/2 - yourHeight/2.

 

8 minutes ago, Bektor said:

I don't see any reason why BufferBuilder/VertexBuffer is the prefered way, especially when I do not know how it is connected to the hole rendering system and how those two classes work internally.

It is preferred because it is conventional. I mean in theory you can use java's ImageIO to load images from an InputStream, upload their pixel data to a buffer, setup a GLtexture and link the buffer to the texture every time you need a texture but you are not doing that, are you? Instead you use TextureManager::bindTexture. Same goes for BufferBuilder. And as I've said:

9 hours ago, V0idWa1k3r said:

And well, some people might assume that all drawing is indeed done using methods from that class and abuse that.

As to how it worksL it basically has an internal buffer it fills with data you pass to it. The offsets, strides and elements/vertex are controlled by the VertexFormat specified. Once you invoke draw the buffer is passed to OpenGL and rendered. It is somewhat of a complex yet very flexible wrapper around GL 3.0+ rendering. If you are familiar with GL4.5 you should be familiar with this aswell. As a side bonus it allows for "modern" shaders to be used(and MC does that in some cases).

 

15 minutes ago, Bektor said:

Hm, now I am wondering, where does MC the conversion? I mean, when calling drawTexturedModalRect I don't think I ever passed the width of the complete image to draw, only the position and width and height of the part of the image to be drawn nor is the texture passed.

Let's look at Gui::drawTexturedModalRect. Specifically at it signature:

public void drawTexturedModalRect(int x, int y, int textureX, int textureY, int width, int height)

The parameters I've marked as bold are to be processed into UVs in the method. textureX/Y are the start, and textureX/Y + width/height are the end. How are they processed? Well, let's look into that method. You will quickly notice that the results of manipulations with these values are multiplied by 0.00390625F. What is this magical number? It is 1/256. MC assumes the height/width of a GUI icon to be 256x256 and does calculations based on that assumption. If the texture is bigger in size(say via a resourcepack) it actually does not matter as the range of [0-1] is effectively a percentage and if the code is based around a 256x256 assumption(the variables passed to this method, that is) the end-result percentages are still going to be correct regardless of the texture width/height. 

 

Posted
51 minutes ago, V0idWa1k3r said:

Let's look at Gui::drawTexturedModalRect. Specifically at it signature:

public void drawTexturedModalRect(int x, int y, int textureX, int textureY, int width, int height)

The parameters I've marked as bold are to be processed into UVs in the method. textureX/Y are the start, and textureX/Y + width/height are the end. How are they processed? Well, let's look into that method. You will quickly notice that the results of manipulations with these values are multiplied by 0.00390625F. What is this magical number? It is 1/256. MC assumes the height/width of a GUI icon to be 256x256 and does calculations based on that assumption. If the texture is bigger in size(say via a resourcepack) it actually does not matter as the range of [0-1] is effectively a percentage and if the code is based around a 256x256 assumption(the variables passed to this method, that is) the end-result percentages are still going to be correct regardless of the texture width/height. 

Changing my code to multiply the texture values with 1/256 just let's the complete image disappear.

        glTexCoord2f(textureX * (1/256), textureY * (1/256));
	    glVertex3i(0, 0, 0);
	    
	    glTexCoord2f(textureX * (1/256), (textureY + height) * (1/256));
	    glVertex3i(0, height + 400, 0);
	    
	    glTexCoord2f((textureX + width) * (1/256), (textureY + height) * (1/256));
	    glVertex3i(width + 800, height + 400, 0);
	    
	    glTexCoord2f((textureX + width) * (1/256), textureY * (1/256));
	    glVertex3i(width + 800, 0, 0);

 

51 minutes ago, V0idWa1k3r said:

It is somewhat of a complex yet very flexible wrapper around GL 3.0+ rendering.

Hm, why the hell creates Mojang wrappers around GL 3.0+ rendering instead of using it direclty. I mean, I don't know any graphics card today which doesn't support OpenGL 3.0+, even NVIDIAs GTX 400 series supports OpenGL 4.6 with the newest beta drivers and 4.5 with the newest stable drivers.

Developer of Primeval Forest.

Posted
1 minute ago, Bektor said:

Changing my code to multiply the texture values with 1/256 just let's the complete image disappear.

Your textureX/Y are of an integer type. So is 1. And so is 256. Dividing an integer 1 by an integer 256 prouces you 0 and well, anything *0 is 0.

 

2 minutes ago, Bektor said:

Hm, why the hell creates Mojang wrappers around GL 3.0+ rendering instead of using it direclty.

Why do people create abstractions to begin with? Let's all write our code in assembly! That is sure going to be fun! :P

On a serious note - their BufferBuilder is very flexible - and that is why they did it. As long as you have the format you can easily upload anything you want to it and render it as desired without having to manualy setup GL buffers/arrays/attributes/younameit every time. Heck, you can even have custom formats defined and it will work just fine. Also it allows to easily modify the individual elements of the buffer if needed after they've been uploaded with a single method invocation. Doing it on a raw buffer is... A bit more challenging.

 

5 minutes ago, Bektor said:

don't know any graphics card today which doesn't support OpenGL 3.0+

And yet a lot of people had issues with MC starting to require OpenGL 3.3. I remember having one myself on a linux machine at work due to the way mesa drivers are done.

Posted (edited)
17 minutes ago, V0idWa1k3r said:

Your textureX/Y are of an integer type. So is 1. And so is 256. Dividing an integer 1 by an integer 256 prouces you 0 and well, anything *0 is 0.

 

Always forget this one thing, but it didn't solve the problem. The texture is still gone:

	    glTexCoord2f(textureX * (1.f / 256.f), textureY * (1.f / 256.f));
	    glVertex3i(0, 0, 0);
	    
	    glTexCoord2f(textureX * (1.f / 256.f), (textureY + height) * (1.f / 256.f));
	    glVertex3i(0, height + 400, 0);
	    
	    glTexCoord2f((textureX + width) * (1.f / 256.f), (textureY + height) * (1.f / 256.f));
	    glVertex3i(width + 800, height + 400, 0);
	    
	    glTexCoord2f((textureX + width) * (1.f / 256.f), textureY * (1.f / 256.f));
	    glVertex3i(width + 800, 0, 0);

Hope I didn't forgot a small thing again, thought somehow I hope I did as it would solve the problem faster. ^^

 

17 minutes ago, V0idWa1k3r said:

Why do people create abstractions to begin with? Let's all write our code in assembly! That is sure going to be fun! :P

Assembly makes fun. You can even enforce a PC configuration with this as it won't run anywhere else. xD But why using Assembly, binary with its 01010101 is even better. :P 

 

17 minutes ago, V0idWa1k3r said:

And yet a lot of people had issues with MC starting to require OpenGL 3.3. I remember having one myself on a linux machine at work due to the way mesa drivers are done.

I don't know much about Mesa drivers, except that they got a somewhat stable OpenGL 4.5 support, but why not using official drivers?

And I don't think Minecraft uses OpenGL 3.3, the last version I know for sure they are using is OpenGL 2.1.

Edited by Bektor

Developer of Primeval Forest.

Posted (edited)
35 minutes ago, Bektor said:

The texture is still gone:

Hm, interesting. Can you please elaborate on the word 'gone' in your scenario? I have debugged your code and had no issues with it - apart from the fact that it is rendered across the entire screen(I've got that fixed in my test) and the UVs point at a relatively small region of the image.

Spoiler

 

Here is my test sample:


@Override
public void invoke()
{
    Minecraft.getMinecraft().getTextureManager().bindTexture(new ResourceLocation("minecraft", "textures/gui/demo_background.png"));
    ScaledResolution sRes = new ScaledResolution(Minecraft.getMinecraft());
    int guiStartX = sRes.getScaledWidth() / 2 - 8;
    int guiStartY = sRes.getScaledHeight() / 2 - 26;
    this.renderTexturedRect(guiStartX, guiStartY, 176, 51, 16, 51);
}

void renderTexturedRect(int x, int y, int textureX, int textureY, int width, int height)
{
    GL11.glBegin(GL11.GL_QUADS);
    GL11.glTexCoord2f(textureX * (1.f / 256.f), textureY * (1.f / 256.f));
    GL11.glVertex3i(x, y, 0);

    GL11.glTexCoord2f(textureX * (1.f / 256.f), (textureY + height) * (1.f / 256.f));
    GL11.glVertex3i(x, y + height, 0);

    GL11.glTexCoord2f((textureX + width) * (1.f / 256.f), (textureY + height) * (1.f / 256.f));
    GL11.glVertex3i(x + width, y + height, 0);

    GL11.glTexCoord2f((textureX + width) * (1.f / 256.f), textureY * (1.f / 256.f));
    GL11.glVertex3i(x + width, y, 0);
    GL11.glEnd();
}

 

 

 

 

Edited by V0idWa1k3r
Posted (edited)
19 minutes ago, V0idWa1k3r said:

Hm, interesting. Can you please elaborate on the word 'gone' in your scenario? I have debugged your code and had no issues with it - apart from the fact that it is rendered across the entire screen and the UVs point at a relatively small region of the image.

 

Ok, fixed it with changing the first 51 parameter from my method to 0.

 

19 minutes ago, V0idWa1k3r said:

Here is my test sample:

Hm, I'm wondering what this invoke method is.

Edited by Bektor

Developer of Primeval Forest.

Posted (edited)
8 minutes ago, Bektor said:

Even when implementing your ScaledResolution thing, it is still not drawn in the screen. Also when changing my glVertex3i to yours it is not drawn inside of the screen.

I suppose that would be because of these lines:

8 minutes ago, Bektor said:

int guiStartX = (sRes.getScaledWidth() - 16) / 2;

int guiStartY = (sRes.getScaledHeight() - 51) / 2;

 

8 minutes ago, Bektor said:

Hm, I'm wondering what this invoke method is.

A part of my testing framework that I use to debug code from people when I do not see an obvious issue.

 

8 minutes ago, Bektor said:

With gone I mean that it is nowhere to be seen on the screen as if it wouldn't be rendered at all, thought the code to render it is still be called.

Well, I am at a loss here then as this identical code works for me. If you don't mind could you please setup a github repo of your project(or a minimal part of it that allows the issue to be reproduced reliably) that can be cloned and debugged locally. I could then debug it with your exact conditions to try to figure out what exactly is wrong.

EDIT: even though you've fixed it I am still curious as to what the issue was exactly. Was it a texture issue? 

Edited by V0idWa1k3r
Posted (edited)
3 minutes ago, V0idWa1k3r said:
11 minutes ago, Bektor said:

int guiStartX = (sRes.getScaledWidth() - 16) / 2;

int guiStartY = (sRes.getScaledHeight() - 51) / 2;

 

Fixed with changing the first 51 parameter from my method to 0. The ScaledResolution thing also solved problems with disappering textures when changing the screen size, which I didn't even noticed before. :) 

 

3 minutes ago, V0idWa1k3r said:

EDIT: even though you've fixed it I am still curious as to what the issue was exactly. Was it a texture issue? 

I suppose it was an issue with missing ScaledResolution and me having the reading direction for OpenGL texture coordinates wrong in my head.

Edited by Bektor

Developer of Primeval Forest.

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

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

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