1212import meteordevelopment .meteorclient .utils .misc .Pool ;
1313import meteordevelopment .meteorclient .utils .player .FindItemResult ;
1414import meteordevelopment .meteorclient .utils .player .InvUtils ;
15+ import meteordevelopment .meteorclient .utils .player .PlayerUtils ;
1516import meteordevelopment .meteorclient .utils .world .BlockIterator ;
1617import meteordevelopment .meteorclient .utils .world .BlockUtils ;
1718import meteordevelopment .orbit .EventHandler ;
1819import net .minecraft .block .*;
1920import net .minecraft .util .math .BlockPos ;
21+ import net .minecraft .util .math .Vec3d ;
22+ import net .minecraft .util .hit .BlockHitResult ;
23+ import net .minecraft .world .RaycastContext ;
2024
2125import java .util .ArrayList ;
2226import java .util .List ;
27+ import java .util .Comparator ;
2328
2429public class SpawnProofer extends Module {
2530 private final SettingGroup sgGeneral = settings .getDefaultGroup ();
2631
27- private final Setting <Integer > range = sgGeneral .add (new IntSetting .Builder ()
28- .name ("range" )
29- .description ("Range for block placement and rendering" )
30- .defaultValue (3 )
32+ private final Setting <Integer > placeDelay = sgGeneral .add (new IntSetting .Builder ()
33+ .name ("place-delay" )
34+ .description ("The tick delay between placing blocks." )
35+ .defaultValue (1 )
36+ .range (0 , 10 )
37+ .build ()
38+ );
39+
40+ private final Setting <Double > placeRange = sgGeneral .add (new DoubleSetting .Builder ()
41+ .name ("place-range" )
42+ .description ("How far away from the player you can place a block." )
43+ .defaultValue (4.5 )
3144 .min (0 )
45+ .sliderMax (6 )
3246 .build ()
3347 );
3448
35- private final Setting <List <Block >> blocks = sgGeneral .add (new BlockListSetting .Builder ()
36- .name ("blocks" )
37- .description ("Block to use for spawn proofing" )
38- .defaultValue (Blocks .TORCH , Blocks .STONE_BUTTON , Blocks .STONE_SLAB )
39- .filter (this ::filterBlocks )
49+ private final Setting <Double > wallsRange = sgGeneral .add (new DoubleSetting .Builder ()
50+ .name ("walls-range" )
51+ .description ("How far away from the player you can place a block behind walls." )
52+ .defaultValue (4.5 )
53+ .min (0 )
54+ .sliderMax (6 )
4055 .build ()
4156 );
4257
43- private final Setting <Integer > delay = sgGeneral .add (new IntSetting .Builder ()
44- .name ("delay" )
45- .description ("Delay in ticks between placing blocks" )
58+ private final Setting <Integer > blocksPerTick = sgGeneral .add (new IntSetting .Builder ()
59+ .name ("blocks-per-tick" )
60+ .description ("How many blocks to place in one tick." )
61+ .defaultValue (1 )
62+ .min (1 )
63+ .build ()
64+ );
65+
66+ private final Setting <Integer > lightLevel = sgGeneral .add (new IntSetting .Builder ()
67+ .name ("light-level" )
68+ .description ("Light levels to spawn proof. Old spawning light: 7." )
4669 .defaultValue (0 )
4770 .min (0 )
71+ .sliderMax (15 )
4872 .build ()
4973 );
5074
51- private final Setting <Boolean > rotate = sgGeneral .add (new BoolSetting .Builder ()
52- .name ("rotate" )
53- .description ("Rotates towards the blocks being placed." )
54- .defaultValue (true )
75+ private final Setting <List <Block >> blocks = sgGeneral .add (new BlockListSetting .Builder ()
76+ .name ("blocks" )
77+ .description ("Block to use for spawn proofing." )
78+ .defaultValue (Blocks .TORCH , Blocks .STONE_BUTTON , Blocks .STONE_SLAB )
79+ .filter (this ::filterBlocks )
5580 .build ()
5681 );
5782
@@ -62,33 +87,29 @@ public class SpawnProofer extends Module {
6287 .build ()
6388 );
6489
65- private final Setting <Boolean > newMobSpawnLightLevel = sgGeneral .add (new BoolSetting .Builder ()
66- .name ("new-mob-spawn-light-level " )
67- .description ("Use the new (1.18+) mob spawn behavior " )
90+ private final Setting <Boolean > rotate = sgGeneral .add (new BoolSetting .Builder ()
91+ .name ("rotate " )
92+ .description ("Rotates towards the blocks being placed. " )
6893 .defaultValue (true )
6994 .build ()
7095 );
7196
72-
7397 private final Pool <BlockPos .Mutable > spawnPool = new Pool <>(BlockPos .Mutable ::new );
7498 private final List <BlockPos .Mutable > spawns = new ArrayList <>();
75- private int ticksWaited ;
99+ private int timer ;
76100
77101 public SpawnProofer () {
78102 super (Categories .World , "spawn-proofer" , "Automatically spawnproofs unlit areas." );
79103 }
80104
81105 @ EventHandler
82106 private void onTickPre (TickEvent .Pre event ) {
83- // Delay
84- if (delay .get () != 0 && ticksWaited < delay .get () - 1 ) {
85- return ;
86- }
107+ if (timer < placeDelay .get ()) return ;
87108
88109 // Find slot
89110 boolean foundBlock = InvUtils .testInHotbar (itemStack -> blocks .get ().contains (Block .getBlockFromItem (itemStack .getItem ())));
90111 if (!foundBlock ) {
91- error ("Found none of the chosen blocks in hotbar" );
112+ error ("Found none of the chosen blocks in hotbar. " );
92113 toggle ();
93114 return ;
94115 }
@@ -97,13 +118,17 @@ private void onTickPre(TickEvent.Pre event) {
97118 for (BlockPos .Mutable blockPos : spawns ) spawnPool .free (blockPos );
98119 spawns .clear ();
99120
100- int lightLevel = newMobSpawnLightLevel .get () ? 0 : 7 ;
101- BlockIterator .register (range .get (), range .get (), (blockPos , blockState ) -> {
102- BlockUtils .MobSpawn spawn = BlockUtils .isValidMobSpawn (blockPos , blockState , lightLevel );
121+ BlockIterator .register ((int ) Math .ceil (placeRange .get ()), (int ) Math .ceil (placeRange .get ()), (blockPos , blockState ) -> {
122+ BlockUtils .MobSpawn spawn = BlockUtils .isValidMobSpawn (blockPos , blockState , lightLevel .get ());
103123
104124 if ((spawn == BlockUtils .MobSpawn .Always && (mode .get () == Mode .Always || mode .get () == Mode .Both )) ||
105125 spawn == BlockUtils .MobSpawn .Potential && (mode .get () == Mode .Potential || mode .get () == Mode .Both )) {
106126
127+ if (!BlockUtils .canPlace (blockPos )) return ;
128+
129+ // Check range and raycast
130+ if (isOutOfRange (blockPos )) return ;
131+
107132 spawns .add (spawnPool .get ().set (blockPos ));
108133 }
109134 });
@@ -112,49 +137,48 @@ private void onTickPre(TickEvent.Pre event) {
112137 @ EventHandler
113138 private void onTickPost (TickEvent .Post event ) {
114139 // Delay
115- if (delay .get () != 0 && ticksWaited < delay .get () - 1 ) {
116- ticksWaited ++;
117- return ;
118- }
140+ if (timer ++ < placeDelay .get ()) return ;
119141
120142 if (spawns .isEmpty ()) return ;
121143
122144 // Find slot
123145 FindItemResult block = InvUtils .findInHotbar (itemStack -> blocks .get ().contains (Block .getBlockFromItem (itemStack .getItem ())));
124146 if (!block .found ()) {
125- error ("Found none of the chosen blocks in hotbar" );
147+ error ("Found none of the chosen blocks in hotbar. " );
126148 toggle ();
127149 return ;
128150 }
129151
130- // Place blocks
131- if (delay .get () == 0 ) {
132- for (BlockPos blockPos : spawns ) BlockUtils .place (blockPos , block , rotate .get (), -50 , false );
152+ int placedCount = 0 ;
153+
154+ // Sort blocks to use the lowest light level spawns first
155+ if (isLightSource (Block .getBlockFromItem (mc .player .getInventory ().getStack (block .slot ()).getItem ()))) {
156+ spawns .sort (Comparator .comparingInt (blockPos -> mc .world .getLightLevel (blockPos )));
157+ placedCount = blocksPerTick .get () - 1 ; // Force only one light source per tick to stop unnecessary placements
133158 }
134- else {
135- // Check if light source
136- if (isLightSource (Block .getBlockFromItem (mc .player .getInventory ().getStack (block .slot ()).getItem ()))) {
137-
138- // Find lowest light level
139- int lowestLightLevel = 16 ;
140- BlockPos .Mutable selectedBlockPos = spawns .getFirst ();
141-
142- for (BlockPos blockPos : spawns ) {
143- int lightLevel = mc .world .getLightLevel (blockPos );
144- if (lightLevel < lowestLightLevel ) {
145- lowestLightLevel = lightLevel ;
146- selectedBlockPos .set (blockPos );
147- }
148- }
149-
150- BlockUtils .place (selectedBlockPos , block , rotate .get (), -50 , false );
151- }
152- else {
153- BlockUtils .place (spawns .getFirst (), block , rotate .get (), -50 , false );
159+
160+ // Place blocks!
161+ for (BlockPos blockPos : spawns ) {
162+ if (placedCount >= blocksPerTick .get ()) continue ;
163+
164+ if (BlockUtils .place (blockPos , block , rotate .get (), -50 , false )) {
165+ placedCount ++;
154166 }
155167 }
156168
157- ticksWaited = 0 ;
169+ timer = 0 ;
170+ }
171+
172+ private boolean isOutOfRange (BlockPos blockPos ) {
173+ Vec3d pos = blockPos .toCenterPos ();
174+ if (!PlayerUtils .isWithin (pos , placeRange .get ())) return true ;
175+
176+ RaycastContext raycastContext = new RaycastContext (mc .player .getEyePos (), pos , RaycastContext .ShapeType .COLLIDER , RaycastContext .FluidHandling .NONE , mc .player );
177+ BlockHitResult result = mc .world .raycast (raycastContext );
178+ if (result == null || !result .getBlockPos ().equals (blockPos ))
179+ return !PlayerUtils .isWithin (pos , wallsRange .get ());
180+
181+ return false ;
158182 }
159183
160184 private boolean filterBlocks (Block block ) {
@@ -180,7 +204,6 @@ private boolean isLightSource(Block block) {
180204 public enum Mode {
181205 Always ,
182206 Potential ,
183- Both ,
184- None
207+ Both
185208 }
186209}
0 commit comments