Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug Renderer API #230

Draft
wants to merge 7 commits into
base: 1.19
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions library/misc/debug_renderers/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
plugins {
id("qsl.module")
}

qslModule {
name = "Quilt Debug Renderers API"
moduleName = "debug_renderers"
id = "quilt_debug_renderers"
description = "Quilt APIs for creating and using debug renderers."
library = "misc"
moduleDependencies {
core {
api("qsl_base")

compileOnly("networking")
testmodOnly("networking")
}
management {
compileOnly("client_command")
compileOnly("command")
testmodOnly("client_command")
testmodOnly("command")
}
}
entrypoints {
init {
values = ["org.quiltmc.qsl.debug_renderers.impl.Initializer"]
}
client_events {
values = ["org.quiltmc.qsl.debug_renderers.impl.client.VanillaDebugRenderers"]
}
client_init {
values = ["org.quiltmc.qsl.debug_renderers.impl.client.ClientInitializer"]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.quiltmc.qsl.debug_renderers.api;

import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import org.quiltmc.loader.api.minecraft.ClientOnly;
import org.quiltmc.qsl.debug_renderers.impl.DebugFeaturesImpl;
import org.quiltmc.qsl.networking.api.PlayerLookup;

import java.util.Collection;
import java.util.List;
import java.util.Objects;

public final class DebugFeature {
private final Identifier id;
private final boolean needsServer;

private DebugFeature(Identifier id, boolean needsServer) {
this.id = id;
this.needsServer = needsServer;
}

public boolean isEnabled() {
return DebugFeaturesImpl.isEnabled(this);
}

public Collection<ServerPlayerEntity> getPlayersWithFeatureEnabled(MinecraftServer server) {
return DebugFeaturesImpl.isEnabled(this) ?
PlayerLookup.all(server).stream().filter(p -> DebugFeaturesImpl.isEnabledForPlayer(p, this)).toList() :
List.of();
}

public boolean isEnabledOnServerAndClient(ServerPlayerEntity player) {
return DebugFeaturesImpl.isEnabled(this) && DebugFeaturesImpl.isEnabledForPlayer(player, this);
}

@ClientOnly
public boolean isEnabledOnServerAndClient() {
return DebugFeaturesImpl.isEnabledOnServer(this) && DebugFeaturesImpl.isEnabledOnServer(this);
}

@ClientOnly
public boolean shouldRender() {
return this.isEnabled() && !this.needsServer() || this.isEnabledOnServerAndClient();
}

public static DebugFeature register(Identifier id, boolean needsServer) {
var existingFeature = DebugFeaturesImpl.get(id);
if (existingFeature != null) {
throw new IllegalArgumentException("A debug feature with the id %s already exists!".formatted(id));
}
var newFeature = new DebugFeature(id, needsServer);
return DebugFeaturesImpl.register(newFeature);
}

public Identifier id() {
return this.id;
}

public boolean needsServer() {
return this.needsServer;
}

@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (DebugFeature) obj;
return Objects.equals(this.id, that.id) &&
this.needsServer == that.needsServer;
}

@Override
public int hashCode() {
return Objects.hash(this.id, this.needsServer);
}

@Override
public String toString() {
return "DebugFeature[" +
"id=" + this.id + ", " +
"needsServer=" + this.needsServer + ']';
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.quiltmc.qsl.debug_renderers.api;

import org.jetbrains.annotations.ApiStatus;

import net.minecraft.util.Identifier;

/**
* In this class are {@link DebugFeature DebugFeatures} for the vanilla Debug Renderers which do not have other means
* of activation (i.e., not chunk borders, not collision, and not game test)
*/
public final class VanillaDebugFeatures {
/**
* @see net.minecraft.client.render.debug.DebugRenderer#pathfindingDebugRenderer
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_PATH
*/
public static final DebugFeature PATHFINDING = DebugFeature.register(new Identifier("pathfinding"), true);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#waterDebugRenderer
*/
public static final DebugFeature WATER = DebugFeature.register(new Identifier("water"), false);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#heightmapDebugRenderer
*/
public static final DebugFeature HEIGHTMAP = DebugFeature.register(new Identifier("heightmap"), false);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#neighborUpdateDebugRenderer
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_NEIGHBORS_UPDATE
*/
public static final DebugFeature NEIGHBORS_UPDATE = DebugFeature.register(new Identifier("neighbors_update"), true);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#structureDebugRenderer
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_STRUCTURES
*/
public static final DebugFeature STRUCTURE = DebugFeature.register(new Identifier("structure"), true);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#skyLightDebugRenderer
*/
public static final DebugFeature LIGHT = DebugFeature.register(new Identifier("light"), false);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#worldGenAttemptDebugRenderer
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_WORLDGEN_ATTEMPT
*/
public static final DebugFeature WORLD_GEN_ATTEMPT = DebugFeature.register(new Identifier("world_gen_attempt"), true);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#blockOutlineDebugRenderer
*/
public static final DebugFeature SOLID_FACE = DebugFeature.register(new Identifier("solid_face"), false);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#chunkLoadingDebugRenderer
*/
public static final DebugFeature CHUNK = DebugFeature.register(new Identifier("chunk"), false);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#villageDebugRenderer
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_POI_ADDED
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_POI_REMOVED
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_POI_TICKET_COUNT
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_BRAIN
*/
public static final DebugFeature BRAIN = DebugFeature.register(new Identifier("brain"), true);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#villageSectionsDebugRenderer
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_VILLAGE_SECTIONS
*/
public static final DebugFeature VILLAGE_SECTIONS = DebugFeature.register(new Identifier("village_sections"), true);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#beeDebugRenderer
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_BEE
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_HIVE
*/
public static final DebugFeature BEE = DebugFeature.register(new Identifier("bee"), true);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#raidCenterDebugRenderer
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_RAIDS
*/
public static final DebugFeature RAID = DebugFeature.register(new Identifier("raid"), true);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#goalSelectorDebugRenderer
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_GOAL_SELECTOR
*/
public static final DebugFeature GOAL_SELECTOR = DebugFeature.register(new Identifier("goal_selector"), true);
/**
* @see net.minecraft.client.render.debug.DebugRenderer#gameEventDebugRenderer
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_GAME_EVENT
* @see net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket#DEBUG_GAME_EVENT_LISTENERS
*/
public static final DebugFeature GAME_EVENT = DebugFeature.register(new Identifier("game_event"), true);

private VanillaDebugFeatures() {}

@ApiStatus.Internal
public static void init() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.quiltmc.qsl.debug_renderers.api.client;

import net.minecraft.client.render.debug.DebugRenderer;
import org.quiltmc.qsl.base.api.event.Event;
import org.quiltmc.qsl.base.api.event.client.ClientEventAwareListener;
import org.quiltmc.qsl.debug_renderers.api.DebugFeature;

@FunctionalInterface
public interface DebugRendererRegistrationCallback extends ClientEventAwareListener {
Event<DebugRendererRegistrationCallback> EVENT = Event.create(DebugRendererRegistrationCallback.class, callbacks -> registrar -> {
for (var callback : callbacks) {
callback.registerDebugRenderers(registrar);
}
});

void registerDebugRenderers(DebugRendererRegistrationCallback.DebugRendererRegistrar registrar);

@FunctionalInterface
interface DebugRendererRegistrar {
void register(DebugFeature feature, DebugRenderer.Renderer renderer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.quiltmc.qsl.debug_renderers.impl;

import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;

import com.mojang.brigadier.Command;
import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import org.jetbrains.annotations.ApiStatus;

import net.minecraft.command.CommandSource;
import net.minecraft.command.argument.IdentifierArgumentType;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.ClickEvent;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;

import org.quiltmc.qsl.command.api.CommandRegistrationCallback;
import org.quiltmc.qsl.debug_renderers.api.DebugFeature;

@ApiStatus.Internal
final class DebugFeatureCommands {
static void init() {
CommandRegistrationCallback.EVENT.register((dispatcher, buildContext, environment) -> dispatcher.register(
literal("quilt_debug").then(
argument("feature", IdentifierArgumentType.identifier())
.suggests((c, b) -> CommandSource.suggestIdentifiers(DebugFeaturesImpl.getFeatures().stream().filter(DebugFeature::needsServer).map(DebugFeature::id), b)).then(
literal("enable").executes(setEnabled(true))
).then(
literal("disable").executes(setEnabled(false))
)
)
));
}

private static final DynamicCommandExceptionType INVALID_FEATURE = new DynamicCommandExceptionType(id -> Text.literal("No such Debug Feature "+id+"!"));

private static final Dynamic2CommandExceptionType NOT_SERVER_FEATURE = new Dynamic2CommandExceptionType((id, enable) -> {
var suggestedCommand = "/quilt_debug_client " + id + " " + (enable == Boolean.TRUE ? "enable" : "disable");
return Text.empty()
.append(Text.literal("Debug Feature " + id + " is not server-side! Did you mean to use ["))
.append(Text.literal(suggestedCommand).styled(s -> s.withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, suggestedCommand))))
.append("]");
});

private static Command<ServerCommandSource> setEnabled(boolean value) {
return ctx -> {
var id = IdentifierArgumentType.getIdentifier(ctx, "feature");
var feature = DebugFeaturesImpl.get(id);
if (feature == null) {
throw INVALID_FEATURE.create(id);
}

if (!feature.needsServer()) {
throw NOT_SERVER_FEATURE.create(id, value);
}

DebugFeaturesImpl.setEnabledNotifyClients(feature, value, ctx.getSource().getServer());
ctx.getSource().sendFeedback(
Text.empty()
.append(Text.literal("[Debug|Server]: ").formatted(Formatting.LIGHT_PURPLE, Formatting.BOLD))
.append(Text.literal(id+" "+(value ? "enabled" : "disabled"))),
true
);
return Command.SINGLE_SUCCESS;
};
}
}
Loading