1+ package mekanism .common .tests .helpers ;
2+
3+ import mekanism .api .annotations .NothingNullByDefault ;
4+ import mekanism .common .capabilities .Capabilities ;
5+ import mekanism .common .util .WorldUtils ;
6+ import net .minecraft .core .BlockPos ;
7+ import net .minecraft .core .Direction ;
8+ import net .minecraft .core .registries .BuiltInRegistries ;
9+ import net .minecraft .gametest .framework .GameTestAssertException ;
10+ import net .minecraft .gametest .framework .GameTestInfo ;
11+ import net .minecraft .server .level .ChunkHolder ;
12+ import net .minecraft .server .level .ChunkLevel ;
13+ import net .minecraft .server .level .ChunkMap ;
14+ import net .minecraft .server .level .DistanceManager ;
15+ import net .minecraft .world .InteractionHand ;
16+ import net .minecraft .world .InteractionResult ;
17+ import net .minecraft .world .entity .player .Player ;
18+ import net .minecraft .world .item .Item ;
19+ import net .minecraft .world .item .ItemStack ;
20+ import net .minecraft .world .item .context .UseOnContext ;
21+ import net .minecraft .world .level .ChunkPos ;
22+ import net .minecraft .world .level .block .entity .BaseContainerBlockEntity ;
23+ import net .minecraft .world .level .block .entity .BlockEntity ;
24+ import net .minecraft .world .level .block .state .BlockState ;
25+ import net .minecraft .world .phys .BlockHitResult ;
26+ import net .neoforged .neoforge .items .IItemHandler ;
27+ import net .neoforged .testframework .gametest .ExtendedGameTestHelper ;
28+
29+ @ NothingNullByDefault
30+ public class MekGameTestHelper extends ExtendedGameTestHelper {
31+
32+ public static final int INACCESSIBLE_LEVEL = ChunkMap .MAX_VIEW_DISTANCE + 1 ;
33+ public static final int UNLOAD_LEVEL = ChunkLevel .MAX_LEVEL + 1 ;
34+
35+ public MekGameTestHelper (GameTestInfo info ) {
36+ super (info );
37+ }
38+
39+ public ChunkMap getChunkMap () {
40+ return getLevel ().getChunkSource ().chunkMap ;
41+ }
42+
43+ public int setChunkLoadLevel (ChunkPos relativePos , int newLevel ) {
44+ long absPos = absolutePos (relativePos ).toLong ();
45+ DistanceManager distanceManager = getChunkMap ().getDistanceManager ();
46+ ChunkHolder holder = distanceManager .getChunk (absPos );
47+ int oldLevel = holder == null ? UNLOAD_LEVEL : holder .getTicketLevel ();
48+ distanceManager .updateChunkScheduling (absPos , newLevel , holder , oldLevel );
49+ //Note: We purposely don't unload or load it if that changed as this method is meant for use when we don't know if
50+ // we are loading or unloading, and instead want to simulate the weird inaccessible but not unloaded state that
51+ // vanilla can get chunks into
52+ return oldLevel ;
53+ }
54+
55+ public ChunkPos absolutePos (ChunkPos relativePos ) {
56+ BlockPos relativeMiddle = relativePos .getMiddleBlockPosition (0 );
57+ BlockPos absolutePos = absolutePos (relativeMiddle );
58+ return new ChunkPos (absolutePos );
59+ }
60+
61+ public BlockState getBlockState (int x , int y , int z ) {
62+ return getBlockState (new BlockPos (x , y , z ));
63+ }
64+
65+ public boolean isChunkLoaded (ChunkPos relativePos ) {
66+ return WorldUtils .isChunkLoaded (getLevel (), absolutePos (relativePos ));
67+ }
68+
69+ public boolean isBlockLoaded (BlockPos relativePos ) {
70+ return WorldUtils .isBlockLoaded (getLevel (), absolutePos (relativePos ));
71+ }
72+
73+ public void fail (String message , ChunkPos relativePos ) {
74+ ChunkPos absolutePos = absolutePos (relativePos );
75+ fail (message + " at " + absolutePos .x + "," + absolutePos .z + " (relative: " + relativePos .x + "," + relativePos .z + ") (t=" + getTick () + ")" );
76+ }
77+
78+ @ Override
79+ public void assertContainerContains (BlockPos relativePos , Item item ) {
80+ assertContainerContains (relativePos , item , 1 );
81+ }
82+
83+ public void assertContainerContains (int x , int y , int z , Item item , int count ) {
84+ assertContainerContains (new BlockPos (x , y , z ), item , count );
85+ }
86+
87+ /**
88+ * This is similar and based off of vanilla's assertContainerContains, except supports checking for a specific amount, and checking blocks that expose item handlers.
89+ */
90+ public void assertContainerContains (BlockPos relativePos , Item item , int count ) {
91+ //TODO: Do we want to make a PR to Neo that adds this overload, even if it is as simple as only checking the count
92+ // and doesn't also add support for checking item handlers?
93+ BlockEntity blockentity = getBlockEntity (relativePos );
94+ boolean sameCount ;
95+ if (blockentity instanceof BaseContainerBlockEntity containerBE ) {
96+ sameCount = containerBE .countItem (item ) == count ;
97+ } else {
98+ IItemHandler handler = getCapability (Capabilities .ITEM .block (), relativePos , null );
99+ if (handler == null ) {
100+ throw new GameTestAssertException ("Expected a container or item handler at " + relativePos + ", found " + BuiltInRegistries .BLOCK_ENTITY_TYPE .getKey (blockentity .getType ()));
101+ }
102+ int found = 0 ;
103+ for (int i = 0 , slots = handler .getSlots (); i < slots ; i ++) {
104+ ItemStack stack = handler .getStackInSlot (i );
105+ if (stack .is (item )) {
106+ found += stack .getCount ();
107+ }
108+ }
109+ sameCount = found == count ;
110+ }
111+ if (!sameCount ) {
112+ throw new GameTestAssertException ("Container should contain: " + count + " " + item );
113+ }
114+ }
115+
116+ /**
117+ * Adds support for validating that item handlers are empty.
118+ */
119+ @ Override
120+ public void assertContainerEmpty (BlockPos relativePos ) {
121+ BlockEntity blockentity = getBlockEntity (relativePos );
122+ if (blockentity instanceof BaseContainerBlockEntity containerBE ) {
123+ if (!containerBE .isEmpty ()) {
124+ throw new GameTestAssertException ("Container should be empty" );
125+ }
126+ } else {
127+ IItemHandler handler = getCapability (Capabilities .ITEM .block (), relativePos , null );
128+ if (handler != null ) {
129+ for (int i = 0 , slots = handler .getSlots (); i < slots ; i ++) {
130+ if (!handler .getStackInSlot (i ).isEmpty ()) {
131+ throw new GameTestAssertException ("Container should be empty" );
132+ }
133+ }
134+ }
135+ }
136+ }
137+
138+ public Player makeMockPlayerLookingAt (int x , int y , int z , Direction direction ) {
139+ return makeMockPlayerLookingAt (new BlockPos (x , y , z ), direction );
140+ }
141+
142+ public Player makeMockPlayerLookingAt (BlockPos relativePos , Direction direction ) {
143+ Player player = makeMockPlayer ();
144+ player .setXRot (direction == Direction .DOWN ? 90 : direction == Direction .UP ? -90 : 0 );
145+ float yRot = direction .toYRot ();
146+ player .setYRot (yRot );
147+ player .setYHeadRot (yRot );
148+ player .setPos (absolutePos (relativePos ).getCenter ().subtract (
149+ 0.45 * direction .getStepX (),
150+ 0.45 * direction .getStepY () + player .getEyeHeight (),
151+ 0.45 * direction .getStepZ ()
152+ ));
153+ return player ;
154+ }
155+
156+ /**
157+ * Adds support for providing a more accurate/useful Vec3 location in the hit result.
158+ */
159+ @ Override
160+ public void useOn (BlockPos relativePos , ItemStack item , Player player , Direction direction ) {
161+ useOn (relativePos , item , player , direction , 1 );
162+ }
163+
164+ public void useOn (BlockPos relativePos , ItemStack item , Player player , Direction direction , int times ) {
165+ player .setItemInHand (InteractionHand .MAIN_HAND , item );
166+ BlockHitResult hit = createHitResult (relativePos , direction , false );
167+ UseOnContext context = new UseOnContext (getLevel (), player , InteractionHand .MAIN_HAND , item , hit );
168+ for (int i = 0 ; i < times ; i ++) {
169+ item .useOn (context );
170+ }
171+ }
172+
173+ /**
174+ * Adds support for providing a more accurate/useful Vec3 location in the hit result.
175+ */
176+ @ Override
177+ public void useBlock (BlockPos relativePos , Player player , ItemStack item , Direction direction ) {
178+ player .setItemInHand (InteractionHand .MAIN_HAND , item );
179+ BlockHitResult hit = createHitResult (relativePos , direction , true );
180+ InteractionResult result = getBlockState (relativePos ).use (getLevel (), player , InteractionHand .MAIN_HAND , hit );
181+ if (!result .consumesAction ()) {
182+ item .useOn (new UseOnContext (getLevel (), player , InteractionHand .MAIN_HAND , item , hit ));
183+ }
184+ }
185+
186+ //TODO: Do we want to PR the more accurate hit result location stuff to Neo?
187+ private BlockHitResult createHitResult (BlockPos relativePos , Direction direction , boolean inside ) {
188+ BlockPos absolutePos = absolutePos (relativePos );
189+ return new BlockHitResult (absolutePos .getCenter ().relative (direction , 0.5 ), direction , absolutePos , inside );
190+ }
191+ }
0 commit comments