1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-26 18:29:21 +00:00

Set up proper gametest sourceset within Fabric module

stuff (yeah this isn't a good commit message I'll get back to this later who knows)
This commit is contained in:
Eclipse
2025-08-30 10:49:48 +00:00
parent 3c8fcdff09
commit a942ac53b1
13 changed files with 389 additions and 471 deletions

View File

@@ -18,15 +18,6 @@ loom {
}
}
fabricApi {
configureTests {
createSourceSet = true
modId = "geyser-gametest"
enableClientGameTests = false
eula = true
}
}
dependencies {
modImplementation(libs.fabric.loader)
modApi(libs.fabric.api)
@@ -59,6 +50,15 @@ dependencies {
relocate("org.cloudburstmc.netty")
relocate("org.cloudburstmc.protocol")
fabricApi {
configureTests {
createSourceSet = true
modId = "geyser-gametest"
enableClientGameTests = false
eula = true
}
}
tasks {
jar {
manifest.attributes["Main-Class"] = "org.geysermc.geyser.platform.fabric.GeyserFabricMain"

View File

@@ -23,28 +23,32 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.gametest;
package org.geysermc.geyser.gametest;
import com.google.common.hash.HashCode;
import com.mojang.serialization.MapCodec;
import io.netty.buffer.Unpooled;
import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.component.TypedDataComponent;
import net.minecraft.gametest.framework.GameTestHelper;
import net.minecraft.gametest.framework.GameTestInstance;
import net.minecraft.gametest.framework.TestData;
import net.minecraft.gametest.framework.TestEnvironmentDefinition;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.HashOps;
import org.geysermc.geyser.item.hashing.DataComponentHashers;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.codec.MinecraftTypes;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponent;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
import org.jetbrains.annotations.NotNull;
public class GeyserComponentHashTestInstance<T> extends GeyserTestInstance {
import java.util.List;
public class GeyserComponentHashTestInstance<T> extends GameTestInstance {
private static final MapCodec<TypedDataComponent<?>> TYPED_COMPONENT_CODEC = DataComponentType.PERSISTENT_CODEC
.dispatchMap("component", TypedDataComponent::type, GeyserComponentHashTestInstance::typedComponentCodec);
@@ -53,12 +57,15 @@ public class GeyserComponentHashTestInstance<T> extends GeyserTestInstance {
private final TypedDataComponent<T> testValue;
protected GeyserComponentHashTestInstance(TypedDataComponent<T> testValue) {
public GeyserComponentHashTestInstance(TypedDataComponent<T> testValue) {
// TODO use default vanilla test environment
super(new TestData<>(Holder.direct(new TestEnvironmentDefinition.AllOf(List.of())),
ResourceLocation.withDefaultNamespace("empty"), 1, 1, true));
this.testValue = testValue;
}
@Override
protected void run(@NotNull GameTestHelper helper, GeyserSession session) {
public void run(@NotNull GameTestHelper helper) {
// Encode vanilla component to buffer
RegistryFriendlyByteBuf buffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), helper.getLevel().registryAccess());
TypedDataComponent.STREAM_CODEC.encode(buffer, testValue);
@@ -70,7 +77,8 @@ public class GeyserComponentHashTestInstance<T> extends GeyserTestInstance {
// Hash both and compare
RegistryOps<HashCode> ops = RegistryOps.create(HashOps.CRC32C_INSTANCE, helper.getLevel().registryAccess());
int expected = testValue.encodeValue(ops).getOrThrow().asInt();
int geyser = DataComponentHashers.hash(session, mcplComponent).asInt();
//int geyser = DataComponentHashers.hash(session, mcplComponent).asInt();
int geyser = 0;
helper.assertValueEqual(expected, geyser, Component.literal("Hash for component " + testValue));
@@ -85,6 +93,7 @@ public class GeyserComponentHashTestInstance<T> extends GeyserTestInstance {
@Override
protected @NotNull MutableComponent typeDescription() {
// TODO more descriptive?
return Component.literal("Geyser Data Component Hash Test");
}

View File

@@ -25,6 +25,12 @@
package org.geysermc.geyser.gametest;
public class GeyserGameTest {
// TODO actually add gametests
import net.fabricmc.api.ModInitializer;
public class GeyserGameTestBootstrap implements ModInitializer {
@Override
public void onInitialize() {
GeyserGameTests.bootstrap();
}
}

View File

@@ -23,17 +23,25 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.gametest;
package org.geysermc.geyser.gametest;
import net.fabricmc.api.ModInitializer;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.gametest.framework.GameTestInstance;
import net.minecraft.resources.ResourceLocation;
public class GeyserFabricGametestBootstrap implements ModInitializer {
public class GeyserGameTests {
@Override
public void onInitialize() {
Registry.register(BuiltInRegistries.TEST_INSTANCE_TYPE, ResourceLocation.fromNamespaceAndPath("geyser", "component_hash"), GeyserComponentHashTestInstance.CODEC);
private static ResourceLocation createKey(String name) {
return ResourceLocation.fromNamespaceAndPath("geyser", name);
}
private static void register(String name, MapCodec<? extends GameTestInstance> codec) {
Registry.register(BuiltInRegistries.TEST_INSTANCE_TYPE, createKey(name), codec);
}
public static void bootstrap() {
register("component_hash", GeyserComponentHashTestInstance.CODEC);
}
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright (c) 2025 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.gametest;
import net.minecraft.core.Holder;
import net.minecraft.gametest.framework.GameTestHelper;
import net.minecraft.gametest.framework.GameTestInstance;
import net.minecraft.gametest.framework.TestData;
import net.minecraft.gametest.framework.TestEnvironmentDefinition;
import net.minecraft.resources.ResourceLocation;
import org.geysermc.geyser.session.GeyserSession;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public abstract class GeyserTestInstance extends GameTestInstance {
protected GeyserTestInstance() {
// TODO use default vanilla test environment
super(new TestData<>(Holder.direct(new TestEnvironmentDefinition.AllOf(List.of())),
ResourceLocation.withDefaultNamespace("empty"), 1, 1, true));
}
@Override
public void run(@NotNull GameTestHelper helper) {
/*Map<UUID, GeyserSession> sessions = GeyserImpl.getInstance().getSessionManager().getSessions();
while (sessions.isEmpty()) {
try {
Thread.sleep(100L);
} catch (InterruptedException ignored) {}
}
GeyserSession session = sessions.values().stream().findAny().orElseThrow();*/
run(helper, null);
}
protected abstract void run(@NotNull GameTestHelper helper, GeyserSession session);
}

View File

@@ -1,85 +0,0 @@
/*
* Copyright (c) 2025 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.gametest.mixin;
import com.mojang.datafixers.DataFixer;
import net.minecraft.gametest.framework.GameTestServer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.Services;
import net.minecraft.server.WorldStem;
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
import net.minecraft.server.packs.repository.PackRepository;
import net.minecraft.world.level.storage.LevelStorageSource;
import org.geysermc.geyser.platform.mod.GeyserServerPortGetter;
import org.slf4j.Logger;
import org.spongepowered.asm.mixin.Final;
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.CallbackInfoReturnable;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Proxy;
@Mixin(GameTestServer.class)
public abstract class GameTestServerMixin extends MinecraftServer implements GeyserServerPortGetter {
@Shadow
@Final
private static Logger LOGGER;
public GameTestServerMixin(Thread thread, LevelStorageSource.LevelStorageAccess levelStorageAccess, PackRepository packRepository, WorldStem worldStem,
Proxy proxy, DataFixer dataFixer, Services services, ChunkProgressListenerFactory chunkProgressListenerFactory) {
super(thread, levelStorageAccess, packRepository, worldStem, proxy, dataFixer, services, chunkProgressListenerFactory);
}
@Override
public int geyser$getServerPort() {
return this.getPort();
}
@Inject(method = "initServer", at = @At("HEAD"), cancellable = true)
public void startTcpServer(CallbackInfoReturnable<Boolean> callbackInfoReturnable) {
// A bit of copying from dedicated server code
InetAddress address = InetAddress.getLoopbackAddress();
if (getPort() < 0) {
setPort(25565);
}
LOGGER.info("Starting gametest server on {}:{}", address, getPort());
LOGGER.info("Geyser's tests will start once a bedrock player connects!");
try {
this.getConnection().startTcpServerListener(address, getPort());
} catch (IOException exception) {
LOGGER.warn("**** FAILED TO BIND TO PORT!");
LOGGER.warn("The exception was: {}", exception.toString());
LOGGER.warn("Perhaps a server is already running on that port?");
callbackInfoReturnable.setReturnValue(false);
}
}
}

View File

@@ -1,8 +1,8 @@
{
"schemaVersion": 1,
"id": "${id}-fabric-tests",
"id": "${id}-gametest",
"version": "${version}",
"name": "${name}-Fabric-gametest",
"name": "${name}-Gametest",
"description": "A bridge/proxy allowing you to connect to Minecraft: Java Edition servers with Minecraft: Bedrock Edition. ",
"authors": [
"${author}"
@@ -15,16 +15,16 @@
"icon": "assets/geyser/icon.png",
"environment": "*",
"entrypoints": {
"fabric-gametest": [
"org.geysermc.geyser.platform.fabric.gametest.GeyserFabricGametestBootstrap"
"main": [
"org.geysermc.geyser.gametest.GeyserGameTestBootstrap"
]
},
"mixins": [
"geyser-fabric-tests.mixins.json"
"geyser-gametest.mixins.json"
],
"depends": {
"fabricloader": ">=0.16.7",
"fabricloader": ">=0.17.2",
"fabric-api": "*",
"minecraft": ">=1.21.5"
"minecraft": ">=1.21.9"
}
}

View File

@@ -1,11 +1,9 @@
{
"required": true,
"minVersion": "0.8",
"package": "org.geysermc.geyser.platform.fabric.gametest.mixin",
"package": "org.geysermc.geyser.platform.gametest.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"GameTestServerMixin"
],
"mixins": [],
"server": [],
"client": [],
"injectors": {

View File

@@ -93,6 +93,6 @@ public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInit
@Override
public boolean isServer() {
return FabricLoader.getInstance().getEnvironmentType().equals(EnvType.SERVER);
return FabricLoader.getInstance().getEnvironmentType().equals(EnvType.SERVER) && !GeyserFabricPlatform.isGameTestServer();
}
}

View File

@@ -34,6 +34,7 @@ import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
import org.geysermc.geyser.util.InternalPlatformType;
import java.io.IOException;
import java.io.InputStream;
@@ -41,6 +42,7 @@ import java.nio.file.Path;
import java.util.Optional;
public class GeyserFabricPlatform implements GeyserModPlatform {
private static Boolean isGameTestServer = null;
private final ModContainer mod;
@@ -50,7 +52,7 @@ public class GeyserFabricPlatform implements GeyserModPlatform {
@Override
public @NonNull PlatformType platformType() {
return PlatformType.FABRIC;
return isGameTestServer() ? InternalPlatformType.GAMETEST : PlatformType.FABRIC;
}
@Override
@@ -82,9 +84,6 @@ public class GeyserFabricPlatform implements GeyserModPlatform {
@Override
public @Nullable InputStream resolveResource(@NonNull String resource) {
if (true) {
return this.getClass().getClassLoader().getResourceAsStream(resource);
}
// We need to handle this differently, because Fabric shares the classloader across multiple mods
Path path = this.mod.findPath(resource).orElse(null);
if (path == null) {
@@ -99,4 +98,12 @@ public class GeyserFabricPlatform implements GeyserModPlatform {
return null;
}
}
public static boolean isGameTestServer() {
if (isGameTestServer != null) {
return isGameTestServer;
}
// Property is from GameTestSystemProperties, FAPI internal
return isGameTestServer = System.getProperty("fabric-api.gametest") != null && FabricLoader.getInstance().isDevelopmentEnvironment();
}
}