diff --git a/fabric-gametest-api-v1/build.gradle b/fabric-gametest-api-v1/build.gradle
index 25cdb5b0d..ea10eb466 100644
--- a/fabric-gametest-api-v1/build.gradle
+++ b/fabric-gametest-api-v1/build.gradle
@@ -1,6 +1,10 @@
 archivesBaseName = "fabric-gametest-api-v1"
 version = getSubprojectVersion(project)
 
+loom {
+	accessWidenerPath = file("src/main/resources/fabric-gametest-api-v1.accesswidener")
+}
+
 moduleDependencies(project, [
 		'fabric-api-base',
 		'fabric-resource-loader-v0'
diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestHelper.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestHelper.java
index cd7b466bb..8aeb1b02c 100644
--- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestHelper.java
+++ b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/impl/gametest/FabricGameTestHelper.java
@@ -28,6 +28,7 @@ import javax.xml.parsers.ParserConfigurationException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import net.minecraft.resource.ResourceFinder;
 import net.minecraft.resource.ResourcePackManager;
 import net.minecraft.server.MinecraftServer;
 import net.minecraft.server.command.TestCommand;
@@ -59,6 +60,10 @@ public final class FabricGameTestHelper {
 
 	private static final Logger LOGGER = LoggerFactory.getLogger(FabricGameTestHelper.class);
 
+	private static final String GAMETEST_STRUCTURE_PATH = "gametest/structures";
+
+	public static final ResourceFinder GAMETEST_STRUCTURE_FINDER = new ResourceFinder(GAMETEST_STRUCTURE_PATH, ".snbt");
+
 	private FabricGameTestHelper() {
 	}
 
diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/StructureTemplateManagerMixin.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/StructureTemplateManagerMixin.java
new file mode 100644
index 000000000..0fef61d94
--- /dev/null
+++ b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/StructureTemplateManagerMixin.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.gametest;
+
+import java.io.IOException;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import com.google.common.collect.ImmutableList;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import com.mojang.datafixers.DataFixer;
+import org.apache.commons.io.IOUtils;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+
+import net.minecraft.block.Block;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.nbt.NbtHelper;
+import net.minecraft.registry.RegistryEntryLookup;
+import net.minecraft.resource.Resource;
+import net.minecraft.resource.ResourceFinder;
+import net.minecraft.resource.ResourceManager;
+import net.minecraft.structure.StructureTemplate;
+import net.minecraft.structure.StructureTemplateManager;
+import net.minecraft.util.Identifier;
+import net.minecraft.world.level.storage.LevelStorage;
+
+import net.fabricmc.fabric.impl.gametest.FabricGameTestHelper;
+
+@Mixin(StructureTemplateManager.class)
+public abstract class StructureTemplateManagerMixin {
+	@Shadow
+	private ResourceManager resourceManager;
+
+	@Shadow
+	public abstract StructureTemplate createTemplate(NbtCompound nbt);
+
+	private Optional<StructureTemplate> fabric_loadSnbtFromResource(Identifier id) {
+		Identifier path = FabricGameTestHelper.GAMETEST_STRUCTURE_FINDER.toResourcePath(id);
+		Optional<Resource> resource = this.resourceManager.getResource(path);
+
+		if (resource.isPresent()) {
+			try {
+				String snbt = IOUtils.toString(resource.get().getReader());
+				NbtCompound nbt = NbtHelper.fromNbtProviderString(snbt);
+				return Optional.of(this.createTemplate(nbt));
+			} catch (IOException | CommandSyntaxException e) {
+				throw new RuntimeException("Failed to load GameTest structure " + id, e);
+			}
+		}
+
+		return Optional.empty();
+	}
+
+	private Stream<Identifier> fabric_streamTemplatesFromResource() {
+		ResourceFinder finder = FabricGameTestHelper.GAMETEST_STRUCTURE_FINDER;
+		return finder.findResources(this.resourceManager).keySet().stream().map(finder::toResourceId);
+	}
+
+	@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/ImmutableList$Builder;add(Ljava/lang/Object;)Lcom/google/common/collect/ImmutableList$Builder;", ordinal = 2, shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD)
+	private void addFabricTemplateProvider(ResourceManager resourceManager, LevelStorage.Session session, DataFixer dataFixer, RegistryEntryLookup<Block> blockLookup, CallbackInfo ci, ImmutableList.Builder<StructureTemplateManager.Provider> builder) {
+		builder.add(new StructureTemplateManager.Provider(this::fabric_loadSnbtFromResource, this::fabric_streamTemplatesFromResource));
+	}
+}
diff --git a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/StructureTestUtilMixin.java b/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/StructureTestUtilMixin.java
deleted file mode 100644
index 26c23b9ba..000000000
--- a/fabric-gametest-api-v1/src/main/java/net/fabricmc/fabric/mixin/gametest/StructureTestUtilMixin.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 2018, 2019 FabricMC
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package net.fabricmc.fabric.mixin.gametest;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
-
-import com.mojang.brigadier.exceptions.CommandSyntaxException;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-
-import net.minecraft.nbt.NbtCompound;
-import net.minecraft.nbt.NbtHelper;
-import net.minecraft.resource.Resource;
-import net.minecraft.server.world.ServerWorld;
-import net.minecraft.structure.StructureTemplate;
-import net.minecraft.test.StructureTestUtil;
-import net.minecraft.util.Identifier;
-
-@Mixin(StructureTestUtil.class)
-public abstract class StructureTestUtilMixin {
-	private static final String GAMETEST_STRUCTURE_PATH = "gametest/structures/";
-
-	// Replace the default test structure loading with something that works a bit better for mods.
-	@Inject(at = @At("HEAD"), method = "createStructureTemplate(Ljava/lang/String;Lnet/minecraft/server/world/ServerWorld;)Lnet/minecraft/structure/StructureTemplate;", cancellable = true)
-	private static void createStructure(String id, ServerWorld world, CallbackInfoReturnable<StructureTemplate> cir) {
-		Identifier baseId = new Identifier(id);
-		Identifier structureId = new Identifier(baseId.getNamespace(), GAMETEST_STRUCTURE_PATH + baseId.getPath() + ".snbt");
-
-		try {
-			Resource resource = world.getServer().getResourceManager().getResource(structureId).orElse(null);
-
-			if (resource == null) {
-				throw new RuntimeException("Unable to get resource: " + structureId);
-			}
-
-			String snbt;
-
-			try (InputStream inputStream = resource.getInputStream()) {
-				snbt = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
-			}
-
-			NbtCompound nbtCompound = NbtHelper.fromNbtProviderString(snbt);
-			StructureTemplate structure = world.getStructureTemplateManager().createTemplate(nbtCompound);
-
-			cir.setReturnValue(structure);
-		} catch (IOException | CommandSyntaxException e) {
-			throw new RuntimeException("Error while trying to load structure: " + structureId, e);
-		}
-	}
-}
diff --git a/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.accesswidener b/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.accesswidener
new file mode 100644
index 000000000..eb7f15b89
--- /dev/null
+++ b/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.accesswidener
@@ -0,0 +1,4 @@
+accessWidener v2 named
+
+accessible class net/minecraft/structure/StructureTemplateManager$Provider
+accessible method net/minecraft/structure/StructureTemplateManager$Provider <init> (Ljava/util/function/Function;Ljava/util/function/Supplier;)V
diff --git a/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.mixins.json b/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.mixins.json
index 8a7826430..9878ffc7d 100644
--- a/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.mixins.json
+++ b/fabric-gametest-api-v1/src/main/resources/fabric-gametest-api-v1.mixins.json
@@ -6,7 +6,7 @@
     "ArgumentTypesMixin",
     "CommandManagerMixin",
     "MinecraftServerMixin",
-    "StructureTestUtilMixin",
+    "StructureTemplateManagerMixin",
     "TestFunctionsMixin",
     "TestServerMixin"
   ],
diff --git a/fabric-gametest-api-v1/src/main/resources/fabric.mod.json b/fabric-gametest-api-v1/src/main/resources/fabric.mod.json
index 1a5271634..23e47fa51 100644
--- a/fabric-gametest-api-v1/src/main/resources/fabric.mod.json
+++ b/fabric-gametest-api-v1/src/main/resources/fabric.mod.json
@@ -27,6 +27,7 @@
   "mixins": [
     "fabric-gametest-api-v1.mixins.json"
   ],
+  "accessWidener": "fabric-gametest-api-v1.accesswidener",
   "custom": {
     "fabric-api:module-lifecycle": "stable"
   }