JoeStrout Posted December 28, 2017 Posted December 28, 2017 (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? Quote
Draco18s Posted December 29, 2017 Posted December 29, 2017 39 minutes ago, diesieben07 said: But since you just obtained the client world one line earlier The key bit there being WorldClient world Quote 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.
JoeStrout Posted December 29, 2017 Author Posted December 29, 2017 That all makes great sense. And it's good to know about registering commands — while I'm currently just doing quick hacks to explore various bits of the API, I may as well do them properly! Quote
Differentiation Posted December 29, 2017 Posted December 29, 2017 (edited) 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 December 29, 2017 by Differentiation 1 Quote
Differentiation Posted December 29, 2017 Posted December 29, 2017 18 hours ago, JoeStrout said: PointingAt() What is in this method? Please show the contents. Thanks! Quote
JoeStrout Posted December 30, 2017 Author Posted December 30, 2017 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); } 1 Quote
Recommended Posts
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.