Compare commits

..
15 Commits
Author SHA1 Message Date
LexManos ef0e7bc17f 1.21.8 RB 1 2025-08-27 12:17:25 -07:00
Matt ec7a098f81 Add event for adding framepasses (#10612) 2025-08-23 22:09:12 -07:00
Matt ff25961a05 Add ForgeLayeredDraw & AddGuiOverlayLayersEvent as API for gui overlay management. (#10579) (#10611) 2025-08-21 12:56:12 -07:00
LexManos c999a2d4eb Fix Inactivated activator rails, acting like inactivated powered rails. Closes #10643 2025-08-21 08:50:35 -07:00
LexManos ce79df9716 Fix Redstone power propagating between powered and activator rails. Fixed #10642 2025-08-20 12:20:11 -07:00
LexManos eae9b9145d Optimize VillagerTradingManager, avoid redundant work. Closes #10577 2025-08-12 18:02:33 -07:00
Paint_Ninja 7f56986027 Minor cleanup to FML, fix double parsing of mods.toml (#10597) 2025-08-04 12:50:39 -07:00
Jonathing c6874bce67 Update GradleUtils to 2.6.4 (#10600) 2025-08-02 21:21:24 -07:00
Paint_Ninja aa11175f41 Sync deps with MC (#10596) 2025-08-02 05:27:19 -07:00
Joseph T. McQuigg 2730559ff3 Allow command suggestions/autocomplete to search all modded paths at once without namespace provided (#10593) 2025-07-31 09:13:35 -07:00
Paint_Ninja e80c3402b7 Optimise WorldWorkerManager (#10578) 2025-07-23 18:37:02 +01:00
LexManos 837c259668 1.21.8 - Intel Graphic Fixes 2025-07-17 11:09:42 -07:00
LexManos 2e48910b4c Fix crash when clicking links in modlist without being in-world. 2025-07-16 10:21:55 -07:00
LexManos 70dd556db6 Use vanilla's entrypoint for GameTest servers 2025-07-01 23:56:59 -07:00
LexManos e44a558f37 Fix Forge error screen not rendering text correctly. Closes #10569 2025-07-01 23:41:08 -07:00
49 changed files with 1017 additions and 119 deletions

No files matched your search

+2 -3
View File
@@ -4,7 +4,7 @@ import net.minecraftforge.gradleutils.PomUtils
plugins {
id 'net.minecraftforge.licenser' version '1.0.1'
id 'com.github.ben-manes.versions' version '0.46.0'
id 'net.minecraftforge.gradleutils' version '[2.3.0,2.4.0)'
id 'net.minecraftforge.gradleutils' version '[2.6.4,2.7)'
id 'eclipse'
id 'de.undercouch.download' version '5.4.0'
id 'net.minecraftforge.gradle.patcher' version '[6.0.22,6.2)' apply false
@@ -16,8 +16,7 @@ plugins {
Util.init() //Init all our extension methods!
ext {
GIT_INFO = gradleutils.gitInfo
VERSION = gradleutils.getFilteredMCTagOffsetBranchVersion(true, '[0-9]', MC_VERSION)
VERSION = gitversion.getMCTagOffsetBranch(MC_VERSION)
FORGE_VERSION = VERSION.substring(MC_VERSION.length() + 1)
}
+1 -1
View File
@@ -71,7 +71,7 @@ sourceSets {
}
}
final String SPEC_VERSION = gradleutils.gitInfo.tag
final String SPEC_VERSION = gitversion.info.tag
// The new versioning sceme is <MCVersion>-<ForgeMC>.<RB>.<CommitsSinceRB>
// ForgeMC is a unique identifier for every MC version we have supported.
+2 -2
View File
@@ -2,8 +2,8 @@
MinecraftForge
=============
[![Stable Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Stable&prefix=1.21.4-&query=$.promos["1.21.4-recommended"]&color=brightgreen&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc3RhciBmYS13LTE4IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZGF0YS1pY29uPSJzdGFyIiBkYXRhLXByZWZpeD0iZmFzIiBmb2N1c2FibGU9ImZhbHNlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMjU5LjMgMTcuOEwxOTQgMTUwLjIgNDcuOSAxNzEuNWMtMjYuMiAzLjgtMzYuNyAzNi4xLTE3LjcgNTQuNmwxMDUuNyAxMDMtMjUgMTQ1LjVjLTQuNSAyNi4zIDIzLjIgNDYgNDYuNCAzMy43TDI4OCA0MzkuNmwxMzAuNyA2OC43YzIzLjIgMTIuMiA1MC45LTcuNCA0Ni40LTMzLjdsLTI1LTE0NS41IDEwNS43LTEwM2MxOS0xOC41IDguNS01MC44LTE3LjctNTQuNkwzODIgMTUwLjIgMzE2LjcgMTcuOGMtMTEuNy0yMy42LTQ1LjYtMjMuOS01Ny40IDB6IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K)][Download]
[![Latest Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Latest&prefix=1.21.5-&query=$.promos["1.21.5-latest"]&color=blue&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtYnVnIGZhLXctMTYiIGFyaWEtaGlkZGVuPSJ0cnVlIiBkYXRhLWljb249ImJ1ZyIgZGF0YS1wcmVmaXg9ImZhcyIgZm9jdXNhYmxlPSJmYWxzZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTUxMS45ODggMjg4LjljLS40NzggMTcuNDMtMTUuMjE3IDMxLjEtMzIuNjUzIDMxLjFINDI0djE2YzAgMjEuODY0LTQuODgyIDQyLjU4NC0xMy42IDYxLjE0NWw2MC4yMjggNjAuMjI4YzEyLjQ5NiAxMi40OTcgMTIuNDk2IDMyLjc1OCAwIDQ1LjI1NS0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDBsLTU0LjczNi01NC43MzZDMzQ1Ljg4NiA0NjcuOTY1IDMxNC4zNTEgNDgwIDI4MCA0ODBWMjM2YzAtNi42MjctNS4zNzMtMTItMTItMTJoLTI0Yy02LjYyNyAwLTEyIDUuMzczLTEyIDEydjI0NGMtMzQuMzUxIDAtNjUuODg2LTEyLjAzNS05MC42MzYtMzIuMTA4bC01NC43MzYgNTQuNzM2Yy0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDAtMTIuNDk2LTEyLjQ5Ny0xMi40OTYtMzIuNzU4IDAtNDUuMjU1bDYwLjIyOC02MC4yMjhDOTIuODgyIDM3OC41ODQgODggMzU3Ljg2NCA4OCAzMzZ2LTE2SDMyLjY2NkMxNS4yMyAzMjAgLjQ5MSAzMDYuMzMuMDEzIDI4OC45LS40ODQgMjcwLjgxNiAxNC4wMjggMjU2IDMyIDI1Nmg1NnYtNTguNzQ1bC00Ni42MjgtNDYuNjI4Yy0xMi40OTYtMTIuNDk3LTEyLjQ5Ni0zMi43NTggMC00NS4yNTUgMTIuNDk4LTEyLjQ5NyAzMi43NTgtMTIuNDk3IDQ1LjI1NiAwTDE0MS4yNTUgMTYwaDIyOS40ODlsNTQuNjI3LTU0LjYyN2MxMi40OTgtMTIuNDk3IDMyLjc1OC0xMi40OTcgNDUuMjU2IDAgMTIuNDk2IDEyLjQ5NyAxMi40OTYgMzIuNzU4IDAgNDUuMjU1TDQyNCAxOTcuMjU1VjI1Nmg1NmMxNy45NzIgMCAzMi40ODQgMTQuODE2IDMxLjk4OCAzMi45ek0yNTcgMGMtNjEuODU2IDAtMTEyIDUwLjE0NC0xMTIgMTEyaDIyNEMzNjkgNTAuMTQ0IDMxOC44NTYgMCAyNTcgMHoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=
[![Stable Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Stable&prefix=1.21.8-&query=$.promos["1.21.8-recommended"]&color=brightgreen&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc3RhciBmYS13LTE4IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZGF0YS1pY29uPSJzdGFyIiBkYXRhLXByZWZpeD0iZmFzIiBmb2N1c2FibGU9ImZhbHNlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMjU5LjMgMTcuOEwxOTQgMTUwLjIgNDcuOSAxNzEuNWMtMjYuMiAzLjgtMzYuNyAzNi4xLTE3LjcgNTQuNmwxMDUuNyAxMDMtMjUgMTQ1LjVjLTQuNSAyNi4zIDIzLjIgNDYgNDYuNCAzMy43TDI4OCA0MzkuNmwxMzAuNyA2OC43YzIzLjIgMTIuMiA1MC45LTcuNCA0Ni40LTMzLjdsLTI1LTE0NS41IDEwNS43LTEwM2MxOS0xOC41IDguNS01MC44LTE3LjctNTQuNkwzODIgMTUwLjIgMzE2LjcgMTcuOGMtMTEuNy0yMy42LTQ1LjYtMjMuOS01Ny40IDB6IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K)][Download]
[![Latest Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Latest&prefix=1.21.8-&query=$.promos["1.21.8-latest"]&color=blue&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtYnVnIGZhLXctMTYiIGFyaWEtaGlkZGVuPSJ0cnVlIiBkYXRhLWljb249ImJ1ZyIgZGF0YS1wcmVmaXg9ImZhcyIgZm9jdXNhYmxlPSJmYWxzZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTUxMS45ODggMjg4LjljLS40NzggMTcuNDMtMTUuMjE3IDMxLjEtMzIuNjUzIDMxLjFINDI0djE2YzAgMjEuODY0LTQuODgyIDQyLjU4NC0xMy42IDYxLjE0NWw2MC4yMjggNjAuMjI4YzEyLjQ5NiAxMi40OTcgMTIuNDk2IDMyLjc1OCAwIDQ1LjI1NS0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDBsLTU0LjczNi01NC43MzZDMzQ1Ljg4NiA0NjcuOTY1IDMxNC4zNTEgNDgwIDI4MCA0ODBWMjM2YzAtNi42MjctNS4zNzMtMTItMTItMTJoLTI0Yy02LjYyNyAwLTEyIDUuMzczLTEyIDEydjI0NGMtMzQuMzUxIDAtNjUuODg2LTEyLjAzNS05MC42MzYtMzIuMTA4bC01NC43MzYgNTQuNzM2Yy0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDAtMTIuNDk2LTEyLjQ5Ny0xMi40OTYtMzIuNzU4IDAtNDUuMjU1bDYwLjIyOC02MC4yMjhDOTIuODgyIDM3OC41ODQgODggMzU3Ljg2NCA4OCAzMzZ2LTE2SDMyLjY2NkMxNS4yMyAzMjAgLjQ5MSAzMDYuMzMuMDEzIDI4OC45LS40ODQgMjcwLjgxNiAxNC4wMjggMjU2IDMyIDI1Nmg1NnYtNTguNzQ1bC00Ni42MjgtNDYuNjI4Yy0xMi40OTYtMTIuNDk3LTEyLjQ5Ni0zMi43NTggMC00NS4yNTUgMTIuNDk4LTEyLjQ5NyAzMi43NTgtMTIuNDk3IDQ1LjI1NiAwTDE0MS4yNTUgMTYwaDIyOS40ODlsNTQuNjI3LTU0LjYyN2MxMi40OTgtMTIuNDk3IDMyLjc1OC0xMi40OTcgNDUuMjU2IDAgMTIuNDk2IDEyLjQ5NyAxMi40OTYgMzIuNzU4IDAgNDUuMjU1TDQyNCAxOTcuMjU1VjI1Nmg1NmMxNy45NzIgMCAzMi40ODQgMTQuODE2IDMxLjk4OCAzMi45ek0yNTcgMGMtNjEuODU2IDAtMTEyIDUwLjE0NC0xMTIgMTEyaDIyNEMzNjkgNTAuMTQ0IDMxOC44NTYgMCAyNTcgMHoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=
)][Download] [![Discord](https://img.shields.io/discord/313125603924639766.svg?color=%237289da&label=Discord&logo=discord&logoColor=%237289da)][Discord] [![Support](https://img.shields.io/badge/Patreon-Support-orange.svg?logo=Patreon)][Patreon]
Forge is a free, open-source modding API all of your favourite mods use!
@@ -39,14 +39,13 @@ import java.util.jar.Manifest;
public class ModFile implements IModFile {
private static final Logger LOGGER = LogUtils.getLogger();
private final String jarVersion;
private final ModFileFactory.ModFileInfoParser parser;
private Map<String, Object> fileProperties;
private List<IModLanguageProvider> loaders;
private Throwable scanError;
private final SecureJar jar;
private final Type modFileType;
private final IModProvider provider;
private IModFileInfo modFileInfo;
private final IModFileInfo modFileInfo;
private ModFileScanData fileModFileScanData;
private CompletableFuture<ModFileScanData> futureScanResult;
private List<CoreModFile> coreMods;
@@ -62,12 +61,11 @@ public class ModFile implements IModFile {
public ModFile(final SecureJar jar, final IModProvider provider, final ModFileFactory.ModFileInfoParser parser, String type) {
this.provider = provider;
this.jar = jar;
this.parser = parser;
var manifest = this.jar.moduleDataProvider().getManifest();
modFileType = Type.valueOf(type);
jarVersion = Optional.ofNullable(manifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION)).orElse("0.0NONE");
this.modFileInfo = ModFileParser.readModList(this, this.parser);
this.modFileInfo = ModFileParser.readModList(this, parser);
}
@Override
@@ -99,7 +97,6 @@ public class ModFile implements IModFile {
}
public boolean identifyMods() {
this.modFileInfo = ModFileParser.readModList(this, this.parser);
if (this.modFileInfo == null) return this.getType() != Type.MOD;
LOGGER.debug(LogMarkers.LOADING,"Loading mod file {} with languages {}", this.getFilePath(), this.modFileInfo.requiredLanguageLoaders());
this.coreMods = ModFileParser.getCoreMods(this);
@@ -84,7 +84,7 @@ public abstract class CommonLaunchHandler implements ILaunchHandlerService {
protected static final LaunchType CLIENT_DATA = new LaunchType("client_data", "minecraft", "net.minecraft.client.data.Main", Dist.CLIENT, true);
protected static final LaunchType DATA = new LaunchType("data", "minecraft", "net.minecraft.data.Main", Dist.CLIENT, true);
protected static final LaunchType SERVER = new LaunchType("server", "minecraft", "net.minecraft.server.Main", Dist.DEDICATED_SERVER, false);
protected static final LaunchType SERVER_GAMETEST = new LaunchType("server_gametest", "net.minecraftforge.forge", "net.minecraftforge.gametest.GameTestMain", Dist.DEDICATED_SERVER, false);
protected static final LaunchType SERVER_GAMETEST = new LaunchType("server_gametest", "minecraft", "net.minecraft.gametest.Main", Dist.DEDICATED_SERVER, false);
protected void runTarget(String module, String target, final String[] arguments, final ModuleLayer layer) throws Throwable {
var mod = layer.findModule(module).orElse(null);
@@ -96,14 +96,14 @@ public final class ForgeDevLocator extends AbstractModProvider implements IModLo
// Find resource directories for every mod in this group
var paths = new LinkedHashSet<Path>();
for (var modid : modids) {
for (var modid : modids.stream().sorted().toList()) {
var rsc = path.resolve(modid);
if (Files.exists(rsc))
paths.add(rsc);
}
var root = memory.getPath(modids.iterator().next()); // use the first modid as our root
buildModsToml(paths, modids, root);
buildModsToml(paths, modids, root, pkg.startsWith("net/minecraftforge/debug/client/"));
buildPackMeta(paths, root);
moveModuleInfo(paths, root);
@@ -187,7 +187,7 @@ public final class ForgeDevLocator extends AbstractModProvider implements IModLo
}
// Builds or update the mods.toml file for all @Mods in this package
private static void buildModsToml(Set<Path> resources, Set<String> modids, Path root) {
private static void buildModsToml(Set<Path> resources, Set<String> modids, Path root, boolean clientOnly) {
var toml = resources.stream()
.map(p -> p.resolve(MODS_TOML))
.filter(Files::exists)
@@ -205,7 +205,8 @@ public final class ForgeDevLocator extends AbstractModProvider implements IModLo
var defaults = Map.of(
"modLoader", "javafml",
"loaderVersion", "[0,)",
"license", "Doesnt fucking matter"
"license", "Doesnt fucking matter",
"clientSideOnly", clientOnly
);
for (var key : defaults.keySet()) {
+3 -3
View File
@@ -5,11 +5,11 @@ org.gradle.daemon=false
org.gradle.parallel=true
JAVA_VERSION=21
MC_VERSION=1.21.7
MC_VERSION=1.21.8
MC_NEXT_VERSION=1.22
MCP_VERSION=20250630.104312
MCP_VERSION=20250717.105350
MAPPING_CHANNEL=official
MAPPING_VERSION=1.21.7
MAPPING_VERSION=1.21.8
# Set to true before the first build of a new MC version, so we don't do compatibility checks
CHECK_COMPATIBILITY=false
@@ -8,8 +8,6 @@ package net.minecraftforge.fml.mclanguageprovider;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.forgespi.language.IModInfo;
import java.util.Objects;
public class MinecraftModContainer extends ModContainer {
private static final String MCMODINSTANCE = "minecraft, the mod";
@@ -20,7 +18,7 @@ public class MinecraftModContainer extends ModContainer {
@Override
public boolean matches(final Object mod) {
return Objects.equals(mod, MCMODINSTANCE);
return MCMODINSTANCE.equals(mod);
}
@Override
@@ -10,7 +10,6 @@ import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraftforge.forgespi.language.IModLanguageProvider;
import net.minecraftforge.forgespi.language.ModFileScanData;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
@@ -20,7 +19,7 @@ import java.util.function.Supplier;
import static net.minecraftforge.fml.Logging.LOADING;
public class MinecraftModLanguageProvider implements IModLanguageProvider {
private static final Logger LOGGER = LogManager.getLogger();
@Override
public String name() {
return "minecraft";
@@ -45,7 +44,7 @@ public class MinecraftModLanguageProvider implements IModLanguageProvider {
final Class<?> mcModClass = Class.forName(getClass().getModule(), "net.minecraftforge.fml.mclanguageprovider.MinecraftModContainer");
return (T)mcModClass.getConstructor(IModInfo.class).newInstance(info);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
LOGGER.fatal(LOADING,"Unable to load MinecraftModContainer, wut?", e);
LogManager.getLogger().fatal(LOADING, "Unable to load MinecraftModContainer, wut?", e);
throw new RuntimeException(e);
}
}
@@ -1,6 +1,6 @@
--- a/com/mojang/blaze3d/vertex/VertexFormat.java
+++ b/com/mojang/blaze3d/vertex/VertexFormat.java
@@ -20,7 +_,7 @@
@@ -21,7 +_,7 @@
@OnlyIn(Dist.CLIENT)
@DontObfuscate
@@ -9,7 +9,7 @@
public static final int UNKNOWN_ELEMENT = -1;
private static final boolean USE_STAGING_BUFFER_WORKAROUND = Util.getPlatform() == Util.OS.WINDOWS && Util.isAarch64();
@Nullable
@@ -34,6 +_,7 @@
@@ -35,6 +_,7 @@
private GpuBuffer immediateDrawVertexBuffer;
@Nullable
private GpuBuffer immediateDrawIndexBuffer;
@@ -17,7 +17,7 @@
VertexFormat(List<VertexFormatElement> p_343616_, List<String> p_345241_, IntList p_345522_, int p_344162_) {
this.elements = p_343616_;
@@ -46,6 +_,11 @@
@@ -47,6 +_,11 @@
int j = vertexformatelement != null ? p_343616_.indexOf(vertexformatelement) : -1;
this.offsetsByElement[i] = j != -1 ? p_345522_.getInt(j) : -1;
}
@@ -29,7 +29,7 @@
}
public static VertexFormat.Builder builder() {
@@ -162,6 +_,9 @@
@@ -169,6 +_,9 @@
);
return this.immediateDrawIndexBuffer;
}
@@ -9,6 +9,25 @@
private final SubtitleOverlay subtitleOverlay;
private final SpectatorGui spectatorGui;
private final PlayerTabOverlay tabList;
@@ -194,6 +_,7 @@
() -> new JumpableVehicleBarRenderer(p_330021_)
);
this.resetTitleTimes();
+ net.minecraftforge.client.gui.overlay.ForgeLayeredDraw.init(this, p_330021_);
}
public void resetTitleTimes() {
@@ -204,6 +_,10 @@
public void render(GuiGraphics p_282884_, DeltaTracker p_342095_) {
if (this.minecraft.screen == null || !(this.minecraft.screen instanceof ReceivingLevelScreen)) {
+ if (Boolean.valueOf(true)) {
+ net.minecraftforge.client.gui.overlay.ForgeLayeredDraw.beginRender(p_282884_, p_342095_);
+ return;
+ }
if (!this.minecraft.options.hideGui) {
this.renderCameraOverlays(p_282884_, p_342095_);
this.renderCrosshair(p_282884_, p_342095_);
@@ -377,7 +_,7 @@
int i = Mth.floor(this.minecraft.mouseHandler.getScaledXPos(window));
int j = Mth.floor(this.minecraft.mouseHandler.getScaledYPos(window));
@@ -38,7 +57,7 @@
@@ -598,6 +_,10 @@
}
private void renderSelectedItemName(GuiGraphics p_283501_) {
public void renderSelectedItemName(GuiGraphics p_283501_) {
+ renderSelectedItemName(p_283501_, 0);
+ }
+
@@ -71,7 +90,7 @@
}
}
@@ -1148,7 +_,7 @@
@@ -1146,7 +_,7 @@
ItemStack itemstack = this.minecraft.player.getInventory().getSelectedItem();
if (itemstack.isEmpty()) {
this.toolHighlightTimer = 0;
@@ -9,6 +9,14 @@
@Nullable
private ClientLevel level;
private final SectionOcclusionGraph sectionOcclusionGraph = new SectionOcclusionGraph();
@@ -161,6 +_,7 @@
this.entityRenderDispatcher = p_234246_;
this.blockEntityRenderDispatcher = p_234247_;
this.renderBuffers = p_234248_;
+ net.minecraftforge.client.event.ForgeEventFactoryClient.onInitLevelRenderer();
}
public void tickParticles(Camera p_369538_) {
@@ -480,7 +_,7 @@
postchain1.addToFrame(framegraphbuilder, i, j, this.targets);
}
@@ -18,6 +26,14 @@
CloudStatus cloudstatus = this.minecraft.options.getCloudsType();
if (cloudstatus != CloudStatus.OFF) {
Optional<Integer> optional = this.level.dimensionType().cloudHeight();
@@ -497,6 +_,7 @@
}
this.addLateDebugPass(framegraphbuilder, vec3, p_407881_);
+ net.minecraftforge.client.FramePassManager.insertForgePasses(framegraphbuilder, targets); // Forge: Modded passes are inserted here.
profilerfiller.popPush("framegraph");
framegraphbuilder.execute(p_367325_, new FrameGraphBuilder.Inspector() {
@Override
@@ -577,7 +_,7 @@
multibuffersource$buffersource.endLastBatch();
this.checkPoseStack(posestack);
@@ -0,0 +1,11 @@
--- a/net/minecraft/commands/SharedSuggestionProvider.java
+++ b/net/minecraft/commands/SharedSuggestionProvider.java
@@ -98,7 +_,7 @@
p_82948_.accept(t);
}
} else if (matchesSubStr(p_82946_, resourcelocation.getNamespace())
- || resourcelocation.getNamespace().equals("minecraft") && matchesSubStr(p_82946_, resourcelocation.getPath())) {
+ || matchesSubStr(p_82946_, resourcelocation.getPath())) { // Forge: Removed "minecraft" namespace requirement for path searching to allow modded path suggestions to show up
p_82948_.accept(t);
}
}
@@ -0,0 +1,11 @@
--- a/net/minecraft/gametest/Main.java
+++ b/net/minecraft/gametest/Main.java
@@ -7,6 +_,8 @@
public class Main {
@DontObfuscate
public static void main(String[] p_393417_) throws Exception {
+ System.setProperty("forge.enableGameTest", "true");
+ System.setProperty("forge.gameTestServer", "true");
SharedConstants.tryDetectVersion();
GameTestMainUtil.runGameTestServer(p_393417_, p_393535_ -> {});
}
@@ -0,0 +1,10 @@
--- a/net/minecraft/gametest/framework/GameTestMainUtil.java
+++ b/net/minecraft/gametest/framework/GameTestMainUtil.java
@@ -69,6 +_,7 @@
Bootstrap.bootStrap();
Util.startTimerHackThread();
+ net.minecraftforge.server.loading.ServerModLoader.load();
String s = optionset.valueOf(universe);
createOrResetDir(s);
p_393652_.accept(s);
@@ -28,7 +28,7 @@
private Vec3 calculateHaltTrackSpeed(Vec3 p_364212_, BlockState p_362846_) {
- if (p_362846_.is(Blocks.POWERED_RAIL) && !p_362846_.getValue(PoweredRailBlock.POWERED)) {
+ if (p_362846_.getBlock() instanceof PoweredRailBlock && !p_362846_.getValue(PoweredRailBlock.POWERED)) {
+ if (p_362846_.getBlock() instanceof PoweredRailBlock powered && !powered.isActivatorRail() && !p_362846_.getValue(PoweredRailBlock.POWERED)) {
return p_364212_.length() < 0.03 ? Vec3.ZERO : p_364212_.scale(0.5);
} else {
return p_364212_;
@@ -37,7 +37,7 @@
private Vec3 calculateBoostTrackSpeed(Vec3 p_366761_, BlockPos p_361866_, BlockState p_368734_) {
- if (p_368734_.is(Blocks.POWERED_RAIL) && p_368734_.getValue(PoweredRailBlock.POWERED)) {
+ if (p_368734_.getBlock() instanceof PoweredRailBlock && p_368734_.getValue(PoweredRailBlock.POWERED)) {
+ if (p_368734_.getBlock() instanceof PoweredRailBlock powered && !powered.isActivatorRail() && p_368734_.getValue(PoweredRailBlock.POWERED)) {
if (p_366761_.length() > 0.01) {
return p_366761_.normalize().scale(p_366761_.length() + 0.06);
} else {
@@ -18,7 +18,7 @@
boolean flag1 = false;
- if (blockstate.is(Blocks.POWERED_RAIL)) {
+ BaseRailBlock baserailblock = (BaseRailBlock)blockstate.getBlock();
+ if (blockstate.getBlock() instanceof PoweredRailBlock) {
+ if (blockstate.getBlock() instanceof PoweredRailBlock powered && !powered.isActivatorRail()) {
flag = blockstate.getValue(PoweredRailBlock.POWERED);
flag1 = !flag;
}
@@ -39,7 +39,7 @@
protected boolean isSameRailWithPower(Level p_55226_, BlockPos p_55227_, boolean p_55228_, int p_55229_, RailShape p_55230_) {
BlockState blockstate = p_55226_.getBlockState(p_55227_);
- if (!blockstate.is(this)) {
+ if (!(blockstate.getBlock() instanceof PoweredRailBlock other)) {
+ if (!(blockstate.getBlock() instanceof PoweredRailBlock other) || this.isActivatorRail() != other.isActivatorRail()) {
return false;
} else {
- RailShape railshape = blockstate.getValue(SHAPE);
+10 -10
View File
@@ -10,7 +10,7 @@ buildscript {
dependencies {
classpath('com.google.code.gson:gson') {
version {
strictly '2.10.1'
strictly '2.11.0'
}
}
}
@@ -32,8 +32,8 @@ dependencyResolutionManagement {
library('accesstransformers', 'net.minecraftforge:accesstransformers:8.2.2')
library('coremods', 'net.minecraftforge:coremods:5.2.6')
library('nashorn', 'org.openjdk.nashorn:nashorn-core:15.4') // Needed by coremods, because the JRE no longer ships JS
library('eventbus', 'net.minecraftforge:eventbus:7.0-beta.10')
library('eventbus-validator', 'net.minecraftforge:eventbus-validator:7.0-beta.10')
library('eventbus', 'net.minecraftforge:eventbus:7.0-beta.11')
library('eventbus-validator', 'net.minecraftforge:eventbus-validator:7.0-beta.11')
library('mergetool-api', 'net.minecraftforge:mergetool-api:1.0')
library('mixin', 'org.spongepowered:mixin:0.8.7')
@@ -84,19 +84,19 @@ dependencyResolutionManagement {
// These are libraries shipped by the MC launcher, try and keep them in sync with the manifest
// but honestly if we don't it just means we ship them as normal libraries by adding them to the installer config in the forge project
library('gson', 'com.google.code.gson:gson:2.10.1')
library('guava', 'com.google.guava:guava:32.1.2-jre')
library('failureaccess', 'com.google.guava:failureaccess:1.0.1')
library('gson', 'com.google.code.gson:gson:2.11.0')
library('guava', 'com.google.guava:guava:33.3.1-jre')
library('failureaccess', 'com.google.guava:failureaccess:1.0.2')
version('log4j', '2.22.1')
version('log4j', '2.24.3')
library('log4j-api', 'org.apache.logging.log4j', 'log4j-api' ).versionRef('log4j')
library('log4j-core', 'org.apache.logging.log4j', 'log4j-core').versionRef('log4j')
bundle('log4j', ['log4j-api', 'log4j-core'])
library('apache-commons', 'org.apache.commons:commons-lang3:3.14.0')
library('mojang-logging', 'com.mojang:logging:1.2.7')
library('apache-commons', 'org.apache.commons:commons-lang3:3.17.0')
library('mojang-logging', 'com.mojang:logging:1.5.10')
library('jopt-simple', 'net.sf.jopt-simple:jopt-simple:5.0.4')
library('commons-io', 'commons-io:commons-io:2.15.1')
library('commons-io', 'commons-io:commons-io:2.17.0')
version('lwjgl', '3.3.3')
library('lwjgl', 'org.lwjgl', 'lwjgl' ).versionRef('lwjgl')
@@ -884,4 +884,8 @@ public class ForgeHooksClient {
return parent.compute(p_395456_);
}
}
public static void addFramePass(ResourceLocation rl, FramePassManager.PassDefinition definition) {
FramePassManager.addPass(rl, definition);
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.client;
import com.mojang.blaze3d.framegraph.FrameGraphBuilder;
import com.mojang.blaze3d.framegraph.FramePass;
import net.minecraft.client.renderer.LevelTargetBundle;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.ApiStatus;
import java.util.ArrayList;
import java.util.List;
public class FramePassManager {
private static final List<PassInfo> addedPasses = new ArrayList<>();
protected static void addPass(ResourceLocation name, PassDefinition pass) {
if (addedPasses.stream().noneMatch(info -> info.name.equals(name.toString()))) {
addedPasses.add(new PassInfo(name.toString(), pass));
} else {
throw new IllegalArgumentException("Cannot create a frame pass with a duplicate name: " + name);
}
}
// Note: Pass order is determined automatically within FrameGraphBuilder. It's unclear what must be done to guarantee ordering.
@ApiStatus.Internal
public static void insertForgePasses(FrameGraphBuilder graphBuilder, LevelTargetBundle bundle) {
for (PassInfo info : addedPasses) {
FramePass pass = graphBuilder.addPass(info.name);
PassDefinition forgePass = info.pass;
forgePass.targets(bundle, pass);
pass.executes(forgePass::executes);
}
}
public interface PassDefinition {
void targets(LevelTargetBundle bundle, FramePass pass);
void executes();
}
private record PassInfo(String name, PassDefinition pass){}
}
@@ -0,0 +1,34 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.client.event;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.FramePassManager;
import net.minecraftforge.eventbus.api.bus.EventBus;
import net.minecraftforge.eventbus.api.event.RecordEvent;
import org.jspecify.annotations.NullMarked;
/**
* Fired after all vanilla frame passes are added into the pass list.
*
* <p>This event is fired on the {@linkplain net.minecraftforge.common.MinecraftForge#EVENT_BUS main Forge event bus},
* only on the {@linkplain net.minecraftforge.fml.LogicalSide#CLIENT logical client}.
*/
@NullMarked
public record AddFramePassEvent() implements RecordEvent {
public static final EventBus<AddFramePassEvent> BUS = EventBus.create(AddFramePassEvent.class);
/**
* Adds a frame pass to pass list.
* Create a new {@linkplain FramePassManager.PassDefinition} to handle render targets and render code.
*
* @param rl Resource location for frame pass name. Use RLs to avoid duplicate names.
* @param definition see usages of {@linkplain com.mojang.blaze3d.framegraph.FramePass} in {@linkplain net.minecraft.client.renderer.LevelRenderer}
* @throws IllegalArgumentException If the name is a duplicate.
*/
public void addPass(ResourceLocation rl, FramePassManager.PassDefinition definition) {
ForgeHooksClient.addFramePass(rl, definition);
}
}
@@ -0,0 +1,34 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.client.event;
import net.minecraftforge.client.gui.overlay.ForgeLayeredDraw;
import net.minecraftforge.eventbus.api.bus.BusGroup;
import net.minecraftforge.eventbus.api.bus.EventBus;
import net.minecraftforge.eventbus.api.event.RecordEvent;
import net.minecraftforge.eventbus.api.event.characteristic.SelfDestructing;
import net.minecraftforge.fml.event.IModBusEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Fired when the {@linkplain ForgeLayeredDraw#VANILLA_ROOT}'s order is resolved during{@link ForgeLayeredDraw#resolveLayers().
* This can be used to add additional or move gui layers and entire layer stacks as needed.
*
* <p> This event is fired on the {@linkplain IModBusEvent mod event bus},
* only on the {@linkplain net.minecraftforge.fml.LogicalSide logical client}.</p>
*
* @param getLayedDraw The provided {@linkplain ForgeLayeredDraw#instance}. By default will be {@linkplain ForgeLayeredDraw#VANILLA_ROOT}.
*/
@NullMarked
public record AddGuiOverlayLayersEvent(ForgeLayeredDraw getLayeredDraw) implements IModBusEvent, SelfDestructing, RecordEvent {
@ApiStatus.Internal
public AddGuiOverlayLayersEvent {}
public static EventBus<AddGuiOverlayLayersEvent> getBus(BusGroup modBusGroup) {
return IModBusEvent.getBus(modBusGroup, AddGuiOverlayLayersEvent.class);
}
}
@@ -10,6 +10,8 @@ import java.util.List;
import java.util.Map;
import java.util.function.Function;
import net.minecraftforge.client.FramePassManager;
import net.minecraftforge.client.gui.overlay.ForgeLayeredDraw;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.util.Result;
import net.minecraftforge.event.TickEvent;
@@ -283,4 +285,12 @@ public final class ForgeEventFactoryClient {
// This is on the mod bus because it happens during the initial texture reload, which is while the Forge bus is shut down.
return fireModBus(new ModelEvent.RegisterModelStateDefinitions());
}
public static void onInitLevelRenderer() {
AddFramePassEvent.BUS.post(new AddFramePassEvent());
}
public static void onComputeLayerOrder(ForgeLayeredDraw layeredDraw) {
fireModBus(new AddGuiOverlayLayersEvent(layeredDraw));
}
}
@@ -78,7 +78,7 @@ public class LoadingErrorScreen extends ErrorScreen {
private void drawMultiLineCenteredString(GuiGraphics guiGraphics, Font fr, Component str, int x, int y) {
for (FormattedCharSequence s : fr.split(str, this.width)) {
guiGraphics.drawString(fr, s, x - fr.width(s) / 2, y, 0xFFFFFF);
guiGraphics.drawString(fr, s, x - fr.width(s) / 2, y, 0xFFFFFFFF);
y += fr.lineHeight;
}
}
@@ -134,9 +134,9 @@ public class LoadingErrorScreen extends ErrorScreen {
int y = top + 2;
for (var string : strings) {
if (center)
guiGraphics.drawString(font, string, left + (width / 2) - (font.width(string) / 2), y, 0xFFFFFF, false);
guiGraphics.drawString(font, string, left + (width / 2) - (font.width(string) / 2), y, 0xFFFFFFFF, false);
else
guiGraphics.drawString(font, string, left + 5, y, 0xFFFFFF, false);
guiGraphics.drawString(font, string, left + 5, y, 0xFFFFFFFF, false);
y += font.lineHeight;
}
}
@@ -52,6 +52,7 @@ import net.minecraftforge.fml.loading.StringUtils;
import net.minecraftforge.forgespi.language.IModInfo;
import net.minecraft.locale.Language;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import org.slf4j.Logger;
@@ -499,4 +500,12 @@ public class ModListScreen extends Screen {
public void onClose() {
this.minecraft.setScreen(this.parentScreen);
}
@Override
protected void handleClickEvent(Minecraft mc, ClickEvent event) {
if (mc.player == null)
defaultHandleClickEvent(event, mc, this);
else
defaultHandleGameClickEvent(event, mc, this);
}
}
@@ -0,0 +1,8 @@
package net.minecraftforge.client.gui.overlay;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.gui.GuiGraphics;
public interface ForgeLayer {
void render(GuiGraphics gg, DeltaTracker dt);
}
@@ -0,0 +1,410 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.client.gui.overlay;
import com.mojang.logging.LogUtils;
import net.minecraft.FieldsAreNonnullByDefault;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.client.event.ForgeEventFactoryClient;
import net.minecraftforge.client.event.AddGuiOverlayLayersEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.*;
import java.util.function.BooleanSupplier;
/**
* As vanilla has switched to a layered drawing system for overlays, this system replaces ForgeGui and its associated headaches.
* Vanilla will now have resource locations to represent its render layers which modders can order against.
* This class is effectively a pseudo-registry for Layers. Add what you need during {@linkplain AddGuiOverlayLayersEvent}
* Layers must be uniquely named per ForgeLayeredDraw, but may be duplicated across different instances.
* Any change which would result in a duplicate will not be applied.
* All methods which return a {@linkplain ForgeLayeredDraw} will return its caller's instance.
* To select a specific instance, use {@linkplain ForgeLayeredDraw#locateStack(ResourceLocation)} before adding
* or use the methods that include a stack identifier.
*/
@FieldsAreNonnullByDefault
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public final class ForgeLayeredDraw implements ForgeLayer {
private final Map<ResourceLocation, ForgeLayer> namedLayers = new HashMap<>();
private final Map<ResourceLocation, Map.Entry<ForgeLayeredDraw, BooleanSupplier>> subLayerStacks = new HashMap<>();
private final List<ResourceLocation> order = new LinkedList<>();
private final List<ForgeLayer> bakedLayers = new ArrayList<>();
private final ResourceLocation name;
public static final ResourceLocation PRE_SLEEP_STACK = ResourceLocation.withDefaultNamespace("pre_sleep_phase");
public static final ResourceLocation CAMERA_OVERLAY = ResourceLocation.withDefaultNamespace("camera_overlay");
public static final ResourceLocation CROSSHAIR = ResourceLocation.withDefaultNamespace("crosshair");
public static final ResourceLocation CHANGE_STRATUM = ResourceLocation.withDefaultNamespace("stratum_change");
public static final ResourceLocation HOTBAR_AND_DECOS = ResourceLocation.withDefaultNamespace("hotbar");
public static final ResourceLocation POTION_EFFECTS = ResourceLocation.withDefaultNamespace("potion_effects");
public static final ResourceLocation BOSS_OVERLAY = ResourceLocation.withDefaultNamespace("boss_overlay");
public static final ResourceLocation POST_SLEEP_STACK = ResourceLocation.withDefaultNamespace("post_sleep_phase");
public static final ResourceLocation DEMO_OVERLAY = ResourceLocation.withDefaultNamespace("demo");
public static final ResourceLocation DEBUG_OVERLAY = ResourceLocation.withDefaultNamespace("debug");
public static final ResourceLocation SCOREBOARD = ResourceLocation.withDefaultNamespace("scoreboard");
public static final ResourceLocation HOTBAR_MESSAGE = ResourceLocation.withDefaultNamespace("hotbar_message");
public static final ResourceLocation TITLE_OVERLAY = ResourceLocation.withDefaultNamespace("title");
public static final ResourceLocation CHAT_OVERLAY = ResourceLocation.withDefaultNamespace("chat_overlay");
public static final ResourceLocation TAB_LIST = ResourceLocation.withDefaultNamespace("tab_list");
public static final ResourceLocation SUBTITLE_OVERLAY = ResourceLocation.withDefaultNamespace("subtitle");
public static final ResourceLocation VANILLA_ROOT = ResourceLocation.withDefaultNamespace("vanilla_root");
public static final ResourceLocation SLEEP_OVERLAY = ResourceLocation.withDefaultNamespace("sleep_overlay");
private static final ForgeLayeredDraw instance = new ForgeLayeredDraw(VANILLA_ROOT);
/**
* Creates an empty draw list. Add entries with {@linkplain ForgeLayeredDraw#add(ResourceLocation, ForgeLayer)}
* @param name marker for which phase this is.
*/
public ForgeLayeredDraw(ResourceLocation name) {
this.name = name;
}
/**
* Adds a full draw stack with its provided condition.
* Use {@linkplain ForgeLayeredDraw#putAbove} and {@linkplain ForgeLayeredDraw#putBelow} for fine location adjustment.
* @param name RL of the name to identify this stack with.
* @param layeredDraw the draw stack
* @param supplier condition for this stack to render
* @return this
*/
public ForgeLayeredDraw add(ResourceLocation name, ForgeLayeredDraw layeredDraw, BooleanSupplier supplier) {
if (isNameAvailable(name)) {
subLayerStacks.put(name, Map.entry(layeredDraw, supplier));
order.add(name);
} else {
nameTakenWarning(name);
}
return this;
}
/**
* Add a layer to the layer list. This layer will be at the end of the list, which means
* it will be rendered last (on top) of already added layers.
* @param name RL for other mods to order against.
* @param layer layer render code, see {@linkplain ForgeLayer} and example usages in {@linkplain Gui}
* @return this
*/
public ForgeLayeredDraw add(ResourceLocation targetStack, ResourceLocation name, ForgeLayer layer) {
locateStack(targetStack).ifPresentOrElse((stack) -> stack.add(name, layer), () -> stackNotPresentWarning(targetStack));
return this;
}
/**
* Helper method, assumes the intended draw stack instance is the caller.
* Use any of the non-deprecated add methods if you want a specific instance,
* or call {@linkplain ForgeLayeredDraw#locateStack(ResourceLocation)} to get a reference to an instance.
* @param name RL of layer to add
* @param layer layer render code, see {@linkplain ForgeLayer} and example usages in {@linkplain Gui}
* @return this
*/
public ForgeLayeredDraw add(ResourceLocation name, ForgeLayer layer) {
if (isNameAvailable(name)) {
namedLayers.put(name, layer);
order.add(name);
} else {
nameTakenWarning(name);
}
return this;
}
/**
* Use to specify where your custom draw stack should go. Can also be used to re-order layers.
* Both target and destination must be in the same draw stack.
* @param expectedStack Which ForgeLayeredDraw the target is expected to be in. If you already have a reference to it, you can use {@linkplain ForgeLayeredDraw#move(ResourceLocation, ResourceLocation, LayerOffset)}
* @param target layer name to move
* @param destination layer name to order against
* @return this
*/
public ForgeLayeredDraw putAbove(ResourceLocation expectedStack, ResourceLocation target, ResourceLocation destination) {
locateStack(expectedStack).ifPresentOrElse((stack) -> stack.move(target, destination, LayerOffset.ABOVE), () -> stackNotPresentWarning(expectedStack));
return this;
}
/**
* Use to specify where your custom draw stack should go. Can also be used to re-order layers.
* Both target and destination must be in the same draw stack.
* @param expectedStack Which ForgeLayeredDraw the target is expected to be in. If you already have a reference to it, you can use {@linkplain ForgeLayeredDraw#move(ResourceLocation, ResourceLocation, LayerOffset)}
* @param target layer name to move
* @param destination layer name to order against
* @return this
*/
public ForgeLayeredDraw putBelow(ResourceLocation expectedStack, ResourceLocation target, ResourceLocation destination) {
locateStack(expectedStack).ifPresentOrElse((stack) -> stack.move(target, destination, LayerOffset.BELOW), () -> stackNotPresentWarning(expectedStack));
return this;
}
/**
* Helper method, assumes the intended draw stack instance is the caller.
* Moves pre-existing names around within the order.
* @param target Layer being moved.
* @param destination Layer being ordered against
* @param offset Self-explanatory
* @return this
*/
public ForgeLayeredDraw move(ResourceLocation target, ResourceLocation destination, LayerOffset offset) {
if (!order.contains(target)) {
layerNotPresentWarning(target);
return this;
}
int loc = order.indexOf(destination);
if (loc == -1) {
layerNotPresentWarning(destination);
return this;
}
order.remove(target);
order.add(loc + (offset == LayerOffset.ABOVE ? 1 : 0), target);
return this;
}
/**
* Adds an overlay layer to be rendered above the other provided layer.
* To render "above" another layer means thisLayer will be rendered after otherLayer
* If the current stack does not contain otherLayer, no changes will be made.
* @param newLayer name of the layer to be added
* @param otherLayer name of the layer being ordered against
* @param layer layer render code, see {@linkplain ForgeLayer} and example usages in {@linkplain Gui}
* @return this
*/
public ForgeLayeredDraw addAbove(ResourceLocation expectedStack, ResourceLocation newLayer, ResourceLocation otherLayer, ForgeLayer layer) {
locateStack(expectedStack).ifPresentOrElse((stack) -> {
if (!stack.isNameAvailable(otherLayer)) {
stack.add(newLayer, layer).move(newLayer, otherLayer, LayerOffset.ABOVE);
} else {
layerNotPresentWarning(otherLayer);
}
}, () -> stackNotPresentWarning(expectedStack));
return this;
}
/**
* Helper method, assumes intended draw stack is the caller
*/
public ForgeLayeredDraw addAbove(ResourceLocation newLayer, ResourceLocation otherLayer, ForgeLayer layer) {
return addAbove(name, newLayer, otherLayer, layer);
}
/**
* Adds an overlay layer to be rendered below the other provided layer.
* To render "below" another layer means thisLayer will be rendered before otherLayer
* If the current stack does not contain otherLayer, no changes will be made.
* @param newLayer name of the layer to be added
* @param otherLayer name of the layer being ordered against
* @param layer layer render code, see {@linkplain ForgeLayer} and example usages in {@linkplain Gui}
* @return this
*/
public ForgeLayeredDraw addBelow(ResourceLocation expectedStack, ResourceLocation newLayer, ResourceLocation otherLayer, ForgeLayer layer) {
locateStack(expectedStack).ifPresentOrElse((stack) -> {
if (!stack.isNameAvailable(otherLayer)) {
stack.add(newLayer, layer).move(newLayer, otherLayer, LayerOffset.BELOW);
} else {
layerNotPresentWarning(otherLayer);
}
}, () -> stackNotPresentWarning(expectedStack));
return this;
}
/**
* Helper method, assumes intended draw stack is the caller
*/
public ForgeLayeredDraw addBelow(ResourceLocation newLayer, ResourceLocation otherLayer, ForgeLayer layer) {
return addBelow(name, newLayer, otherLayer, layer);
}
/**
* Add a new layer that will only be rendered when the condition is met. The layer will be added
* at the end of the list, which means it will render last (on top) of already added layers
* @param name name of the layer to be added
* @param layer render code of layer being added
* @param condition supplier for the condition
* @return this
*/
public ForgeLayeredDraw addWithCondition(ResourceLocation targetStack, ResourceLocation name, ForgeLayer layer, BooleanSupplier condition) {
locateStack(targetStack).ifPresentOrElse((stack) -> stack.addWithCondition(name, layer, condition), () -> stackNotPresentWarning(targetStack));
return this;
}
/**
* Assumes the correct stack is the caller of this method, otherwise functions the same as other method.
* @param name name of layer to be added
* @param layer render code of layer being added
* @param condition supplier for the condition
* @return this
*/
public ForgeLayeredDraw addWithCondition(ResourceLocation name, ForgeLayer layer, BooleanSupplier condition) {
add(name, layer).addConditionTo(name, condition);
return this;
}
/**
* Add a condition to a pre-existing layer in the specified stack, its render order is not changed.
* If the target is not present, no changes are made.
* @param targetStack name of the draw stack the target is expected to be in
* @param targetLayer name of layer to add a condition to
* @param condition supplier for the condition
* @return this
*/
public ForgeLayeredDraw addConditionTo(ResourceLocation targetStack, ResourceLocation targetLayer, BooleanSupplier condition) {
locateStack(targetStack).ifPresentOrElse((stack) -> stack.addConditionTo(targetLayer, condition), () -> stackNotPresentWarning(targetStack));
return this;
}
/**
* Assumes the correct stack is the caller of this method.
* Use {@linkplain ForgeLayeredDraw#addConditionTo(ResourceLocation, ResourceLocation, BooleanSupplier)}
* if you do not have a reference to the draw stack you want.
* @param targetLayer name of layer to add condition to
* @param condition supplier for the condition
* @return this
*/
public ForgeLayeredDraw addConditionTo(ResourceLocation targetLayer, BooleanSupplier condition) {
var result = namedLayers.computeIfPresent(targetLayer,
(name, layer) -> (guiGraphics, deltaTracker) -> {
if (condition.getAsBoolean()) {
layer.render(guiGraphics, deltaTracker);
}
});
if (result == null) {
layerNotPresentWarning(targetLayer);
}
return this;
}
/**
* Propagate the layer order down to the inner render list after providing modders an opportunity to alter the list as they wish.
* @apiNote Modders should <emph>NEVER</emph> be calling this method.
* @return this
*/
@ApiStatus.Internal
public ForgeLayeredDraw resolveLayers() {
if (!order.isEmpty()) {
ForgeEventFactoryClient.onComputeLayerOrder(this);
}
resolveNested();
order.clear();
return this;
}
/**
* Resolve the layer order per stack, recursively as needed.
* Parent layer stack {@linkplain ForgeLayeredDraw#VANILLA_ROOT} holds the results.
*/
private void resolveNested() {
for (ResourceLocation layerName : order) {
if (subLayerStacks.containsKey(layerName)) {
var entry = subLayerStacks.get(layerName);
entry.getKey().resolveNested();
bakedLayers.add((gg, tr) -> {
if (entry.getValue().getAsBoolean()) entry.getKey().render(gg,tr);
});
} else {
bakedLayers.add(namedLayers.get(layerName));
}
}
}
/**
* Attempt to locate a particular draw stack. Search starts at caller's instance. For global search call on VANILLA_ROOT
* Entries which don't extend ForgeLayeredDraw may not have the proper fields to support internal list adjustment, so they are skipped.
* @param targetStack Name of ForgeLayeredDraw to find
* @return Filled Optional if target exists, empty otherwise.
*/
public Optional<ForgeLayeredDraw> locateStack(ResourceLocation targetStack) {
if (!name.equals(targetStack)) {
for (Map.Entry<ForgeLayeredDraw, BooleanSupplier> value : subLayerStacks.values()) {
if (value.getKey() instanceof ForgeLayeredDraw searchable) {
var res = searchable.locateStack(targetStack);
if(res.isPresent()) return res;
}
}
return Optional.empty();
} else {
return Optional.of(this);
}
}
@Nullable
public ForgeLayeredDraw getChild(ResourceLocation childName) {
return locateStack(childName).orElse(null);
}
@Nullable
public ForgeLayer getLayer(ResourceLocation layerName) {
return namedLayers.get(layerName);
}
public ResourceLocation getName() {
return name;
}
private void stackNotPresentWarning(ResourceLocation stackName) {
LogUtils.getLogger().warn("Target stack {} was not present anywhere. Is your ResourceLocation correct?", stackName);
}
private void layerNotPresentWarning(ResourceLocation layer) {
LogUtils.getLogger().warn("Expected layer {} was not found in stack {}, no layer modifications have been made.", layer, name);
}
private void nameTakenWarning(ResourceLocation layer) {
LogUtils.getLogger().warn("Name {} was already present in {} and cannot be re-used.", layer, name);
}
private boolean isNameAvailable(ResourceLocation name) {
return !namedLayers.containsKey(name) && !subLayerStacks.containsKey(name);
}
public enum LayerOffset {
ABOVE,
BELOW
}
@ApiStatus.Internal
public static void beginRender(GuiGraphics gg, DeltaTracker dt) {
instance.render(gg,dt);
}
@ApiStatus.Internal
public void render(GuiGraphics gg, DeltaTracker dt) {
for (ForgeLayer bakedLayer : bakedLayers) {
bakedLayer.render(gg, dt);
}
}
@ApiStatus.Internal
public static void init(Gui gui, Minecraft minecraft) {
var preSleepDraw = new ForgeLayeredDraw(PRE_SLEEP_STACK)
.add(CAMERA_OVERLAY, gui::renderCameraOverlays)
.add(CROSSHAIR, gui::renderCrosshair)
.add(CHANGE_STRATUM, (gg, dt) -> gg.nextStratum())
.add(HOTBAR_AND_DECOS, gui::renderHotbarAndDecorations)
.add(POTION_EFFECTS, gui::renderEffects)
.add(BOSS_OVERLAY, gui::renderBossOverlay);
var postSleepDraw = new ForgeLayeredDraw(POST_SLEEP_STACK)
.add(DEMO_OVERLAY, gui::renderDemoOverlay)
.add(DEBUG_OVERLAY, gui::renderDebugOverlay)
.add(SCOREBOARD, gui::renderScoreboardSidebar)
.add(HOTBAR_MESSAGE, gui::renderOverlayMessage)
.add(TITLE_OVERLAY, gui::renderTitle)
.add(CHAT_OVERLAY, gui::renderChat)
.add(TAB_LIST, gui::renderTabList)
.add(SUBTITLE_OVERLAY, gui::renderSubtitleOverlay);
instance
.add(PRE_SLEEP_STACK, preSleepDraw, () -> !minecraft.options.hideGui)
.add(SLEEP_OVERLAY, gui::renderSleepOverlay)
.add(POST_SLEEP_STACK, postSleepDraw, () -> !minecraft.options.hideGui);
instance.resolveLayers();
}
}
@@ -19,7 +19,6 @@ import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.level.ChunkEvent;
import net.minecraftforge.event.network.ConnectionStartEvent;
import net.minecraftforge.event.server.ServerStoppingEvent;
import net.minecraftforge.event.TickEvent.ServerTickEvent;
import net.minecraftforge.eventbus.api.bus.BusGroup;
import net.minecraftforge.eventbus.api.listener.Priority;
import net.minecraftforge.eventbus.api.listener.SubscribeEvent;
@@ -59,16 +58,6 @@ public final class ForgeInternalHandler {
return false;
}
@SubscribeEvent
static void onServerTick(ServerTickEvent.Pre event) {
WorldWorkerManager.tick(true);
}
@SubscribeEvent
static void onServerTick(ServerTickEvent.Post event) {
WorldWorkerManager.tick(false);
}
// @SubscribeEvent
// static void checkSettings(ClientTickEvent event) {
// if (event.phase == Phase.END)
@@ -20,23 +20,25 @@ import net.minecraft.world.entity.npc.VillagerTrades.ItemListing;
import net.minecraft.core.NonNullList;
import net.minecraftforge.event.village.VillagerTradesEvent;
import net.minecraftforge.event.village.WandererTradesEvent;
import net.minecraftforge.common.util.Lazy;
import net.minecraftforge.event.server.ServerAboutToStartEvent;
import net.minecraftforge.registries.ForgeRegistries;
import org.apache.commons.lang3.tuple.Pair;
public class VillagerTradingManager {
private static final Map<ResourceKey<VillagerProfession>, Int2ObjectMap<ItemListing[]>> VANILLA_TRADES = new HashMap<>();
private static final List<Pair<ItemListing[], Integer>> WANDERER_TRADES = new ArrayList<>();
private static final Lazy<Map<ResourceKey<VillagerProfession>, Int2ObjectMap<ItemListing[]>>> VANILLA_TRADES = Lazy.of(VillagerTradingManager::copyVanillaTrades);
private static final Lazy<List<Pair<ItemListing[], Integer>>> WANDERER_TRADES = Lazy.of(() -> new ArrayList<Pair<ItemListing[], Integer>>(VillagerTrades.WANDERING_TRADER_TRADES));
static {
private static Map<ResourceKey<VillagerProfession>, Int2ObjectMap<ItemListing[]>> copyVanillaTrades() {
var ret = new HashMap<ResourceKey<VillagerProfession>, Int2ObjectMap<ItemListing[]>>();
VillagerTrades.TRADES.forEach((key, value) -> {
Int2ObjectMap<ItemListing[]> copy = new Int2ObjectOpenHashMap<>();
for (var ent : value.int2ObjectEntrySet()) {
copy.put(ent.getIntKey(), Arrays.copyOf(ent.getValue(), ent.getValue().length));
}
VANILLA_TRADES.put(key, copy);
ret.put(key, copy);
});
WANDERER_TRADES.addAll(VillagerTrades.WANDERING_TRADER_TRADES);
return ret;
}
static void loadTrades(ServerAboutToStartEvent e) {
@@ -46,7 +48,9 @@ public class VillagerTradingManager {
/** Posts the WandererTradesEvent. */
private static void postWandererEvent() {
var event = WandererTradesEvent.BUS.fire(new WandererTradesEvent(WANDERER_TRADES));
if (!WandererTradesEvent.BUS.hasListeners())
return;
var event = WandererTradesEvent.BUS.fire(new WandererTradesEvent(WANDERER_TRADES.get()));
VillagerTrades.WANDERING_TRADER_TRADES.clear();
for (var pool : event.getPools())
VillagerTrades.WANDERING_TRADER_TRADES.add(Pair.of(pool.getEntries().toArray(ItemListing[]::new), pool.getRolls()));
@@ -54,10 +58,13 @@ public class VillagerTradingManager {
/** Posts a VillagerTradesEvent for each registered profession. */
private static void postVillagerEvents() {
if (!VillagerTradesEvent.BUS.hasListeners())
return;
// TODO [VillagerType][1.21.5] Villager Professions are stored as keys in vanilla now? Re-evaluate this.
var vanilla = VANILLA_TRADES.get();
for (VillagerProfession value : ForgeRegistries.VILLAGER_PROFESSIONS) {
var prof = ForgeRegistries.VILLAGER_PROFESSIONS.getResourceKey(value).orElseThrow();
Int2ObjectMap<ItemListing[]> trades = VANILLA_TRADES.getOrDefault(prof, new Int2ObjectOpenHashMap<>());
Int2ObjectMap<ItemListing[]> trades = vanilla.getOrDefault(prof, new Int2ObjectOpenHashMap<>());
Int2ObjectMap<List<ItemListing>> mutableTrades = new Int2ObjectOpenHashMap<>();
for (int i = 1; i < 6; i++)
mutableTrades.put(i, NonNullList.create());
@@ -5,73 +5,82 @@
package net.minecraftforge.common;
import java.util.ArrayList;
import java.util.List;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.listener.EventListener;
public class WorldWorkerManager
{
private static final List<IWorker> workers = new ArrayList<>();
import java.util.ArrayList;
public class WorldWorkerManager {
private static final ArrayList<IWorker> workers = new ArrayList<>();
private static long startTime = -1;
private static int index = 0;
private static EventListener tickStartListener;
private static EventListener tickEndListener;
public static void tick(boolean start)
{
if (start)
{
startTime = System.currentTimeMillis();
return;
}
private static void startTick() {
startTime = System.currentTimeMillis();
}
private static void endTick() {
index = 0;
IWorker task = getNext();
if (task == null)
if (task == null) {
clear();
return;
}
long time = 50 - (System.currentTimeMillis() - startTime);
if (time < 10)
time = 10; //If ticks are lagging, give us at least 10ms to do something.
time += System.currentTimeMillis();
while (System.currentTimeMillis() < time && task != null)
{
while (System.currentTimeMillis() < time && task != null) {
boolean again = task.doWork();
if (!task.hasWork())
{
if (!task.hasWork()) {
remove(task);
task = getNext();
}
else if (!again)
{
} else if (!again) {
task = getNext();
}
}
}
public static synchronized void addWorker(IWorker worker)
{
workers.add(worker);
@Deprecated(forRemoval = true, since = "1.21.8")
public static void tick(boolean start) {
if (start) startTick();
else endTick();
}
private static synchronized IWorker getNext()
{
public static synchronized void addWorker(IWorker worker) {
workers.add(worker);
if (tickStartListener == null) {
tickStartListener = TickEvent.ServerTickEvent.Pre.BUS.addListener(event -> startTick());
tickEndListener = TickEvent.ServerTickEvent.Post.BUS.addListener(event -> endTick());
}
}
private static synchronized IWorker getNext() {
return workers.size() > index ? workers.get(index++) : null;
}
private static synchronized void remove(IWorker worker)
{
private static synchronized void remove(IWorker worker) {
workers.remove(worker);
index--;
}
//Internal only, used to clear everything when the server shuts down.
public static synchronized void clear()
{
public static synchronized void clear() {
workers.clear();
if (tickStartListener != null) {
TickEvent.ServerTickEvent.Pre.BUS.removeListener(tickStartListener);
TickEvent.ServerTickEvent.Post.BUS.removeListener(tickEndListener);
tickStartListener = null;
tickEndListener = null;
}
}
public static interface IWorker
{
public interface IWorker {
boolean hasWork();
/**
@@ -1,16 +0,0 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.gametest;
import net.minecraft.server.Main;
public class GameTestMain {
public static void main(String[] args) {
System.setProperty("forge.enableGameTest", "true");
System.setProperty("forge.gameTestServer", "true");
Main.main(args);
}
}
@@ -344,6 +344,7 @@ public net.minecraft.client.data.models.ModelProvider$ItemInfoCollector
public net.minecraft.client.data.models.ModelProvider$ItemInfoCollector <init>()V # constructor
public net.minecraft.client.data.models.ModelProvider$SimpleModelCollector
public net.minecraft.client.data.models.ModelProvider$SimpleModelCollector <init>()V # constructor
public net.minecraft.client.gui.Gui *() # render methods
public net.minecraft.client.gui.GuiGraphics$ScissorStack
public net.minecraft.client.gui.GuiGraphics$ScissorStack <init>()V # constructor
protected net.minecraft.client.gui.components.AbstractButton f_290895_ # SPRITES
Binary file not shown.
@@ -0,0 +1,7 @@
{
"type": "minecraft:function",
"environment": "minecraft:default",
"function": "forge:modify_overlay_test/add_condition",
"max_ticks": 100,
"structure": "forge:empty3x3x3"
}
@@ -0,0 +1,7 @@
{
"type": "minecraft:function",
"environment": "minecraft:default",
"function": "forge:modify_overlay_test/full_stack_condition",
"max_ticks": 100,
"structure": "forge:empty3x3x3"
}
@@ -0,0 +1,7 @@
{
"type": "minecraft:function",
"environment": "minecraft:default",
"function": "forge:modify_overlay_test/not_present_in_stack",
"max_ticks": 100,
"structure": "forge:empty3x3x3"
}
@@ -0,0 +1,7 @@
{
"type": "minecraft:function",
"environment": "minecraft:default",
"function": "forge:modify_overlay_test/ordered_layers",
"max_ticks": 100,
"structure": "forge:empty3x3x3"
}
@@ -0,0 +1,7 @@
{
"type": "minecraft:function",
"environment": "minecraft:default",
"function": "forge:render_frame_layer_test/frame_passes_run",
"max_ticks": 100,
"structure": "forge:empty3x3x3"
}
@@ -0,0 +1,171 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.debug.client;
import net.minecraft.gametest.framework.GameTestHelper;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.client.event.AddGuiOverlayLayersEvent;
import net.minecraftforge.client.gui.overlay.ForgeLayer;
import net.minecraftforge.client.gui.overlay.ForgeLayeredDraw;
import net.minecraftforge.common.extensions.IForgeGameTestHelper;
import net.minecraftforge.eventbus.api.listener.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.gametest.GameTest;
import net.minecraftforge.gametest.GameTestNamespace;
import net.minecraftforge.test.BaseTestMod;
import java.util.List;
import java.util.Map;
import java.util.function.BooleanSupplier;
import static net.minecraftforge.client.gui.overlay.ForgeLayeredDraw.*;
@GameTestNamespace("forge")
@Mod(ModifyOverlayTest.MODID)
public class ModifyOverlayTest extends BaseTestMod {
public static final String MODID = "modify_overlay_test";
private static final ResourceLocation myStackName = name("my_stack_name");
private static final ForgeLayeredDraw myLayerStack = new ForgeLayeredDraw(myStackName);
private static final ForgeLayer notAddedLayer = (gg, tr) -> {};
private static final ForgeLayer layerA = (gg,tr) -> {};
private static final ResourceLocation layerAName = name("layer_a");
private static final ForgeLayer layerB = (gg,tr) -> {};
private static final ResourceLocation layerBName = name("layer_b");
private static final ForgeLayer layerC = (gg,tr) -> {};
private static final ResourceLocation layerCName = name("layer_c");
private static final IForgeGameTestHelper.BoolFlag detectConditionFlag = new IForgeGameTestHelper.BoolFlag("det_cond_flag");
private static final IForgeGameTestHelper.BoolFlag detectConditionStackFlag = new IForgeGameTestHelper.BoolFlag("det_cond_stack_flag");
private static final IForgeGameTestHelper.BoolFlag enableConditionFlag = new IForgeGameTestHelper.BoolFlag("en_cond_flag");
private static final IForgeGameTestHelper.BoolFlag enableConditionStackFlag = new IForgeGameTestHelper.BoolFlag("en_cond_stack_flag");
private static ForgeLayeredDraw drawStack;
public ModifyOverlayTest(FMLJavaModLoadingContext context) {
super(context, false, false);
AddGuiOverlayLayersEvent.getBus(context.getModBusGroup()).addListener(this::overlayTestListener);
}
@GameTest
public static void not_present_in_stack(GameTestHelper helper) {
// Test that we can't order against non-existent layers.
List<ForgeLayer> internalLayersList = null;
try {
internalLayersList = getInternalLayersList(drawStack);
} catch (Exception e) {
helper.fail("Threw a " + e.getMessage() + " when trying to get the inner layer list.");
}
helper.assertFalse(internalLayersList.contains(notAddedLayer), "Found our layer when we shouldn't have. Not good!");
helper.succeed();
}
@GameTest
public static void add_condition(GameTestHelper helper) {
// Test that we can add conditions to pre-existing layers.
helper.assertFalse(detectConditionFlag.getBool(), "Conditional rendering ran when it shouldn't have.");
enableConditionFlag.set(true);
helper.runAfterDelay(5, () -> {
boolean result = detectConditionFlag.getBool();// Just in case of weird race conditions.
enableConditionFlag.set(false);
helper.assertTrue(result, "");
helper.succeed();
});
}
@GameTest
public static void ordered_layers(GameTestHelper helper) {
// Test that layers are in the correct order.
List<ForgeLayer> internalLayersList = null;
Map<ResourceLocation, ForgeLayer> check = null;
try {
Class<?> cls = drawStack.getClass();
var field = cls.getDeclaredField("subLayerStacks");
var field1 = cls.getDeclaredField("namedLayers");
field.setAccessible(true);
field1.setAccessible(true);
Map<ResourceLocation, Map.Entry<ForgeLayeredDraw, BooleanSupplier>> VROOT = ((Map<ResourceLocation, Map.Entry<ForgeLayeredDraw, BooleanSupplier>>) field.get(drawStack));
var PSS = ((ForgeLayeredDraw) VROOT.get(PRE_SLEEP_STACK).getKey());
check = ((Map<ResourceLocation, ForgeLayer>) field1.get(PSS));
internalLayersList = getInternalLayersList(PSS);
} catch (Exception e) {
helper.fail("Threw a " + e.getMessage() + " when trying to get the inner layer list.");
}
int locationOfPotionEffects = internalLayersList.indexOf(check.get(POTION_EFFECTS));
int locationOfLayerA = internalLayersList.indexOf(check.get(layerAName));
int locationOfLayerB = internalLayersList.indexOf(check.get(layerBName));
int locationOfLayerC = internalLayersList.indexOf(check.get(layerCName));
helper.assertTrue(locationOfPotionEffects == locationOfLayerA - 1, "Layer offset from vanilla -> A was incorrect");
helper.assertTrue(locationOfLayerB == locationOfLayerA + 1, "Layer offset from A -> B was incorrect");
helper.assertTrue(locationOfLayerC == locationOfLayerB + 1, "Layer offset from B -> C was incorrect");
helper.succeed();
}
@GameTest
public static void full_stack_condition(GameTestHelper helper) {
// Test conditional rendering & whether a full stack can be added in the first place.
helper.assertFalse(detectConditionStackFlag.getBool(), "Conditional rendering ran when it shouldn't have.");
enableConditionStackFlag.set(true);
helper.runAfterDelay(5, () -> {
boolean result = detectConditionStackFlag.getBool();// Just in case of weird race conditions.
enableConditionStackFlag.set(false);
helper.assertTrue(result, "");
helper.succeed();
});
}
@SubscribeEvent
private void overlayTestListener(AddGuiOverlayLayersEvent event) {
drawStack = event.getLayeredDraw();
var layeredDraw = event.getLayeredDraw();
layeredDraw.addAbove(VANILLA_ROOT, name("i_won_t_exist"), name("non_existent_target_layer"), notAddedLayer);
var pEffects = layeredDraw.locateStack(PRE_SLEEP_STACK).get();
// Test if layers may be ordered against other layers.
// Layers have to be present to be ordered against, of course, but we tested for that already above.
pEffects.addAbove(layerBName, POTION_EFFECTS, layerB);
pEffects.addAbove(layerCName, layerBName, layerC);
layeredDraw.addBelow(PRE_SLEEP_STACK, layerAName, layerBName, layerA);
layeredDraw.addConditionTo(PRE_SLEEP_STACK, BOSS_OVERLAY, () -> {
if (enableConditionFlag.getBool()) {
detectConditionFlag.set(true);
return true;
} else {
detectConditionFlag.set(false);
return false;
}
});
myLayerStack.add(name("my_inner_layer_name"), (gg, tr) -> {
detectConditionStackFlag.set(true);
});
// Demonstrates that entire stacks can have conditions attached
event.getLayeredDraw()
.add(myStackName, myLayerStack, () -> {
if (enableConditionStackFlag.getBool()) {
return true;
} else {
detectConditionStackFlag.set(false);
return false;
}
});
}
private static ResourceLocation name(String name) {
return ResourceLocation.fromNamespaceAndPath(MODID, name);
}
@SuppressWarnings("unchecked")
private static List<ForgeLayer> getInternalLayersList(ForgeLayeredDraw stack) throws ClassCastException, NoSuchFieldException, IllegalAccessException {
Class<?> cls = stack.getClass();
var layersField = cls.getDeclaredField("bakedLayers");
layersField.setAccessible(true);
return (List<ForgeLayer>) layersField.get(stack);
}
}
@@ -0,0 +1,94 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.debug.client;
import net.minecraft.client.renderer.LevelTargetBundle;
import net.minecraftforge.client.FramePassManager;
import net.minecraftforge.client.event.AddFramePassEvent;
import net.minecraftforge.common.extensions.IForgeGameTestHelper;
import net.minecraftforge.eventbus.api.listener.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.gametest.GameTest;
import net.minecraftforge.gametest.GameTestNamespace;
import net.minecraftforge.test.BaseTestMod;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.ShapeRenderer;
import net.minecraft.gametest.framework.GameTestHelper;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.framegraph.FramePass;
@GameTestNamespace("forge")
@Mod(RenderFrameLayerTest.MODID)
public class RenderFrameLayerTest extends BaseTestMod {
public static final String MODID = "render_frame_layer_test";
private static final IForgeGameTestHelper.BoolFlag passOne = new IForgeGameTestHelper.BoolFlag("pass_one_flag");
private static final IForgeGameTestHelper.BoolFlag passTwo = new IForgeGameTestHelper.BoolFlag("pass_two_flag");
public RenderFrameLayerTest(FMLJavaModLoadingContext context) {
super(context, false, false);
AddFramePassEvent.BUS.addListener(RenderFrameLayerTest::renderTest);
}
@GameTest
public static void frame_passes_run(GameTestHelper helper) {
passOne.set(false); passTwo.set(false); // pass#executes will reset to true if they run
helper.runAfterDelay(4, () -> {
helper.assertTrue(passOne.getBool(), "Pass One was not executed");
helper.assertTrue(passTwo.getBool(), "Pass Two was not executed");
helper.succeed();
});
}
/**
* If this is working, two white line box cubes will be rendered at ground level in a superflat world around (0,0)
*/
public static void renderTest(AddFramePassEvent event) {
var mainCamera = Minecraft.getInstance().gameRenderer.getMainCamera();
FramePassManager.PassDefinition def = new FramePassManager.PassDefinition() {
@Override
public void targets(LevelTargetBundle bundle, FramePass pass) {
bundle.main = pass.readsAndWrites(bundle.main);
}
@Override
public void executes() {
PoseStack ps = new PoseStack();
passOne.set(true);
ps.translate(mainCamera.getPosition().multiply(-1,-1,-1));
ps.pushPose();
var buffSource = Minecraft.getInstance().renderBuffers().bufferSource();
var vc = buffSource.getBuffer(RenderType.lines());
ShapeRenderer.renderLineBox(ps, vc, 0, -60, 0, 10, -50, 10, 1f, 1f, 1f, 1f);
buffSource.endBatch();
ps.popPose();
}
};
event.addPass(rl(MODID), def);
FramePassManager.PassDefinition def2 = new FramePassManager.PassDefinition() {
@Override
public void targets(LevelTargetBundle bundle, FramePass pass) {
bundle.main = pass.readsAndWrites(bundle.main);
}
@Override
public void executes() {
PoseStack ps = new PoseStack();
passTwo.set(true);
ps.translate(mainCamera.getPosition().multiply(-1,-1,-1));
ps.pushPose();
var buffSource = Minecraft.getInstance().renderBuffers().bufferSource();
var vc = buffSource.getBuffer(RenderType.lines());
ShapeRenderer.renderLineBox(ps, vc, 1, -61, 1, 9, -49, 9, 1f, 1f, 1f, 1f);
buffSource.endBatch();
ps.popPose();
}
};
event.addPass(rl(MODID+"_two"), def2);
}
}
@@ -1 +0,0 @@
clientSideOnly=true
@@ -1 +0,0 @@
clientSideOnly=true
@@ -1 +0,0 @@
clientSideOnly=true
@@ -1 +0,0 @@
clientSideOnly=true
@@ -1 +0,0 @@
clientSideOnly=true
@@ -1 +0,0 @@
clientSideOnly=true