1
+ package net .caffeinemc .mods .lithium .neoforge .test ;
2
+
3
+
4
+ import net .minecraft .core .BlockPos ;
5
+ import net .minecraft .gametest .framework .*;
6
+ import net .minecraft .world .level .block .Blocks ;
7
+ import net .minecraft .world .level .block .NoteBlock ;
8
+ import net .minecraft .world .level .block .state .BlockState ;
9
+ import net .neoforged .neoforge .gametest .GameTestHolder ;
10
+ import net .neoforged .neoforge .gametest .PrefixGameTestTemplate ;
11
+ import org .jetbrains .annotations .Nullable ;
12
+
13
+ import java .io .IOException ;
14
+ import java .nio .file .Files ;
15
+ import java .nio .file .Path ;
16
+ import java .nio .file .Paths ;
17
+ import java .util .ArrayList ;
18
+ import java .util .Collection ;
19
+ import java .util .List ;
20
+ import java .util .Optional ;
21
+ import java .util .stream .Stream ;
22
+
23
+ @ PrefixGameTestTemplate (value = false )
24
+ @ GameTestHolder ("lithium-gametest" )
25
+ public class LithiumNeoforgeGameTest {
26
+
27
+ //Not very nice to do this but it works
28
+ public static final String LITHIUM_GAMETEST_SNBT_PATH = "../../../common/src/gametest/resources/data/lithium-gametest/gametest/structure" ;
29
+
30
+ //Some tests are excluded because Neoforge is breaking them, not lithium.
31
+ //test_redstone.lava_push_speed broken by https://github.com/neoforged/NeoForge/issues/1575
32
+ //test_redstone.comparator_update_collection broken by https://github.com/neoforged/NeoForge/issues/1750
33
+ public static final Collection <String > NEOFORGE_EXLUDED_TESTS = List .of ("test_redstone.lava_push_speed" , "test_redstone.comparator_update_collection" );
34
+
35
+ static {
36
+ StructureUtils .testStructuresDir = LITHIUM_GAMETEST_SNBT_PATH ;
37
+ }
38
+
39
+ @ Nullable
40
+ public static String getTemplateName (String structureName ) {
41
+ int dotIndex = structureName .indexOf ("." );
42
+ if (dotIndex < 0 ) {
43
+ return null ;
44
+ }
45
+ return structureName .substring (0 , dotIndex );
46
+ }
47
+
48
+ private static boolean shouldReplaceWithRedstoneBlock (BlockState blockState ) {
49
+ return blockState .is (Blocks .RED_TERRACOTTA );
50
+ }
51
+
52
+ private static boolean isSuccessBlock (BlockState blockState ) {
53
+ return blockState .is (Blocks .EMERALD_BLOCK ) || blockState .is (Blocks .GREEN_WOOL ) || blockState .is (Blocks .LIME_WOOL );
54
+ }
55
+
56
+ private static boolean isFailureBlock (BlockState blockState ) {
57
+ return blockState .is (Blocks .RED_WOOL );
58
+ }
59
+
60
+ /**
61
+ * A test function that can be used to create tests with a simple redstone interface.
62
+ * This allows omitting the test function code by starting the structure filename with "test_redstone."
63
+ */
64
+ public static void test_redstone (GameTestHelper gameTestHelper ) {
65
+ ArrayList <BlockPos > successBlocks = new ArrayList <>();
66
+ ArrayList <BlockPos > failureBlocks = new ArrayList <>();
67
+
68
+ //Replace all red terracotta with redstone block at the start and fill the condition block lists
69
+ gameTestHelper .forEveryBlockInStructure (blockPos -> {
70
+ BlockState blockState = gameTestHelper .getBlockState (blockPos );
71
+ if (shouldReplaceWithRedstoneBlock (blockState )) {
72
+ gameTestHelper .setBlock (blockPos , Blocks .REDSTONE_BLOCK .defaultBlockState ());
73
+ }
74
+ if (isSuccessBlock (blockState )) successBlocks .add (blockPos .immutable ());
75
+ if (isFailureBlock (blockState )) failureBlocks .add (blockPos .immutable ());
76
+ });
77
+ if (successBlocks .isEmpty ()) {
78
+ throw new GameTestAssertException ("Expected success condition blocks anywhere inside the test. test_redstone requires green wool, lime wool or emerald blocks for the success condition" );
79
+ }
80
+
81
+ //Fail when any powered note block is on top of a failure condition block.
82
+ //Succeed when any powered note block is on top of a success condition block. Assume the success/failure condition blocks don't move during the test.
83
+ gameTestHelper .onEachTick (
84
+ () -> {
85
+ //Always check the failure condition blocks before the success conditions. Failed tests throw exceptions.
86
+ Optional <BlockPos > failurePosition = checkFailureBlocks (gameTestHelper , failureBlocks );
87
+ if (failurePosition .isPresent ()) {
88
+ throw new GameTestAssertPosException ("Failure condition block activated!" , gameTestHelper .absolutePos (failurePosition .get ()), failurePosition .get (), gameTestHelper .getTick ());
89
+ }
90
+
91
+ if (checkSuccessBlocks (gameTestHelper , successBlocks )) {
92
+ gameTestHelper .succeed ();
93
+ }
94
+ }
95
+ );
96
+ }
97
+
98
+ private static boolean checkSuccessBlocks (GameTestHelper gameTestHelper , ArrayList <BlockPos > successBlocks ) {
99
+ return successBlocks .stream ().anyMatch (blockPos -> {
100
+ BlockState blockState = gameTestHelper .getBlockState (blockPos .above ());
101
+ return blockState .is (Blocks .NOTE_BLOCK ) && blockState .getValue (NoteBlock .POWERED ) &&
102
+ isSuccessBlock (gameTestHelper .getBlockState (blockPos ));
103
+ });
104
+ }
105
+
106
+ private static Optional <BlockPos > checkFailureBlocks (GameTestHelper gameTestHelper , ArrayList <BlockPos > failureBlocks ) {
107
+ return failureBlocks .stream ().filter (blockPos -> {
108
+ BlockState blockState = gameTestHelper .getBlockState (blockPos .above ());
109
+ return blockState .is (Blocks .NOTE_BLOCK ) && blockState .getValue (NoteBlock .POWERED ) &&
110
+ isFailureBlock (gameTestHelper .getBlockState (blockPos ));
111
+ }).findFirst ();
112
+ }
113
+
114
+ @ GameTestGenerator
115
+ public Collection <TestFunction > getAllRedstoneTests () {
116
+ List <String > structureNames = null ;
117
+ try {
118
+ structureNames = getLithiumSNBTFilenames ();
119
+ } catch (IOException e ) {
120
+ throw new RuntimeException (e );
121
+ }
122
+ ArrayList <TestFunction > testFunctions = new ArrayList <>();
123
+ for (String structureName : structureNames ) {
124
+ String templateName = getTemplateName (structureName );
125
+
126
+ if ("test_redstone" .equals (templateName )) {
127
+ testFunctions .add (new TestFunction (
128
+ "lithium_test_redstone" ,
129
+ structureName .substring (templateName .length () + 1 ),
130
+ "lithium-gametest:" + structureName , //This structure file location method is Fabric dependent?
131
+ 400 /* Timeout ticks. 20 Seconds is fine for our redstone tests. */ ,
132
+ 10 /* Setup ticks (between placing structure and test start). Prevents random redstone firing from activating success / failure condition immediately. */ ,
133
+ !NEOFORGE_EXLUDED_TESTS .contains (structureName ) /* Test required */ ,
134
+ LithiumNeoforgeGameTest ::test_redstone ));
135
+ }
136
+ }
137
+
138
+ return testFunctions ;
139
+ }
140
+
141
+ private List <String > getLithiumSNBTFilenames () throws IOException {
142
+ Path folderPath = Paths .get (LITHIUM_GAMETEST_SNBT_PATH );
143
+ try (Stream <Path > paths = Files .walk (folderPath )) {
144
+ return paths
145
+ .filter (Files ::isRegularFile )
146
+ .map (path -> path .toFile ().getName ())
147
+ .filter (string -> string .endsWith (".snbt" ))
148
+ .map (file -> file .substring (0 , file .lastIndexOf ('.' ))).toList ();
149
+ }
150
+ }
151
+ }
0 commit comments