Jump to content

Level.setBlock(BlockPos, BlockState, int) causes major lag(sometimes...)


Recommended Posts

Posted (edited)

I made a screen and a container and all of that when right clicking a block, it works just fine.

There's a button there I customly made called EnumButton, it gets an enum and whenever you click it it cycles between the values, that works alright.
I also have an apply button that takes the currently selected value in that enum and sets the block's state to it(this block has an EnumProperty of that specific enum, I get the block by using menu.getBlockEntity().getBlockState().getBlock())
All iis working alright..

The enum has two values: BROADCAST, and TARGETED.
When switching from BROADCAST to TARGETED there's 0 lag...
However when switching from TARGETED to BROADCAST there's a good few seconds of unexplained lag...

I divded the Level.setBlock() code to see where it happens, and it happens in the setBlock() itself:

 

System.out.println("Getting Block Pos");
BlockPos pos = blockEntity.getBlockPos();
System.out.println("Setting state");
BlockState nState = state.setValue(RedstoneTransmitterBlock.TRANSMISSION_KIND, newTransmissionKind);
System.out.println("Applying to world");

world.setBlock(pos, nState, 2);
System.out.println("Changed tranmission kind to " + newTransmissionKind);

Only the last print is delayed when switching from TARGETED to BROADCAST, otherwise no delay...
Why would it happen and how can I fix it??

 

EDIT:
This whole code posted here happens only if the current state is different than the button's selected state, so no worries there

Edited by 1Mangomaster1
Posted (edited)

On the screen, on a method called applyChanges() that is called on onClose() (overriding it)

 

	private void applyChanges() {
		RedstoneTransmitterBlockEntity blockEntity = menu.blockEntity();
		
		Level world = blockEntity.getLevel();
		BlockState state = blockEntity.getBlockState();
		RedstoneTransmitterBlock block = (RedstoneTransmitterBlock)state.getBlock();
		
		int newChannel = Integer.parseInt(eb_channel.getValue());
		if (block.channel() != newChannel) {
			block.channel(newChannel);
			System.out.println("Changed channel to " + newChannel);
		}
		
		TransmissionKind currentTransmissionKind = state.getValue(RedstoneTransmitterBlock.TRANSMISSION_KIND);
		TransmissionKind newTransmissionKind = bu_transmissionKind.current();
		if (currentTransmissionKind != newTransmissionKind) {
			System.out.println("Getting Block Pos");
			BlockPos pos = blockEntity.getBlockPos();
			System.out.println("Setting state");
			BlockState nState = state.setValue(RedstoneTransmitterBlock.TRANSMISSION_KIND, newTransmissionKind);
			System.out.println("Applying to world");
			
			world.setBlock(pos, nState, 2);
			System.out.println("Changed tranmission kind to " + newTransmissionKind);
		}
	}


	private void setChannelVisibility() {
		TransmissionKind current = bu_transmissionKind.current();
		boolean showChannel = current == TransmissionKind.BROADCAST;
		
		eb_channel.active = showChannel;
		eb_channel.visible = showChannel;
	}
	
	@Override
	public void onClose() {
		if (bu_transmissionKind.current() == TransmissionKind.BROADCAST && eb_channel.getValue().isBlank())
			return;
		
		applyChanges();
		super.onClose();
	}

 

Edited by 1Mangomaster1
Posted
43 minutes ago, 1Mangomaster1 said:

On the screen

you mean in the Client screen?

if yes you can not do this, blocks must place on server
also do not handle logic on the client, the client is not your friend do not trust him

Posted (edited)

Wait Screen is only on Client??

OOF

And if so where should I handle it? How can I ensure a code will run from the server when I click a button?

Edited by 1Mangomaster1
Posted
1 hour ago, 1Mangomaster1 said:

How can I ensure a code will run from the server when I click a button?

send a network message to the server, and handle there your code (action of the button)

you can read more about it here

Posted (edited)

I have the Level...
I jsut can't read/write it into/from the FriendlyByteBuf so I can't do the encode and decode for the packet.

FriendlyByteBuf has stuff like readBlockPos() and stuff but it doesn't have stuff like readLevel()... then how do I encode and decode it?
(same applies to BlockState).. I thought I'd make something like

	private Level world;
	private BlockPos pos;
	
	private BlockState state;
	
	public ServerboundBlockUpdatePacket(Level world, BlockPos pos, BlockState state) {
		this.world = world;
		this.pos = pos;
		
		this.state = state;
	}


but I can't decode and encode both Level and BlockState..

EDIT:
wait by that do you mean I do not need to pass the world(?)

Edited by 1Mangomaster1
Posted (edited)
31 minutes ago, 1Mangomaster1 said:

wait by that do you mean I do not need to pass the world(?)

yes, you should only send the data which are 100% necessary to come from clients,
since the most logic should be done on server and the most stuff exists on server

if you really need to send the BlockState through Network you can use FriendlyByteBuf#writeWithCodec,
but i would recommend you to avoid this

Edited by Luis_ST
Posted

I've instead decided to go smaller and make a specific packet for the specific change I want, instead of a general BlockState change, which let me only receive a BlockPos, an Enum and an Integer, making it super easy, thank you!

Posted

Wdym validate it? You mean checking if the types match? I do

Basically I have a block that opens a GUI when you click it, in it there are EditBoxes and Buttons to change some block properties, so when you apply those changes I want the server to make the changes not the client, so I have to send it(Screen is on Client)

Posted (edited)
7 minutes ago, 1Mangomaster1 said:

Wdym validate it? You mean checking if the types match? I do

Verify that it is a valid location for the player to access. Think about a hacked client that sends whatever location data it wants.

Remember, the first rule of net code is:
The client is a lying, cheating, bastard.

The only thing you should send to the server is what user input was given.

  • user clicked this button
  • user entered this text
  • user pressed this key

The server already knows what container the user is interacting with, where the user is, what tile entity they've accessed, and its location. You don't need to send that info. All the server does is go "ok, they clicked a button, let me compute the result of that action."

The client can mimic that result, in order to hide the latency, but the server's return update packet will override anything the client did.

Edited by Draco18s

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

Then how can I get the BlockPos, the enum and the int if not by passing them, after all, they can be mimiced too...

Not that in this case it matters all that much...
it just changes the functionality of a redstone component block I make... so someone could change them remotely with a hacked client...
Still you're right tho and it is a healthier practice, but currently it looks like this:

 

private void applyChanges() {
	WirelessBlockEntity blockEntity = menu.blockEntity();
	BlockPos pos = blockEntity.getBlockPos();

	PacketHandler.CHANNEL.sendToServer(new WirelessUpdatePacket(pos, bu_transmissionKind.current(), Integer.parseInt(eb_channel.getValue())));
}

How can I pass those values in a different way? (The packet is actually sent not when the button is clicked, but when you GUI is closed, the reason for that is to not just send a lot of unused stuff if the button is spammed, but rather change the values only when they're relavent, which is when I stop editing them(closing the GUI)

Posted

Also, Assuming I send nothing in there.. Yeah I can get the container.. but not the screen, what it means is I have no way of telling those values I mentioned above(enum and int) as they are the ones defined in the EditBox and Button...

(also the reason in the last post you see me do Integer.parseInt() without check is because the EditBox filter is designed to only allow numbers in)

Posted
17 hours ago, 1Mangomaster1 said:

the enum and the int if not by passing them, after all, they can be mimiced too...

Yes, but those are direct user input choices.

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.

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



×
×
  • Create New...

Important Information

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