Jump to content

how to properly change the world from a client event?


JoeStrout

Recommended Posts

(I apologize for all the questions this week... I'm sure things will "click" for me soon, and later when I know what I'm doing, I promise to repay my karma by helping newbies whenever I can!)

 

I have a ClientChatEvent handler from which I wish to change the world — for starters, just clear a block to air.  I have no trouble telling what block the user is looking at, and I can call world.setBlockToAir and it clears the block locally... but it fails to do an update (so water doesn't flow into the new hole, for example), and of course this is a blatant siding violation.

 

 

But though I think I understand the concept of sides — and the rule that any world changes need to happen on the logical server — I'm still clueless as to how to actually do that.  One post I found here recommended wrapping any state changes in if (!world.isRemote), but when I do that, then world.setBlockToAir simply doesn't get called at all.   Current, non-working code looks like this:

 	@SubscribeEvent
 	public static void onChat(ClientChatEvent event) {
 		String msg = event.getMessage();
 		if (msg.equals("/evanesco")) {
 			// Vanish whatever we're pointed at
 			RayTraceResult trace = PointingAt();   // (helper method, don't worry about it)
 			if (trace == null || trace.typeOfHit == RayTraceResult.Type.MISS) {
 	 			SendStatus("§6Whiff!§r");	 				
 			} else {
 				BlockPos pos = trace.getBlockPos();
                // sort-of works, but is a siding violation, and doesn't update water etc:
				//Minecraft.getMinecraft().world.setBlockToAir(pos);
                // attempt to do it more properly:
 				WorldClient world = Minecraft.getMinecraft().world; 
                if (!world.isRemote) {
                		world.setBlockToAir(pos);
                		System.out.println("Set block to air on the server");
                } else {
                		// (this is what actually happens in my tests)
                		System.out.println("Not on the server, so doing nothing");
                }
                world.playSound(Minecraft.getMinecraft().player, pos, SoundEvents.ENTITY_ENDERMEN_TELEPORT, SoundCategory.PLAYERS, 1.0F, 1.0F);
 				SendStatus("§5Shoop!§r");
 			}
 			event.setCanceled(true);
 		}
 	}

 

Can anyone point me in the right direction for going from my client-side event detection, to changing block states on the server?

Link to comment
Share on other sites

39 minutes ago, diesieben07 said:

But since you just obtained the client world one line earlier

The key bit there being WorldClient world

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.

Link to comment
Share on other sites

13 hours ago, JoeStrout said:

(I apologize for all the questions this week... I'm sure things will "click" for me soon, and later when I know what I'm doing, I promise to repay my karma by helping newbies whenever I can!)

 

I have a ClientChatEvent handler from which I wish to change the world — for starters, just clear a block to air.  I have no trouble telling what block the user is looking at, and I can call world.setBlockToAir and it clears the block locally... but it fails to do an update (so water doesn't flow into the new hole, for example), and of course this is a blatant siding violation.

 

 

But though I think I understand the concept of sides — and the rule that any world changes need to happen on the logical server — I'm still clueless as to how to actually do that.  One post I found here recommended wrapping any state changes in if (!world.isRemote), but when I do that, then world.setBlockToAir simply doesn't get called at all.   Current, non-working code looks like this:


 	@SubscribeEvent
 	public static void onChat(ClientChatEvent event) {
 		String msg = event.getMessage();
 		if (msg.equals("/evanesco")) {
 			// Vanish whatever we're pointed at
 			RayTraceResult trace = PointingAt();   // (helper method, don't worry about it)
 			if (trace == null || trace.typeOfHit == RayTraceResult.Type.MISS) {
 	 			SendStatus("§6Whiff!§r");	 				
 			} else {
 				BlockPos pos = trace.getBlockPos();
                // sort-of works, but is a siding violation, and doesn't update water etc:
				//Minecraft.getMinecraft().world.setBlockToAir(pos);
                // attempt to do it more properly:
 				WorldClient world = Minecraft.getMinecraft().world; 
                if (!world.isRemote) {
                		world.setBlockToAir(pos);
                		System.out.println("Set block to air on the server");
                } else {
                		// (this is what actually happens in my tests)
                		System.out.println("Not on the server, so doing nothing");
                }
                world.playSound(Minecraft.getMinecraft().player, pos, SoundEvents.ENTITY_ENDERMEN_TELEPORT, SoundCategory.PLAYERS, 1.0F, 1.0F);
 				SendStatus("§5Shoop!§r");
 			}
 			event.setCanceled(true);
 		}
 	}

 

Can anyone point me in the right direction for going from my client-side event detection, to changing block states on the server?

Comments for feedback:

1. Since you are handling block replacements, you need to use ServerChatEvent instead of ClientChatEvent.

2. You can just make a new command and register it. execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException method is executed in server-side.

3. Don't use Minecraft.getMinecraft() in server-side once you get the server to run the code, this might crash you or cause errors if on a server.

4. Don't use WorldClient, simply use playerIn.world once you have EntityPlayerMP through CommandBase in execute(...) method.

 

Follow these tips and you'll be set!

...
        @Override
        public String getCommandName()
        {
            return "evanesco";
        }

	@Override
 	public void execute(MinecraftServer serverIn, ICommandSender senderIn, String[] args) throws CommandException
    	{
      	    EntityPlayer playerIn = CommandBase.getCommandSenderAsPlayer(senderIn);
 	    RayTraceResult trace = PointingAt();
 			
 	    if (trace == null || trace.typeOfHit == RayTraceResult.Type.MISS) 
 	    {
 	 	SendStatus("§6Whiff!§r");	 				
 	    } 
 	    else 
 	    {
 		BlockPos pos = trace.getBlockPos();
 			
 		World worldIn = playerIn.world;
            	worldIn.setBlockToAir(pos);
            	worldIn.playSound(/* if you want sound to play for all players */ null /* else if only for this player, playerIn */, pos, SoundEvents.ENTITY_ENDERMEN_TELEPORT, SoundCategory.PLAYERS, 1.0F, 1.0F);
 			
 		SendStatus("§5Shoop!§r");
		CommandBase.notifyCommandListener("commands.evanesco.success", new Object[0]);
            }
    	}
...

I stripped the code for you, if you wish to use it, you may.

 

I hope I helped. Happy modding!

Edited by Differentiation
  • Like 1
Link to comment
Share on other sites

9 hours ago, Differentiation said:

What is in this method? Please show the contents.

Thanks!

Sure, I'd be delighted to actually be on the helpful end around here for a change.  :)

	// Get a RayTraceResult describing whatever the player's looking at (within 32 blocks).
	private static RayTraceResult PointingAt(boolean stopOnLiquid) {
		WorldClient world = Minecraft.getMinecraft().world;
		EntityPlayerSP player = Minecraft.getMinecraft().player;
		Vec3d posVec = new Vec3d(player.posX, player.posY + player.getEyeHeight(), player.posZ);
		Vec3d lookVec = player.getLookVec();
		return world.rayTraceBlocks(posVec, posVec.add(lookVec.scale(32)), stopOnLiquid);
	}

 

  • Like 1
Link to comment
Share on other sites

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

    • They were already updated, and just to double check I even did a cleanup and fresh update from that same page. I'm quite sure drivers are not the problem here. 
    • i tried downloading the drivers but it says no AMD graphics hardware has been detected    
    • Update your AMD/ATI drivers - get the drivers from their website - do not update via system  
    • As the title says i keep on crashing on forge 1.20.1 even without any mods downloaded, i have the latest drivers (nvidia) and vanilla minecraft works perfectly fine for me logs: https://pastebin.com/5UR01yG9
    • Hello everyone, I'm making this post to seek help for my modded block, It's a special block called FrozenBlock supposed to take the place of an old block, then after a set amount of ticks, it's supposed to revert its Block State, Entity, data... to the old block like this :  The problem I have is that the system breaks when handling multi blocks (I tried some fix but none of them worked) :  The bug I have identified is that the function "setOldBlockFields" in the item's "setFrozenBlock" function gets called once for the 1st block of multiblock getting frozen (as it should), but gets called a second time BEFORE creating the first FrozenBlock with the data of the 1st block, hence giving the same data to the two FrozenBlock :   Old Block Fields set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=head] BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@73681674 BlockEntityData : id:"minecraft:bed",x:3,y:-60,z:-6} Old Block Fields set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} Frozen Block Entity set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockPos{x=3, y=-60, z=-6} BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} Frozen Block Entity set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockPos{x=2, y=-60, z=-6} BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} here is the code inside my custom "freeze" item :    @Override     public @NotNull InteractionResult useOn(@NotNull UseOnContext pContext) {         if (!pContext.getLevel().isClientSide() && pContext.getHand() == InteractionHand.MAIN_HAND) {             BlockPos blockPos = pContext.getClickedPos();             BlockPos secondBlockPos = getMultiblockPos(blockPos, pContext.getLevel().getBlockState(blockPos));             if (secondBlockPos != null) {                 createFrozenBlock(pContext, secondBlockPos);             }             createFrozenBlock(pContext, blockPos);             return InteractionResult.SUCCESS;         }         return super.useOn(pContext);     }     public static void createFrozenBlock(UseOnContext pContext, BlockPos blockPos) {         BlockState oldState = pContext.getLevel().getBlockState(blockPos);         BlockEntity oldBlockEntity = oldState.hasBlockEntity() ? pContext.getLevel().getBlockEntity(blockPos) : null;         CompoundTag oldBlockEntityData = oldState.hasBlockEntity() ? oldBlockEntity.serializeNBT() : null;         if (oldBlockEntity != null) {             pContext.getLevel().removeBlockEntity(blockPos);         }         BlockState FrozenBlock = setFrozenBlock(oldState, oldBlockEntity, oldBlockEntityData);         pContext.getLevel().setBlockAndUpdate(blockPos, FrozenBlock);     }     public static BlockState setFrozenBlock(BlockState blockState, @Nullable BlockEntity blockEntity, @Nullable CompoundTag blockEntityData) {         BlockState FrozenBlock = BlockRegister.FROZEN_BLOCK.get().defaultBlockState();         ((FrozenBlock) FrozenBlock.getBlock()).setOldBlockFields(blockState, blockEntity, blockEntityData);         return FrozenBlock;     }  
  • Topics

×
×
  • Create New...

Important Information

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