Skip to content

Commit e7bfed4

Browse files
committed
TargetPlace
1 parent d2c61de commit e7bfed4

File tree

5 files changed

+373
-0
lines changed

5 files changed

+373
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,11 @@ Because so many of these mods are entirely original, I plan to release some of t
302302
![Popup](https://i.imgur.com/W97borj.png)
303303
![GUI](https://i.imgur.com/eunLgVr.png)
304304

305+
### TargetPlace
306+
- Select a block by pressing P, when that block is broken or destroyed (turned to air) it is automatically replaced with the block you're holding and it will be orientated down. This works from a distance as well.
307+
- Deselect by holding SHIFT + P
308+
- Useful for aiding in the breaking of bedrock with TNT and pistons. New piston can be spawned in the same tick as explosion, breaking bedrock all while just standing near it.
309+
305310

306311
## What’s changed or improved in this fork?
307312

src/main/java/net/wurstclient/command/CmdList.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public final class CmdList
6363
public final SettingsCmd settingsCmd = new SettingsCmd();
6464
public final NecoCmd NecoCmd = new NecoCmd();
6565
public final TCmd tCmd = new TCmd();
66+
public final TargetPlaceCmd targetPlaceCmd = new TargetPlaceCmd();
6667
public final net.wurstclient.commands.ItemHandlerCmd itemHandlerCmd =
6768
new net.wurstclient.commands.ItemHandlerCmd();
6869
public final TooManyHaxCmd tooManyHaxCmd = new TooManyHaxCmd();
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright (c) 2014-2025 Wurst-Imperium and contributors.
3+
*
4+
* This source code is subject to the terms of the GNU General Public
5+
* License, version 3. If a copy of the GPL was not distributed with this
6+
* file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
7+
*/
8+
package net.wurstclient.commands;
9+
10+
import net.wurstclient.Category;
11+
import net.wurstclient.WurstClient;
12+
import net.wurstclient.command.CmdException;
13+
import net.wurstclient.command.CmdSyntaxError;
14+
import net.wurstclient.command.Command;
15+
import net.wurstclient.hacks.TargetPlaceHack;
16+
import net.wurstclient.util.ChatUtils;
17+
18+
public final class TargetPlaceCmd extends Command
19+
{
20+
public TargetPlaceCmd()
21+
{
22+
super("targetplace", "Selects a block for TargetPlace or unselects it.",
23+
".targetplace");
24+
setCategory(Category.BLOCKS);
25+
}
26+
27+
@Override
28+
public void call(String[] args) throws CmdException
29+
{
30+
if(args.length > 0)
31+
throw new CmdSyntaxError();
32+
33+
TargetPlaceHack hack = WurstClient.INSTANCE.getHax().targetPlaceHack;
34+
if(!hack.isEnabled())
35+
{
36+
ChatUtils.error("TargetPlace is disabled.");
37+
return;
38+
}
39+
40+
if(!hack.handleActivation())
41+
ChatUtils.error("TargetPlace did nothing.");
42+
}
43+
}

src/main/java/net/wurstclient/hack/HackList.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ public final class HackList implements UpdateListener
203203
public final ThrowHack throwHack = new ThrowHack();
204204
public final TillauraHack tillauraHack = new TillauraHack();
205205
public final TimerHack timerHack = new TimerHack();
206+
public final TargetPlaceHack targetPlaceHack = new TargetPlaceHack();
206207
public final TiredHack tiredHack = new TiredHack();
207208
public final TooManyHaxHack tooManyHaxHack = new TooManyHaxHack();
208209
public final TpAuraHack tpAuraHack = new TpAuraHack();
Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
/*
2+
* Copyright (c) 2014-2025 Wurst-Imperium and contributors.
3+
*
4+
* This source code is subject to the terms of the GNU General Public
5+
* License, version 3. If a copy of the GPL was not distributed with this
6+
* file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
7+
*/
8+
package net.wurstclient.hacks;
9+
10+
import java.awt.Color;
11+
import com.mojang.blaze3d.platform.InputConstants;
12+
import com.mojang.blaze3d.vertex.PoseStack;
13+
import net.minecraft.core.BlockPos;
14+
import net.minecraft.core.Direction;
15+
import net.minecraft.world.item.ItemStack;
16+
import net.minecraft.world.level.block.state.BlockState;
17+
import net.minecraft.world.phys.AABB;
18+
import net.minecraft.world.phys.BlockHitResult;
19+
import net.minecraft.world.phys.HitResult;
20+
import net.minecraft.world.phys.Vec3;
21+
import net.wurstclient.Category;
22+
import net.wurstclient.WurstClient;
23+
import net.wurstclient.events.RenderListener;
24+
import net.wurstclient.events.UpdateListener;
25+
import net.wurstclient.hack.Hack;
26+
import net.wurstclient.mixinterface.IMinecraftClient;
27+
import net.wurstclient.settings.ColorSetting;
28+
import net.wurstclient.settings.TextFieldSetting;
29+
import net.wurstclient.util.BlockUtils;
30+
import net.wurstclient.util.ChatUtils;
31+
import net.wurstclient.util.RenderUtils;
32+
import net.wurstclient.util.Rotation;
33+
import net.wurstclient.util.RotationUtils;
34+
35+
public final class TargetPlaceHack extends Hack
36+
implements RenderListener, UpdateListener
37+
{
38+
private static final WurstClient WURST = WurstClient.INSTANCE;
39+
40+
private static final IMinecraftClient IMC = WurstClient.IMC;
41+
42+
43+
private final ColorSetting highlightColor =
44+
new ColorSetting("Highlight color", Color.CYAN);
45+
46+
private final TextFieldSetting activationKey =
47+
new TextFieldSetting("Activation key",
48+
"Determines which key activates TargetPlace.\n\n"
49+
+ "Use translation keys such as key.keyboard.p.",
50+
"key.keyboard.p", this::isValidActivationKey);
51+
52+
private BlockPos targetBlock;
53+
private boolean activationKeyDown;
54+
private long lastActivationTime;
55+
56+
public TargetPlaceHack()
57+
{
58+
super("TargetPlace");
59+
setCategory(Category.BLOCKS);
60+
addSetting(highlightColor);
61+
addSetting(activationKey);
62+
}
63+
64+
@Override
65+
protected void onEnable()
66+
{
67+
EVENTS.add(UpdateListener.class, this);
68+
EVENTS.add(RenderListener.class, this);
69+
}
70+
71+
@Override
72+
protected void onDisable()
73+
{
74+
EVENTS.remove(UpdateListener.class, this);
75+
EVENTS.remove(RenderListener.class, this);
76+
targetBlock = null;
77+
activationKeyDown = false;
78+
}
79+
80+
@Override
81+
public void onUpdate()
82+
{
83+
if(MC.screen != null)
84+
{
85+
activationKeyDown = false;
86+
return;
87+
}
88+
89+
InputConstants.Key key = getActivationKey();
90+
if(key != null)
91+
{
92+
boolean currentlyDown =
93+
InputConstants.isKeyDown(MC.getWindow(), key.getValue());
94+
if(currentlyDown && !activationKeyDown)
95+
handleActivation(MC.options.keyShift.isDown());
96+
activationKeyDown = currentlyDown;
97+
}
98+
99+
// extra fast placement
100+
if(targetBlock == null || MC.level == null || MC.player == null)
101+
return;
102+
103+
ItemStack stack = MC.player.getMainHandItem();
104+
if(stack.isEmpty())
105+
return;
106+
107+
// Original behavior: once client sees air, attempt a placement.
108+
if(BlockUtils.getState(targetBlock).isAir())
109+
attemptPlace(targetBlock);
110+
111+
// Extra speed: while armed, also spam a few placements every tick so
112+
// one of them lands right after the server breaks the block.
113+
for(int i = 0; i < 3; i++)
114+
attemptPlace(targetBlock);
115+
}
116+
117+
@Override
118+
public void onRender(PoseStack matrices, float partialTicks)
119+
{
120+
if(targetBlock == null || MC.level == null)
121+
return;
122+
123+
BlockState state = MC.level.getBlockState(targetBlock);
124+
if(state.isAir())
125+
return;
126+
127+
AABB box;
128+
try
129+
{
130+
box = BlockUtils.getBoundingBox(targetBlock);
131+
}catch(UnsupportedOperationException e)
132+
{
133+
return;
134+
}
135+
if(box == null)
136+
return;
137+
138+
int color = highlightColor.getColorI(160);
139+
RenderUtils.drawOutlinedBox(matrices, box, color, false);
140+
}
141+
142+
public boolean hasValidTarget()
143+
{
144+
return isTargetValid(targetBlock);
145+
}
146+
147+
private boolean isTargetValid(BlockPos blockPos)
148+
{
149+
if(blockPos == null || MC.level == null)
150+
return false;
151+
152+
BlockState state = MC.level.getBlockState(blockPos);
153+
if(state.isAir())
154+
return false;
155+
156+
return !BlockUtils.isUnbreakable(blockPos);
157+
}
158+
159+
public boolean handleActivation()
160+
{
161+
return handleActivation(false);
162+
}
163+
164+
public boolean handleActivation(boolean shiftDown)
165+
{
166+
if(!isEnabled())
167+
return false;
168+
169+
BlockHitResult hitResult = null;
170+
if(MC.hitResult instanceof BlockHitResult blockHit)
171+
hitResult = blockHit;
172+
173+
return handleActivation(hitResult, shiftDown);
174+
}
175+
176+
private boolean handleActivation(BlockHitResult hitResult,
177+
boolean shiftDown)
178+
{
179+
BlockPos lookedPos = null;
180+
if(hitResult != null && hitResult.getType() == HitResult.Type.BLOCK)
181+
lookedPos = hitResult.getBlockPos();
182+
183+
if(lookedPos != null && lookedPos.equals(targetBlock))
184+
{
185+
if(shiftDown)
186+
{
187+
targetBlock = null;
188+
ChatUtils.message("TargetPlace: selection cleared.");
189+
return finishActivation(true);
190+
}
191+
return finishActivation(false);
192+
}
193+
194+
if(lookedPos != null && isTargetValid(lookedPos))
195+
{
196+
faceUnderside(lookedPos);
197+
targetBlock = lookedPos;
198+
ChatUtils.message(
199+
"TargetPlace: selected " + BlockUtils.getName(lookedPos)
200+
+ " at " + lookedPos.toShortString() + ".");
201+
return finishActivation(true);
202+
}
203+
204+
return finishActivation(false);
205+
}
206+
207+
private boolean finishActivation(boolean handled)
208+
{
209+
if(handled)
210+
lastActivationTime = System.currentTimeMillis();
211+
return handled;
212+
}
213+
214+
private void attemptPlace(BlockPos target)
215+
{
216+
faceUnderside(target);
217+
Placement placement = findPlacement(target);
218+
if(placement == null)
219+
return;
220+
221+
ItemStack stack = MC.player.getMainHandItem();
222+
if(stack.isEmpty())
223+
return;
224+
225+
// Look almost straight up before placing so the piston faces DOWN. //
226+
if(MC.player != null)
227+
{
228+
float yaw = MC.player.getYRot();
229+
Rotation rot = new Rotation(yaw, -89F);
230+
rot.sendPlayerLookPacket();
231+
}
232+
233+
WURST.getRotationFaker().faceVectorPacket(placement.hitVec());
234+
IMC.getInteractionManager().rightClickBlock(placement.neighbor(),
235+
placement.side(), placement.hitVec());
236+
}
237+
238+
private void faceUnderside(BlockPos target)
239+
{
240+
if(MC.player == null || MC.level == null)
241+
return;
242+
243+
BlockPos above = target.relative(Direction.UP);
244+
Vec3 hitVec = Vec3.atLowerCornerOf(above).add(0.5, -0.5, 0.5);
245+
WURST.getRotationFaker().faceVectorPacket(hitVec);
246+
Rotation needed = RotationUtils.getNeededRotations(hitVec);
247+
needed.sendPlayerLookPacket();
248+
}
249+
250+
private Placement findPlacement(BlockPos target)
251+
{
252+
BlockPos above = target.relative(Direction.UP);
253+
if(MC.level == null)
254+
return null;
255+
256+
BlockState state = MC.level.getBlockState(above);
257+
if(state.isAir())
258+
return null;
259+
260+
Vec3 center = Vec3.atLowerCornerOf(above).add(0.5, 0.5, 0.5);
261+
Vec3[] offsets =
262+
new Vec3[]{new Vec3(0.2, -0.5, 0.2), new Vec3(0.2, -0.5, -0.2),
263+
new Vec3(-0.2, -0.5, 0.2), new Vec3(-0.2, -0.5, -0.2),
264+
new Vec3(0.0, -0.5, 0.2), new Vec3(0.2, -0.5, 0.0),
265+
new Vec3(0.0, -0.5, -0.2), new Vec3(-0.2, -0.5, 0.0),
266+
new Vec3(-0.1, -0.5, 0.1), new Vec3(0.1, -0.5, -0.1)};
267+
268+
for(Vec3 offset : offsets)
269+
{
270+
Vec3 hitVec = center.add(offset);
271+
if(needsPlacementAdjust(above, hitVec))
272+
return new Placement(above, Direction.DOWN, hitVec);
273+
}
274+
275+
return new Placement(above, Direction.DOWN, center.add(0, -0.5, 0));
276+
}
277+
278+
private boolean needsPlacementAdjust(BlockPos above, Vec3 hitVec)
279+
{
280+
// ensure we are close enough to the block edge
281+
Vec3 center = Vec3.atLowerCornerOf(above).add(0.5, 0.5, 0.5);
282+
double dx = Math.abs(hitVec.x - center.x);
283+
double dz = Math.abs(hitVec.z - center.z);
284+
return dx >= 0.2 || dz >= 0.2;
285+
}
286+
287+
private InputConstants.Key getActivationKey()
288+
{
289+
try
290+
{
291+
return InputConstants.getKey(activationKey.getValue());
292+
293+
}catch(IllegalArgumentException e)
294+
{
295+
return null;
296+
}
297+
}
298+
299+
private boolean isValidActivationKey(String translationKey)
300+
{
301+
try
302+
{
303+
return InputConstants.getKey(translationKey) != null;
304+
305+
}catch(IllegalArgumentException e)
306+
{
307+
return false;
308+
}
309+
}
310+
311+
public boolean isActivationKeyDown()
312+
{
313+
return activationKeyDown;
314+
}
315+
316+
public long getLastActivationTime()
317+
{
318+
return lastActivationTime;
319+
}
320+
321+
private record Placement(BlockPos neighbor, Direction side, Vec3 hitVec)
322+
{}
323+
}

0 commit comments

Comments
 (0)