diff --git a/fabric-debug-api-v1/build.gradle b/fabric-debug-api-v1/build.gradle new file mode 100644 index 0000000000..91ac94ae90 --- /dev/null +++ b/fabric-debug-api-v1/build.gradle @@ -0,0 +1,12 @@ +version = getSubprojectVersion(project) + +moduleDependencies(project, [ + 'fabric-api-base' +]) + +testDependencies(project, [ +]) + +loom { + accessWidenerPath = file("src/main/resources/fabric-debug-api-v1.accesswidener") +} diff --git a/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/api/debug/v1/client/ClientDebugSubscriptionRegistry.java b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/api/debug/v1/client/ClientDebugSubscriptionRegistry.java new file mode 100644 index 0000000000..91e04f672f --- /dev/null +++ b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/api/debug/v1/client/ClientDebugSubscriptionRegistry.java @@ -0,0 +1,62 @@ +/* + * 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.api.debug.v1.client; + +import java.util.Objects; + +import net.minecraft.util.debug.DebugSubscription; + +import net.fabricmc.fabric.impl.debug.client.ClientDebugSubscriptionRegistryImpl; + +/// A registry for [debug subscriptions][DebugSubscription] on the client, +/// allowing listening to registered debug subscriptions on the client. +public final class ClientDebugSubscriptionRegistry { + /// Registers a [DebugSubscription] on the client. + /// + /// **Note:** this will register **outside development environments** if it + /// is not checked. Surround calls to this method with + /// [net.fabricmc.loader.api.FabricLoader#isDevelopmentEnvironment] if you + /// do not intend for a debug feature to be present in production. + /// + /// @param the inner type of the [DebugSubscription]. + /// @param debugSubscription the [DebugSubscription] to register. + public static void register(DebugSubscription debugSubscription) { + Objects.requireNonNull(debugSubscription); + ClientDebugSubscriptionRegistryImpl.register(debugSubscription); + } + + /// Registers a [DebugSubscription] on the client if the `isEnabledFlag` + /// parameter is `true`. + /// + /// **Note:** this will register **outside development environments** if it + /// is not checked. Surround calls to this method with + /// [net.fabricmc.loader.api.FabricLoader#isDevelopmentEnvironment] if you + /// do not intend for a debug feature to be present in production. + /// + /// @param the inner type of the [DebugSubscription]. + /// @param debugSubscription the [DebugSubscription] to register. + /// @param isEnabledFlag the flag determining whether to register this + /// [DebugSubscription]. + public static void register( + DebugSubscription debugSubscription, + boolean isEnabledFlag + ) { + if (isEnabledFlag) { + register(debugSubscription); + } + } +} diff --git a/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/api/debug/v1/client/renderer/DebugRendererFactory.java b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/api/debug/v1/client/renderer/DebugRendererFactory.java new file mode 100644 index 0000000000..294cf90a5f --- /dev/null +++ b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/api/debug/v1/client/renderer/DebugRendererFactory.java @@ -0,0 +1,29 @@ +/* + * 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.api.debug.v1.client.renderer; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.debug.DebugRenderer; + +/// A factory that creates a +/// [debug subscription][net.minecraft.util.debug.DebugSubscription] renderer. +@FunctionalInterface +public interface DebugRendererFactory { + /// @return a new renderer for a + /// [debug subscription][net.minecraft.util.debug.DebugSubscription]. + DebugRenderer.SimpleDebugRenderer create(Minecraft minecraft); +} diff --git a/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/api/debug/v1/client/renderer/DebugRendererRegistry.java b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/api/debug/v1/client/renderer/DebugRendererRegistry.java new file mode 100644 index 0000000000..c98c3fabef --- /dev/null +++ b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/api/debug/v1/client/renderer/DebugRendererRegistry.java @@ -0,0 +1,58 @@ +/* + * 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.api.debug.v1.client.renderer; + +import java.util.Objects; + +import net.minecraft.util.debug.DebugSubscription; + +import net.fabricmc.fabric.impl.debug.client.renderer.DebugRendererRegistryImpl; + +/// Registry for custom +/// [debug renderers][net.minecraft.client.renderer.debug.DebugRenderer.SimpleDebugRenderer]. +public final class DebugRendererRegistry { + /// Registers a debug renderer for the given [DebugSubscription]. + /// + /// @param the inner type of the [DebugSubscription]. + /// @param debugSubscription the [DebugSubscription]. + /// @param rendererFactory the factory/constructor for the debug renderer. + public static void register( + DebugSubscription debugSubscription, + DebugRendererFactory rendererFactory + ) { + Objects.requireNonNull(debugSubscription); + DebugRendererRegistryImpl.register(debugSubscription, rendererFactory); + } + + /// Registers a debug renderer for the given [DebugSubscription] if + /// `isEnabledFlag` is `true`. + /// + /// @param the inner type of the [DebugSubscription]. + /// @param debugSubscription the [DebugSubscription]. + /// @param rendererFactory the factory/constructor for the debug renderer. + /// @param isEnabledFlag the flag determining whether to register this debug + /// renderer. + public static void register( + DebugSubscription debugSubscription, + DebugRendererFactory rendererFactory, + boolean isEnabledFlag + ) { + if (isEnabledFlag) { + register(debugSubscription, rendererFactory); + } + } +} diff --git a/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/impl/debug/client/ClientDebugSubscriptionRegistryImpl.java b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/impl/debug/client/ClientDebugSubscriptionRegistryImpl.java new file mode 100644 index 0000000000..3df5e245c2 --- /dev/null +++ b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/impl/debug/client/ClientDebugSubscriptionRegistryImpl.java @@ -0,0 +1,30 @@ +/* + * 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.impl.debug.client; + +import java.util.HashSet; +import java.util.Set; + +import net.minecraft.util.debug.DebugSubscription; + +public final class ClientDebugSubscriptionRegistryImpl { + public static final Set> DEBUG_SUBSCRIPTIONS = new HashSet<>(); + + public static void register(DebugSubscription debugSubscription) { + DEBUG_SUBSCRIPTIONS.add(debugSubscription); + } +} diff --git a/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/impl/debug/client/renderer/DebugRendererRegistryImpl.java b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/impl/debug/client/renderer/DebugRendererRegistryImpl.java new file mode 100644 index 0000000000..be8e4250f0 --- /dev/null +++ b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/impl/debug/client/renderer/DebugRendererRegistryImpl.java @@ -0,0 +1,59 @@ +/* + * 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.impl.debug.client.renderer; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import net.minecraft.util.debug.DebugSubscription; + +import net.fabricmc.fabric.api.debug.v1.client.renderer.DebugRendererFactory; + +public final class DebugRendererRegistryImpl { + public static final Set RENDERERS = new HashSet<>(); + + public static void register( + DebugSubscription debugSubscription, + DebugRendererFactory rendererFactory + ) { + RENDERERS.add(new Entry(debugSubscription, rendererFactory)); + } + + public record Entry( + DebugSubscription debugSubscription, + DebugRendererFactory rendererFactory + ) { + // Ensure DebugSubscriptions with different values don't both + // get into the Set, causing undesirable behavior + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + Entry entry = (Entry) o; + return Objects.equals( + debugSubscription, + entry.debugSubscription + ); + } + + @Override + public int hashCode() { + return Objects.hashCode(debugSubscription); + } + } +} diff --git a/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/mixin/debug/client/ClientDebugSubscriberMixin.java b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/mixin/debug/client/ClientDebugSubscriberMixin.java new file mode 100644 index 0000000000..b7c8637ace --- /dev/null +++ b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/mixin/debug/client/ClientDebugSubscriberMixin.java @@ -0,0 +1,44 @@ +/* + * 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.debug.client; + +import java.util.Set; + +import com.llamalad7.mixinextras.sugar.Local; +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.client.multiplayer.ClientDebugSubscriber; +import net.minecraft.util.debug.DebugSubscription; + +import net.fabricmc.fabric.impl.debug.client.ClientDebugSubscriptionRegistryImpl; + +@Mixin(ClientDebugSubscriber.class) +public abstract class ClientDebugSubscriberMixin { + @Inject( + method = "requestedSubscriptions", + at = @At("RETURN") + ) + private void addSubscribers( + CallbackInfoReturnable>> cir, + @Local(name = "subscriptions") Set> subscriptions + ) { + subscriptions.addAll(ClientDebugSubscriptionRegistryImpl.DEBUG_SUBSCRIPTIONS); + } +} diff --git a/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/mixin/debug/client/DebugRendererMixin.java b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/mixin/debug/client/DebugRendererMixin.java new file mode 100644 index 0000000000..7ea9177479 --- /dev/null +++ b/fabric-debug-api-v1/src/client/java/net/fabricmc/fabric/mixin/debug/client/DebugRendererMixin.java @@ -0,0 +1,52 @@ +/* + * 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.debug.client; + +import java.util.List; + +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.CallbackInfo; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.debug.DebugRenderer; + +import net.fabricmc.fabric.impl.debug.client.renderer.DebugRendererRegistryImpl; + +@Mixin(DebugRenderer.class) +public abstract class DebugRendererMixin { + @Shadow + @Final + private List renderers; + + private DebugRendererMixin() { + } + + @Inject(method = "refreshRendererList", at = @At("RETURN")) + private void registerRenderers(CallbackInfo ci) { + Minecraft minecraft = Minecraft.getInstance(); + + for (DebugRendererRegistryImpl.Entry entry : DebugRendererRegistryImpl.RENDERERS) { + // a Stream#map would make the most sense here, but they're banned + // so you have to suffer with me now + renderers.add(entry.rendererFactory().create(minecraft)); + } + } +} diff --git a/fabric-debug-api-v1/src/client/resources/fabric-debug-api-v1.client.mixins.json b/fabric-debug-api-v1/src/client/resources/fabric-debug-api-v1.client.mixins.json new file mode 100644 index 0000000000..3e5cfed058 --- /dev/null +++ b/fabric-debug-api-v1/src/client/resources/fabric-debug-api-v1.client.mixins.json @@ -0,0 +1,18 @@ +{ + "required": true, + "package": "net.fabricmc.fabric.mixin.debug.client", + "compatibilityLevel": "JAVA_25", + "client": [ + "ClientDebugSubscriberMixin", + "DebugRendererMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "overwrites": { + "requireAnnotations": true + }, + "mixinextras": { + "minVersion": "0.5.0" + } +} diff --git a/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/api/debug/v1/DebugValueFactory.java b/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/api/debug/v1/DebugValueFactory.java new file mode 100644 index 0000000000..c41cd34a0b --- /dev/null +++ b/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/api/debug/v1/DebugValueFactory.java @@ -0,0 +1,27 @@ +/* + * 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.api.debug.v1; + +/// A factory that creates a debug value of type [T] using data of type [D]. +/// +/// @param the data passed for construction +/// (e.g. [net.minecraft.world.entity.Entity]). +/// @param the debug value being constructed. +@FunctionalInterface +public interface DebugValueFactory { + T create(D data); +} diff --git a/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/api/debug/v1/EntityDebugSubscriptionRegistry.java b/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/api/debug/v1/EntityDebugSubscriptionRegistry.java new file mode 100644 index 0000000000..4783ea6266 --- /dev/null +++ b/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/api/debug/v1/EntityDebugSubscriptionRegistry.java @@ -0,0 +1,77 @@ +/* + * 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.api.debug.v1; + +import java.util.Objects; +import java.util.function.Predicate; + +import net.minecraft.util.debug.DebugSubscription; +import net.minecraft.world.entity.Entity; + +import net.fabricmc.fabric.impl.debug.EntityDebugSubscriptionRegistryImpl; + +/// A server-side registry for [debug subscriptions][DebugSubscription] specific +/// to entities or properties of entities. +public final class EntityDebugSubscriptionRegistry { + /// Registers a [DebugSubscription] based on a given [Entity] and + /// [Predicate]. + /// + /// @param the inner type of the [DebugSubscription]. + /// @param the type of [Entity] to check against. + /// @param debugSubscription the [DebugSubscription]. + /// @param shouldSubscribe whether an [Entity] should subscribe to this + /// [DebugSubscription]. + /// @param valueFactory the factory for the value of type [T]. + public static void register( + DebugSubscription debugSubscription, + Predicate shouldSubscribe, + DebugValueFactory valueFactory + ) { + Objects.requireNonNull(debugSubscription); + EntityDebugSubscriptionRegistryImpl.register( + debugSubscription, + shouldSubscribe, + valueFactory + ); + } + + /// Registers a [DebugSubscription] based on a given [Entity] and + /// [Predicate] if `isEnabledFlag` is `true`. + /// + /// @param the inner type of the [DebugSubscription]. + /// @param the type of [Entity] to check against. + /// @param debugSubscription the [DebugSubscription]. + /// @param shouldSubscribe whether an [Entity] should subscribe to this + /// [DebugSubscription]. + /// @param valueFactory the factory for the value of type [T]. + /// @param isEnabledFlag the flag determining whether to register this + /// [DebugSubscription]. + public static void register( + DebugSubscription debugSubscription, + Predicate shouldSubscribe, + DebugValueFactory valueFactory, + boolean isEnabledFlag + ) { + if (isEnabledFlag) { + register( + debugSubscription, + shouldSubscribe, + valueFactory + ); + } + } +} diff --git a/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/impl/debug/EntityDebugSubscriptionRegistryImpl.java b/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/impl/debug/EntityDebugSubscriptionRegistryImpl.java new file mode 100644 index 0000000000..1c3f66a0c6 --- /dev/null +++ b/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/impl/debug/EntityDebugSubscriptionRegistryImpl.java @@ -0,0 +1,84 @@ +/* + * 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.impl.debug; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; + +import net.minecraft.util.debug.DebugSubscription; +import net.minecraft.util.debug.DebugValueSource; +import net.minecraft.world.entity.Entity; + +import net.fabricmc.fabric.api.debug.v1.DebugValueFactory; + +public final class EntityDebugSubscriptionRegistryImpl { + public static final Set ENTITY_DEBUG_SUBSCRIPTIONS = + new HashSet<>(); + + public static void register( + DebugSubscription debugSubscription, + Predicate shouldSubscribe, + DebugValueFactory valueFactory + ) { + //noinspection unchecked // Casts to super-type + ENTITY_DEBUG_SUBSCRIPTIONS.add(new Entry( + (DebugSubscription) debugSubscription, + shouldSubscribe, + (DebugValueFactory) valueFactory + )); + } + + public static void addDebugValues( + Object entity, + DebugValueSource.Registration registration + ) { + for (Entry entry : ENTITY_DEBUG_SUBSCRIPTIONS) { + if (entry.shouldSubscribe().test((Entity) entity)) { + registration.register( + entry.debugSubscription(), + () -> entry.valueFactory().create((Entity) entity) + ); + } + } + } + + public record Entry( + DebugSubscription debugSubscription, + Predicate shouldSubscribe, + DebugValueFactory valueFactory + ) { + // Ensure DebugSubscriptions with different values don't both + // get into the Set, causing undesirable behavior + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + Entry entry = (Entry) o; + return Objects.equals( + debugSubscription, + entry.debugSubscription + ); + } + + @Override + public int hashCode() { + return Objects.hash(debugSubscription); + } + } +} diff --git a/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/mixin/debug/EntityMixin.java b/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/mixin/debug/EntityMixin.java new file mode 100644 index 0000000000..40e1704fb5 --- /dev/null +++ b/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/mixin/debug/EntityMixin.java @@ -0,0 +1,43 @@ +/* + * 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.debug; + +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.CallbackInfo; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.debug.DebugValueSource; +import net.minecraft.world.entity.Entity; + +import net.fabricmc.fabric.impl.debug.EntityDebugSubscriptionRegistryImpl; + +@Mixin(Entity.class) +public abstract class EntityMixin { + @Inject( + method = "registerDebugValues", + at = @At("HEAD") + ) + private void addDebugValues( + ServerLevel level, + DebugValueSource.Registration registration, + CallbackInfo ci + ) { + EntityDebugSubscriptionRegistryImpl.addDebugValues(this, registration); + } +} diff --git a/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/mixin/debug/MobMixin.java b/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/mixin/debug/MobMixin.java new file mode 100644 index 0000000000..4f90e2e12d --- /dev/null +++ b/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/mixin/debug/MobMixin.java @@ -0,0 +1,46 @@ +/* + * 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.debug; + +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.CallbackInfo; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.debug.DebugValueSource; +import net.minecraft.world.entity.Mob; + +import net.fabricmc.fabric.impl.debug.EntityDebugSubscriptionRegistryImpl; + +/// The Mob class does not super-call +/// [net.minecraft.world.entity.Entity#registerDebugValues], so we have to +/// duplicate some code. +@Mixin(Mob.class) +public abstract class MobMixin { + @Inject( + method = "registerDebugValues", + at = @At("HEAD") + ) + private void addDebugValues( + ServerLevel level, + DebugValueSource.Registration registration, + CallbackInfo ci + ) { + EntityDebugSubscriptionRegistryImpl.addDebugValues(this, registration); + } +} diff --git a/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/mixin/debug/ServerDebugSubscribersMixin.java b/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/mixin/debug/ServerDebugSubscribersMixin.java new file mode 100644 index 0000000000..5112fb639a --- /dev/null +++ b/fabric-debug-api-v1/src/main/java/net/fabricmc/fabric/mixin/debug/ServerDebugSubscribersMixin.java @@ -0,0 +1,45 @@ +/* + * 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.debug; + +import com.llamalad7.mixinextras.expression.Definition; +import com.llamalad7.mixinextras.expression.Expression; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import net.minecraft.util.debug.ServerDebugSubscribers; + +import net.fabricmc.loader.api.FabricLoader; + +@Mixin(ServerDebugSubscribers.class) +public abstract class ServerDebugSubscribersMixin { + @Definition( + id = "IS_RUNNING_IN_IDE", + field = "Lnet/minecraft/SharedConstants;IS_RUNNING_IN_IDE:Z" + ) + @Expression("IS_RUNNING_IN_IDE") + @WrapOperation( + method = "hasRequiredPermissions", + at = @At("MIXINEXTRAS:EXPRESSION") + ) + private boolean requireInIde(Operation original) { + return original.call() || FabricLoader.getInstance() + .isDevelopmentEnvironment(); + } +} diff --git a/fabric-debug-api-v1/src/main/resources/assets/fabric-debug-api-v1/icon.png b/fabric-debug-api-v1/src/main/resources/assets/fabric-debug-api-v1/icon.png new file mode 100644 index 0000000000..12c4531de9 Binary files /dev/null and b/fabric-debug-api-v1/src/main/resources/assets/fabric-debug-api-v1/icon.png differ diff --git a/fabric-debug-api-v1/src/main/resources/fabric-debug-api-v1.accesswidener b/fabric-debug-api-v1/src/main/resources/fabric-debug-api-v1.accesswidener new file mode 100644 index 0000000000..33eaa64a38 --- /dev/null +++ b/fabric-debug-api-v1/src/main/resources/fabric-debug-api-v1.accesswidener @@ -0,0 +1 @@ +accessWidener v1 official diff --git a/fabric-debug-api-v1/src/main/resources/fabric-debug-api-v1.mixins.json b/fabric-debug-api-v1/src/main/resources/fabric-debug-api-v1.mixins.json new file mode 100644 index 0000000000..3d269d3c84 --- /dev/null +++ b/fabric-debug-api-v1/src/main/resources/fabric-debug-api-v1.mixins.json @@ -0,0 +1,19 @@ +{ + "required": true, + "package": "net.fabricmc.fabric.mixin.debug", + "compatibilityLevel": "JAVA_25", + "mixins": [ + "EntityMixin", + "MobMixin", + "ServerDebugSubscribersMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "overwrites": { + "requireAnnotations": true + }, + "mixinextras": { + "minVersion": "0.5.0" + } +} diff --git a/fabric-debug-api-v1/src/main/resources/fabric.mod.json b/fabric-debug-api-v1/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..b2786dcf90 --- /dev/null +++ b/fabric-debug-api-v1/src/main/resources/fabric.mod.json @@ -0,0 +1,35 @@ +{ + "schemaVersion": 1, + "id": "fabric-debug-api-v1", + "name": "Fabric Debug API (v1)", + "version": "${version}", + "environment": "*", + "license": "Apache-2.0", + "icon": "assets/fabric-debug-api-v1/icon.png", + "contact": { + "homepage": "https://fabricmc.net", + "irc": "irc://irc.esper.net:6667/fabric", + "issues": "https://github.com/FabricMC/fabric/issues", + "sources": "https://github.com/FabricMC/fabric" + }, + "authors": [ + "FabricMC" + ], + "depends": { + "fabricloader": ">=0.18.3", + "fabric-api-base": "*", + "fabric-registry-sync-v0": "*" + }, + "description": "A toolkit for registering and using debug subscriptions and other debug tools Mojang have created.", + "mixins": [ + "fabric-debug-api-v1.mixins.json", + { + "config": "fabric-debug-api-v1.client.mixins.json", + "environment": "client" + } + ], + "accessWidener" : "fabric-debug-api-v1.accesswidener", + "custom": { + "fabric-api:module-lifecycle": "stable" + } +} diff --git a/fabric-debug-api-v1/src/testmod/java/net/fabricmc/fabric/test/debug/DebugApiTest.java b/fabric-debug-api-v1/src/testmod/java/net/fabricmc/fabric/test/debug/DebugApiTest.java new file mode 100644 index 0000000000..4c589bc575 --- /dev/null +++ b/fabric-debug-api-v1/src/testmod/java/net/fabricmc/fabric/test/debug/DebugApiTest.java @@ -0,0 +1,56 @@ +/* + * 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.test.debug; + +import net.minecraft.core.Registry; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; +import net.minecraft.util.debug.DebugSubscription; +import net.minecraft.world.entity.Avatar; +import net.minecraft.world.entity.decoration.Mannequin; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.debug.v1.EntityDebugSubscriptionRegistry; +import net.fabricmc.loader.api.FabricLoader; + +public class DebugApiTest implements ModInitializer { + public static DebugSubscription SUS_AVATAR; + public static boolean DEBUG_SUS_AVATAR = true; + + @Override + public void onInitialize() { + if (FabricLoader.getInstance().isDevelopmentEnvironment()) { + SUS_AVATAR = Registry.register( + BuiltInRegistries.DEBUG_SUBSCRIPTION, + Identifier.fromNamespaceAndPath( + "fabric-debug-api-v1-testmod", + "sus_avatar" + ), + new DebugSubscription<>(SusDebugInfo.STREAM_CODEC) + ); + EntityDebugSubscriptionRegistry.register( + SUS_AVATAR, + entity -> entity instanceof Avatar, + avatar -> new SusDebugInfo( + avatar.getPlainTextName(), + avatar instanceof Mannequin + ), + DEBUG_SUS_AVATAR + ); + } + } +} diff --git a/fabric-debug-api-v1/src/testmod/java/net/fabricmc/fabric/test/debug/SusDebugInfo.java b/fabric-debug-api-v1/src/testmod/java/net/fabricmc/fabric/test/debug/SusDebugInfo.java new file mode 100644 index 0000000000..103dc6020a --- /dev/null +++ b/fabric-debug-api-v1/src/testmod/java/net/fabricmc/fabric/test/debug/SusDebugInfo.java @@ -0,0 +1,36 @@ +/* + * 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.test.debug; + +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; + +public record SusDebugInfo( + String playerName, + boolean isSuspicious +) { + public static final StreamCodec STREAM_CODEC = StreamCodec.of( + (byteBuf, object) -> { + byteBuf.writeUtf(object.playerName); + byteBuf.writeBoolean(object.isSuspicious); + }, + byteBuf -> new SusDebugInfo( + byteBuf.readUtf(), + byteBuf.readBoolean() + ) + ); +} diff --git a/fabric-debug-api-v1/src/testmod/resources/fabric.mod.json b/fabric-debug-api-v1/src/testmod/resources/fabric.mod.json new file mode 100644 index 0000000000..ee5b29f9e7 --- /dev/null +++ b/fabric-debug-api-v1/src/testmod/resources/fabric.mod.json @@ -0,0 +1,18 @@ +{ + "schemaVersion": 1, + "id": "fabric-debug-api-v1-testmod", + "name": "Fabric Debug API (v1) Test Mod", + "version": "1.0.0", + "environment": "*", + "license": "Apache-2.0", + "entrypoints": { + "main": [ + "net.fabricmc.fabric.test.debug.DebugApiTest" + ], + "client": [ + "net.fabricmc.fabric.test.debug.client.DebugApiTestClient" + ], + "fabric-client-gametest": [ + ] + } +} diff --git a/fabric-debug-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/debug/client/DebugApiTestClient.java b/fabric-debug-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/debug/client/DebugApiTestClient.java new file mode 100644 index 0000000000..3fe76a822c --- /dev/null +++ b/fabric-debug-api-v1/src/testmodClient/java/net/fabricmc/fabric/test/debug/client/DebugApiTestClient.java @@ -0,0 +1,128 @@ +/* + * 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.test.debug.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.culling.Frustum; +import net.minecraft.client.renderer.debug.DebugRenderer; +import net.minecraft.gizmos.GizmoStyle; +import net.minecraft.gizmos.Gizmos; +import net.minecraft.gizmos.TextGizmo; +import net.minecraft.util.ARGB; +import net.minecraft.util.debug.DebugValueAccess; +import net.minecraft.world.phys.Vec3; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.debug.v1.client.ClientDebugSubscriptionRegistry; +import net.fabricmc.fabric.api.debug.v1.client.renderer.DebugRendererRegistry; +import net.fabricmc.fabric.test.debug.DebugApiTest; +import net.fabricmc.loader.api.FabricLoader; + +public class DebugApiTestClient implements ClientModInitializer { + @Override + public void onInitializeClient() { + if (FabricLoader.getInstance().isDevelopmentEnvironment()) { + ClientDebugSubscriptionRegistry.register( + DebugApiTest.SUS_AVATAR, + DebugApiTest.DEBUG_SUS_AVATAR + ); + DebugRendererRegistry.register( + DebugApiTest.SUS_AVATAR, + SuspiciousDebugRenderer::new, + DebugApiTest.DEBUG_SUS_AVATAR + ); + } + } + + public static class SuspiciousDebugRenderer implements DebugRenderer.SimpleDebugRenderer { + public SuspiciousDebugRenderer(Minecraft ignoredMinecraft) { + } + + @Override + public void emitGizmos( + double d, + double e, + double f, + DebugValueAccess debugValueAccess, + Frustum frustum, + float g + ) { + debugValueAccess.forEachEntity( + DebugApiTest.SUS_AVATAR, + (entity, susDebugInfo) -> { + Gizmos.billboardText( + susDebugInfo.playerName(), + new Vec3(entity.getX(), entity.getY() + 3.25, entity.getZ()), + TextGizmo.Style.whiteAndCentered() + ); + + if (susDebugInfo.isSuspicious()) { + Vec3 arrowPos = new Vec3( + entity.getX() + 1.0, + entity.getY() + 2.7, + entity.getZ() + 1.0 + ); + Gizmos.arrow( + arrowPos, + arrowPos.add(new Vec3(-0.5, -1.0, -0.5)), + ARGB.color(255, 255, 0, 0) + ); + Gizmos.billboardText( + "Sussy", + arrowPos.add(0.0, 0.125, 0.0), + TextGizmo.Style.forColorAndCentered(ARGB.color( + 255, + 191, + 0, + 0 + )) + ); + Gizmos.circle( + new SwappedVec3(entity.getEyePosition()), + 0.5f, + GizmoStyle.stroke(ARGB.color( + 255, + 255, + 0, + 0 + ), 8.0f) + ); + } + } + ); + } + } + + private static class SwappedVec3 extends Vec3 { + SwappedVec3(double x, double y, double z) { + super(x, y, z); + } + + SwappedVec3(Vec3 vec3) { + this(vec3.x(), vec3.y(), vec3.z()); + } + + @Override + public Vec3 add(double d, double e, double f) { + return super.add( + f, + d, + e + ); + } + } +} diff --git a/gradle.properties b/gradle.properties index 48d90dce40..6a9e855e8d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,6 +22,7 @@ fabric-content-registries-v0-version=11.0.3 fabric-crash-report-info-v1-version=1.0.0 fabric-data-attachment-api-v1-version=2.0.4 fabric-data-generation-api-v1-version=24.0.5 +fabric-debug-api-v1-version=1.0.0 fabric-dimensions-v1-version=5.0.0 fabric-entity-events-v1-version=4.1.1 fabric-events-interaction-v0-version=5.1.2 diff --git a/settings.gradle b/settings.gradle index f9ece75fe5..8e249715bc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -37,6 +37,7 @@ include 'fabric-crash-report-info-v1' include 'fabric-creative-tab-api-v1' include 'fabric-data-attachment-api-v1' include 'fabric-data-generation-api-v1' +include 'fabric-debug-api-v1' include 'fabric-dimensions-v1' include 'fabric-entity-events-v1' include 'fabric-events-interaction-v0'