mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-14 01:04:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c047cf136c | ||
|
|
f4287ecc81 | ||
|
|
364a1e737f | ||
|
|
45a977bb33 | ||
|
|
ab00c907e7 | ||
|
|
979abb2b93 | ||
|
|
056360b050 | ||
|
|
960fce505c | ||
|
|
ea597ff45a | ||
|
|
f35dce7eb3 | ||
|
|
ef224bd2b8 | ||
|
|
0749f49b6c | ||
|
|
5c508efaa9 | ||
|
|
dbafe039d8 | ||
|
|
b36ee22925 | ||
|
|
287ee7b0b5 | ||
|
|
5f67621477 | ||
|
|
f237e7a8c3 | ||
|
|
a54420de36 | ||
|
|
65dbdc8eb0 | ||
|
|
d42979f620 | ||
|
|
ac56afc99c | ||
|
|
81b7bfe4ea | ||
|
|
4cbcf64ce1 | ||
|
|
29b763a0c3 | ||
|
|
52f41a4448 | ||
|
|
597409e2f2 | ||
|
|
21fbd72585 | ||
|
|
83a09f7584 | ||
|
|
0e864fbe6e | ||
|
|
2fd8900c01 |
No files matched your search
+19
-3
@@ -3,6 +3,7 @@ package net.minecraftforge.forge.tasks
|
||||
import groovy.transform.CompileStatic
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
import org.objectweb.asm.tree.MethodNode
|
||||
|
||||
@@ -12,13 +13,19 @@ abstract class BytecodePredicateFinder extends BytecodeFinder {
|
||||
@Internal
|
||||
abstract Property<Closure<Boolean>> getPredicate()
|
||||
|
||||
private final Set<String> matches = new HashSet<>()
|
||||
private final Map<String, List<String>> matches = new TreeMap<>(Comparator.naturalOrder())
|
||||
|
||||
@Override
|
||||
protected process(ClassNode parent, MethodNode node) {
|
||||
for (final current : node.instructions) {
|
||||
if (predicate.get().call(parent, node, current)) {
|
||||
matches.add(parent.name)
|
||||
var methods = matches.compute(parent.name, { k, v -> v ?: new ArrayList<>() })
|
||||
|
||||
// only add non-synthetic methods
|
||||
if ((node.access & Opcodes.ACC_SYNTHETIC) == 0) {
|
||||
methods.add(node.name + node.desc)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -27,6 +34,15 @@ abstract class BytecodePredicateFinder extends BytecodeFinder {
|
||||
@Internal
|
||||
@Override
|
||||
protected Object getData() {
|
||||
return matches.toSorted(Comparator.naturalOrder()) ?: {throw new RuntimeException('Failed to find any targets, please ensure that method names and descriptors are correct.')}()
|
||||
var array = new ArrayList<Object>()
|
||||
matches.forEach { c, m ->
|
||||
{
|
||||
array.add(
|
||||
'class': c,
|
||||
'methods': m
|
||||
)
|
||||
}
|
||||
}
|
||||
return array ?: { throw new RuntimeException('Failed to find any targets, please ensure that method names and descriptors are correct.') }()
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -118,7 +118,7 @@ dependencies {
|
||||
bootstrap(libs.unsafe) // Needed by securemodules
|
||||
bootstrap(libs.bundles.asm) // Needed by securemodules
|
||||
|
||||
implementation(libs.jopt.simple) { version { strictly '5.0.4' } }
|
||||
implementation(libs.jopt.simple)
|
||||
|
||||
installer(libs.bootstrap)
|
||||
installer(libs.bootstrap.api) // Needed by securemodules
|
||||
@@ -381,13 +381,13 @@ tasks.register('extractInheritance', ExtractInheritance) {
|
||||
}
|
||||
}
|
||||
tasks.register("findFinalizeSpawnTargets", BytecodePredicateFinder) {
|
||||
jar = createJoinedSRG.output
|
||||
jar = createClientOfficial.output
|
||||
output = rootProject.file('src/main/resources/coremods/finalize_spawn_targets.json')
|
||||
predicate = {
|
||||
parent, node, insn ->
|
||||
return 'net/minecraft/world/level/BaseSpawner' != parent.name // Ignore this class as we special case it.
|
||||
&& insn.getOpcode().equals(Opcodes.INVOKEVIRTUAL)
|
||||
&& insn.name == 'm_6518_'
|
||||
&& insn.name == 'finalizeSpawn'
|
||||
&& insn.desc == '(Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/EntitySpawnReason;Lnet/minecraft/world/entity/SpawnGroupData;)Lnet/minecraft/world/entity/SpawnGroupData;';
|
||||
}
|
||||
}
|
||||
|
||||
+25
-6
@@ -2,16 +2,15 @@
|
||||
|
||||
MinecraftForge
|
||||
=============
|
||||
[][Download]
|
||||
[][Download]
|
||||
[][Download] [][Discord] [][Patreon]
|
||||
|
||||
Forge is a free, open-source modding API all of your favourite mods use!
|
||||
|
||||
| Version | Support |
|
||||
|---------| ------------- |
|
||||
| 1.20.x | Active |
|
||||
| 1.19.x | Legacy |
|
||||
| Version | Support |
|
||||
|--------------|-----------------------------|
|
||||
| All versions | [Supported][Support-Policy] |
|
||||
|
||||
* [Download]
|
||||
* [Forum]
|
||||
@@ -31,6 +30,19 @@ You can download the installer for the *Recommended Build* or the
|
||||
|
||||
For support and questions, visit [the Support Forum][Support-Forum] or [the Forge Discord server][Discord].
|
||||
|
||||
## Required Java Versions
|
||||
|
||||
We require the version of Java that Minecraft requires, which is currently:
|
||||
|
||||
| Minecraft | Java |
|
||||
|---------------|-------------------|
|
||||
| 1.20.6+ | [21][Adoptium-21] |
|
||||
| 1.18 - 1.20.4 | [17][Adoptium-17] |
|
||||
| 1.17.1 | [16][Adoptium-16] |
|
||||
| 1.16.5- | [8][Adoptium-8] |
|
||||
|
||||
**If you are on Windows and have difficulty using Java to launch JAR files, consider using [Jarfix by Johann Löfflmann][Jarfix].**
|
||||
|
||||
# Creating Mods
|
||||
|
||||
[See the "Getting Started" section in the Forge Documentation][Getting-Started].
|
||||
@@ -63,8 +75,15 @@ Please ensure you have a valid email address associated with your GitHub account
|
||||
|
||||
[Download]: https://files.minecraftforge.net/
|
||||
|
||||
[Adoptium-21]: https://adoptium.net/temurin/releases/?version=21
|
||||
[Adoptium-17]: https://adoptium.net/temurin/releases/?version=17
|
||||
[Adoptium-16]: https://adoptium.net/temurin/releases/?version=16
|
||||
[Adoptium-8]: https://adoptium.net/temurin/releases/?version=8
|
||||
[Jarfix]: https://johann.loefflmann.net/en/software/jarfix/index.html
|
||||
|
||||
[Forum]: https://forums.minecraftforge.net/
|
||||
[Support-Forum]: https://forums.minecraftforge.net/forum/18-support-bug-reports/
|
||||
[Support-Policy]: https://forums.minecraftforge.net/topic/144690-new-tiered-support-policy
|
||||
|
||||
[Discord]: https://discord.minecraftforge.net/
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.fml.loading.FMLConfig;
|
||||
import net.minecraftforge.fml.loading.FMLPaths;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -25,9 +26,66 @@ import static net.minecraftforge.fml.config.ConfigTracker.CONFIG;
|
||||
|
||||
public class ConfigFileTypeHandler {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
static final ConfigFileTypeHandler TOML = new ConfigFileTypeHandler();
|
||||
private static final Path defaultConfigPath = FMLPaths.GAMEDIR.get().resolve(FMLConfig.getConfigValue(FMLConfig.ConfigValue.DEFAULT_CONFIG_PATH));
|
||||
|
||||
private static final ConfigFileTypeHandler CLIENT = new ConfigFileTypeHandler(ModConfig.Type.CLIENT);
|
||||
private static final ConfigFileTypeHandler COMMON = new ConfigFileTypeHandler(ModConfig.Type.COMMON);
|
||||
private static final ConfigFileTypeHandler SERVER = new ConfigFileTypeHandler(ModConfig.Type.SERVER);
|
||||
|
||||
private final @Nullable ModConfig.Type type;
|
||||
private @Nullable FileWatcher watcher;
|
||||
|
||||
// exists for bin compat
|
||||
public ConfigFileTypeHandler() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
private ConfigFileTypeHandler(@Nullable ModConfig.Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the handler for the given {@link ModConfig.Type}.
|
||||
*
|
||||
* @param type The type to get the handler for
|
||||
* @return The handler
|
||||
*/
|
||||
static ConfigFileTypeHandler get(ModConfig.Type type) {
|
||||
return switch (type) {
|
||||
case CLIENT -> CLIENT;
|
||||
case COMMON -> COMMON;
|
||||
case SERVER -> SERVER;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link FileWatcher} for this handler, creating it if it doesn't exist and/or has been stopped.
|
||||
*
|
||||
* @return The watcher
|
||||
*
|
||||
* @apiNote This is package-private so modders can't just call {@link FileWatcher#stop()} and fuck up everything.
|
||||
*/
|
||||
FileWatcher getWatcher() {
|
||||
if (this.watcher == null) {
|
||||
LOGGER.debug(CONFIG, "Starting watcher for handler: {}", this);
|
||||
this.watcher = new FileWatcher();
|
||||
}
|
||||
|
||||
return this.watcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the {@link FileWatcher} for this handler, and sets it to null afterward. Use this instead of
|
||||
* {@link FileWatcher#stop()}.
|
||||
*/
|
||||
void stopWatcher() {
|
||||
if (this.watcher == null) return;
|
||||
|
||||
LOGGER.debug(CONFIG, "Stopping watcher for hander: {}", this);
|
||||
this.watcher.stop();
|
||||
this.watcher = null;
|
||||
}
|
||||
|
||||
public Function<ModConfig, CommentedFileConfig> reader(Path configBasePath) {
|
||||
return (c) -> {
|
||||
final Path configPath = configBasePath.resolve(c.getFileName());
|
||||
@@ -37,7 +95,7 @@ public class ConfigFileTypeHandler {
|
||||
onFileNotFound((newfile, configFormat)-> setupConfigFile(c, newfile, configFormat)).
|
||||
writingMode(WritingMode.REPLACE).
|
||||
build();
|
||||
LOGGER.debug(CONFIG, "Built TOML config for {}", configPath.toString());
|
||||
LOGGER.debug(CONFIG, "Built TOML config for {}", configPath);
|
||||
try
|
||||
{
|
||||
configData.load();
|
||||
@@ -46,9 +104,9 @@ public class ConfigFileTypeHandler {
|
||||
{
|
||||
throw new ConfigLoadingException(c, ex);
|
||||
}
|
||||
LOGGER.debug(CONFIG, "Loaded TOML config file {}", configPath.toString());
|
||||
FileWatcher.defaultInstance().addWatch(configPath, new ConfigWatcher(c, configData, Thread.currentThread().getContextClassLoader()));
|
||||
LOGGER.debug(CONFIG, "Watching TOML config file {} for changes", configPath.toString());
|
||||
LOGGER.debug(CONFIG, "Loaded TOML config file {}", configPath);
|
||||
this.getWatcher().addWatch(configPath, new ConfigWatcher(c, configData, Thread.currentThread().getContextClassLoader()));
|
||||
LOGGER.debug(CONFIG, "Watching TOML config file {} for changes", configPath);
|
||||
return configData;
|
||||
};
|
||||
}
|
||||
@@ -56,9 +114,9 @@ public class ConfigFileTypeHandler {
|
||||
public void unload(Path configBasePath, ModConfig config) {
|
||||
Path configPath = configBasePath.resolve(config.getFileName());
|
||||
try {
|
||||
FileWatcher.defaultInstance().removeWatch(configBasePath.resolve(config.getFileName()));
|
||||
this.getWatcher().removeWatch(configPath);
|
||||
} catch (RuntimeException e) {
|
||||
LOGGER.error("Failed to remove config {} from tracker!", configPath.toString(), e);
|
||||
LOGGER.error("Failed to remove config {} from tracker!", configPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +167,11 @@ public class ConfigFileTypeHandler {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ConfigFileTypeHandler[" + (type != null ? type : "UNKNOWN") + "]";
|
||||
}
|
||||
|
||||
private record ConfigWatcher(
|
||||
ModConfig modConfig,
|
||||
CommentedFileConfig commentedFileConfig,
|
||||
|
||||
@@ -8,6 +8,9 @@ package net.minecraftforge.fml.config;
|
||||
import com.electronwill.nightconfig.core.CommentedConfig;
|
||||
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.fml.DistExecutor;
|
||||
import net.minecraftforge.fml.loading.FMLPaths;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.Marker;
|
||||
import org.slf4j.MarkerFactory;
|
||||
@@ -52,11 +55,24 @@ public class ConfigTracker {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: [FML] This is only called for the server (outside of forceUnload)
|
||||
// rethink config implementation for eventual FML rewrite
|
||||
public void unloadConfigs(ModConfig.Type type, Path configBasePath) {
|
||||
LOGGER.debug(CONFIG, "Unloading configs type {}", type);
|
||||
for (ModConfig config : this.configSets.get(type)) {
|
||||
closeConfig(config, configBasePath);
|
||||
}
|
||||
ConfigFileTypeHandler.get(type).stopWatcher();
|
||||
}
|
||||
|
||||
// If there is a better way to do this, please tell me. Because this is FUCKED
|
||||
public void forceUnload() {
|
||||
// This is how ModStateProvider handles loading configs. So we're doing the same but with unloadConfigs instead
|
||||
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> this.unloadConfigs(ModConfig.Type.CLIENT, FMLPaths.CONFIGDIR.get()));
|
||||
this.unloadConfigs(ModConfig.Type.COMMON, FMLPaths.CONFIGDIR.get());
|
||||
|
||||
// just in case server watcher is still alive somehow...
|
||||
ConfigFileTypeHandler.get(ModConfig.Type.SERVER).stopWatcher();
|
||||
}
|
||||
|
||||
private static void openConfig(final ModConfig config, final Path configBasePath) {
|
||||
|
||||
@@ -30,7 +30,7 @@ public class ModConfig
|
||||
this.spec = spec;
|
||||
this.fileName = fileName;
|
||||
this.container = container;
|
||||
this.configHandler = ConfigFileTypeHandler.TOML;
|
||||
this.configHandler = ConfigFileTypeHandler.get(type);
|
||||
ConfigTracker.INSTANCE.trackConfig(this);
|
||||
}
|
||||
|
||||
|
||||
+4
-5
@@ -13,10 +13,10 @@ import java.lang.management.MemoryMXBean;
|
||||
import java.lang.management.MemoryUsage;
|
||||
|
||||
public class PerformanceInfo {
|
||||
private static final MemoryMXBean MEMORY_BEAN = ManagementFactory.getMemoryMXBean();
|
||||
|
||||
private final boolean showCPUUsage;
|
||||
private final OperatingSystemMXBean osBean;
|
||||
private final MemoryMXBean memoryBean;
|
||||
|
||||
private float memory;
|
||||
private String text;
|
||||
@@ -24,15 +24,14 @@ public class PerformanceInfo {
|
||||
PerformanceInfo() {
|
||||
showCPUUsage = FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_SHOW_CPU);
|
||||
osBean = showCPUUsage ? ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class) : null;
|
||||
memoryBean = ManagementFactory.getMemoryMXBean();
|
||||
}
|
||||
|
||||
void update() {
|
||||
final MemoryUsage heapusage = memoryBean.getHeapMemoryUsage();
|
||||
final MemoryUsage heapusage = MEMORY_BEAN.getHeapMemoryUsage();
|
||||
memory = (float) heapusage.getUsed() / heapusage.getMax();
|
||||
|
||||
if (!showCPUUsage) {
|
||||
text = "Heap: %d/%d MB (%.1f%%) OffHeap: %d MB".formatted(heapusage.getUsed() >> 20, heapusage.getMax() >> 20, memory * 100.0, memoryBean.getNonHeapMemoryUsage().getUsed() >> 20);
|
||||
text = "Memory: %d/%dMB (%.1f%%)".formatted(heapusage.getUsed() >>> 20, heapusage.getMax() >>> 20, memory * 100f);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -44,7 +43,7 @@ public class PerformanceInfo {
|
||||
cpuText = "CPU: %.1f%%".formatted(cpuLoad * 100f);
|
||||
}
|
||||
|
||||
text = "Heap: %d/%d MB (%.1f%%) OffHeap: %d MB %s".formatted(heapusage.getUsed() >> 20, heapusage.getMax() >> 20, memory * 100.0, memoryBean.getNonHeapMemoryUsage().getUsed() >> 20, cpuText);
|
||||
text = "Memory: %d/%dMB (%.1f%%) %s".formatted(heapusage.getUsed() >>> 20, heapusage.getMax() >>> 20, memory * 100f, cpuText);
|
||||
}
|
||||
|
||||
String text() {
|
||||
|
||||
+8
-1
@@ -50,7 +50,14 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
|
||||
@Override
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty) {
|
||||
return isEmpty ? NAY : YAY;
|
||||
if (isEmpty)
|
||||
return NAY;
|
||||
|
||||
String internalName = classType.getInternalName();
|
||||
if (internalName.startsWith("net/minecraft/") || internalName.startsWith("com/mojang/"))
|
||||
return NAY;
|
||||
|
||||
return YAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+24
-3
@@ -8,6 +8,7 @@ package net.minecraftforge.fml.common.asm;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -35,6 +36,9 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
|
||||
new VanillaObjectHolderData("net.minecraft.core.particles.ParticleTypes", "particle_type", "net.minecraft.core.particles.ParticleType"),
|
||||
new VanillaObjectHolderData("net.minecraft.sounds.SoundEvents", "sound_event", "net.minecraft.sounds.SoundEvent")
|
||||
).collect(Collectors.toUnmodifiableMap(VanillaObjectHolderData::holderClass, Function.identity()));
|
||||
private static final Set<String> VANILLA_OBJECT_HOLDER_CLASSES = VANILLA_OBJECT_HOLDERS.keySet().stream()
|
||||
.map(it -> it.replace('.', '/'))
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
private static final String OBJECT_HOLDER = "Lnet/minecraftforge/registries/ObjectHolder;"; //Don't directly reference this to prevent class loading.
|
||||
private static final int PUBLIC_STATIC_FINAL_FLAGS = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
|
||||
|
||||
@@ -47,9 +51,26 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
|
||||
private static final EnumSet<Phase> NAY = EnumSet.noneOf(Phase.class);
|
||||
|
||||
@Override
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty)
|
||||
{
|
||||
return isEmpty ? NAY : YAY;
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty) {
|
||||
if (isEmpty)
|
||||
return NAY;
|
||||
|
||||
// Let's skip processing classes that definitely don't have object holders
|
||||
String internalName = classType.getInternalName();
|
||||
|
||||
// Forge classes that aren't in the debug package (that package is used for tests)
|
||||
if (internalName.startsWith("net/minecraftforge/") && !internalName.substring(18).contains("debug"))
|
||||
return NAY;
|
||||
|
||||
// Vanilla classes that don't have object holders
|
||||
if (internalName.startsWith("com/mojang/"))
|
||||
return NAY;
|
||||
|
||||
// ...except specific ones we added to the VANILLA_OBJECT_HOLDERS map
|
||||
if (internalName.startsWith("net/minecraft/") && !VANILLA_OBJECT_HOLDER_CLASSES.contains(internalName))
|
||||
return NAY;
|
||||
|
||||
return YAY;
|
||||
}
|
||||
|
||||
private static boolean hasHolder(List<AnnotationNode> lst) {
|
||||
|
||||
@@ -48,9 +48,15 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
|
||||
private static final EnumSet<Phase> NAY = EnumSet.noneOf(Phase.class);
|
||||
|
||||
@Override
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty)
|
||||
{
|
||||
return isEmpty ? NAY : YAY;
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty) {
|
||||
if (isEmpty)
|
||||
return NAY;
|
||||
|
||||
String internalName = classType.getInternalName();
|
||||
if (internalName.startsWith("net/minecraftforge/") || internalName.startsWith("com/mojang/"))
|
||||
return NAY;
|
||||
|
||||
return YAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,15 +65,14 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
|
||||
if ((classNode.access & Opcodes.ACC_ENUM) == 0)
|
||||
return ComputeFlags.NO_REWRITE;
|
||||
|
||||
if (!classNode.interfaces.contains(MARKER_IFACE.getInternalName()))
|
||||
return ComputeFlags.NO_REWRITE;
|
||||
|
||||
Type array = Type.getType("[" + classType.getDescriptor());
|
||||
String arrayDesc = array.getDescriptor();
|
||||
|
||||
FieldNode values = classNode.fields.stream().filter(f -> f.desc.equals(arrayDesc) && ((f.access & FLAGS) == FLAGS)).findFirst().orElse(null);
|
||||
|
||||
if (!classNode.interfaces.contains(MARKER_IFACE.getInternalName())) {
|
||||
return ComputeFlags.NO_REWRITE;
|
||||
}
|
||||
|
||||
//Static methods named "create" with first argument as a string
|
||||
List<MethodNode> candidates = classNode.methods.stream()
|
||||
.filter(m -> ((m.access & Opcodes.ACC_STATIC) != 0) && m.name.equals("create"))
|
||||
|
||||
@@ -203,7 +203,14 @@ public class RuntimeDistCleaner implements ILaunchPluginService {
|
||||
|
||||
@Override
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty) {
|
||||
return isEmpty ? NAY : YAY;
|
||||
if (isEmpty)
|
||||
return NAY;
|
||||
|
||||
String internalName = classType.getInternalName();
|
||||
if (internalName.startsWith("net/minecraftforge/"))
|
||||
return NAY;
|
||||
|
||||
return YAY;
|
||||
}
|
||||
|
||||
private static class LambdaGatherer extends MethodVisitor {
|
||||
|
||||
@@ -46,7 +46,7 @@ record Scanner(ModFile fileToScan, ModFileScanData result) {
|
||||
try (InputStream in = Files.newInputStream(path)) {
|
||||
ModClassVisitor mcv = new ModClassVisitor();
|
||||
ClassReader cr = new ClassReader(in);
|
||||
cr.accept(mcv, 0);
|
||||
cr.accept(mcv, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
mcv.buildData(result.getClasses(), result.getAnnotations());
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
// mark path bad
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+2
-1
@@ -1,6 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -83,7 +83,8 @@ done
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -130,10 +131,13 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
@@ -141,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -149,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -198,11 +202,11 @@ fi
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
||||
Vendored
+10
-10
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
||||
+4
-3
@@ -141,9 +141,6 @@ dependencies {
|
||||
// For more info:
|
||||
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
|
||||
// http://www.gradle.org/docs/current/userguide/dependency_management.html
|
||||
|
||||
// Hack fix for now, force jopt-simple to be exactly 5.0.4 because Mojang ships that version, but some transitive dependencies request 6.0+
|
||||
implementation('net.sf.jopt-simple:jopt-simple:5.0.4') { version { strictly '5.0.4' } }
|
||||
}
|
||||
|
||||
// This block of code expands all declared replace properties in the specified resource targets.
|
||||
@@ -197,6 +194,10 @@ tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||
}
|
||||
|
||||
// IntelliJ no longer downloads javadocs and sources by default.
|
||||
// This tells Gradle to force IDEA to do it.
|
||||
idea.module { downloadJavadoc = downloadSources = true }
|
||||
|
||||
eclipse {
|
||||
// Run everytime eclipse builds the code
|
||||
//autoBuildTasks genEclipseRuns
|
||||
|
||||
@@ -145,6 +145,14 @@
|
||||
this.screen = p_91153_;
|
||||
if (this.screen != null) {
|
||||
this.screen.added();
|
||||
@@ -1146,6 +_,7 @@
|
||||
|
||||
FreeTypeUtil.destroy();
|
||||
Util.shutdownExecutors();
|
||||
+ net.minecraftforge.fml.config.ConfigTracker.INSTANCE.forceUnload();
|
||||
} catch (Throwable throwable) {
|
||||
LOGGER.error("Shutdown failure!", throwable);
|
||||
throw throwable;
|
||||
@@ -1215,9 +_,11 @@
|
||||
this.mouseHandler.handleAccumulatedMovement();
|
||||
profilerfiller.pop();
|
||||
|
||||
@@ -38,12 +38,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ /** @deprecated Forge: Use {@link net.minecraftforge.client.RenderTypeHelper#getEntityRenderType(RenderType, boolean)} while iterating through {@link net.minecraft.client.resources.model.BakedModel#getRenderTypes(BlockState, net.minecraft.util.RandomSource, net.minecraftforge.client.model.data.ModelData)}. */
|
||||
+ /** @deprecated Forge: Use {@link net.minecraftforge.client.RenderTypeHelper#getEntityRenderType(RenderType)} while iterating through {@link net.minecraft.client.resources.model.BakedModel#getRenderTypes(BlockState, net.minecraft.util.RandomSource, net.minecraftforge.client.model.data.ModelData)}. */
|
||||
+ @Deprecated // Note: this method does NOT support model-based render types
|
||||
public static RenderType getRenderType(BlockState p_364446_) {
|
||||
RenderType rendertype = getChunkRenderType(p_364446_);
|
||||
return rendertype == RenderType.translucent() ? Sheets.translucentItemSheet() : Sheets.cutoutBlockSheet();
|
||||
@@ -385,12 +_,77 @@
|
||||
@@ -385,12 +_,82 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@
|
||||
renderCutout = p_109292_;
|
||||
+ }
|
||||
+
|
||||
+ /** Forge: Check if we are running in {@linkplain net.minecraft.client.Minecraft#useFancyGraphics() fancy graphics} to account for fast graphics render types */
|
||||
+ public static boolean isFancy() {
|
||||
+ return renderCutout;
|
||||
+ }
|
||||
+
|
||||
+ private static final net.minecraftforge.client.ChunkRenderTypeSet CUTOUT_MIPPED = net.minecraftforge.client.ChunkRenderTypeSet.of(RenderType.cutoutMipped());
|
||||
+ private static final net.minecraftforge.client.ChunkRenderTypeSet SOLID = net.minecraftforge.client.ChunkRenderTypeSet.of(RenderType.solid());
|
||||
+ private static final Map<net.minecraft.core.Holder.Reference<Block>, net.minecraftforge.client.ChunkRenderTypeSet> BLOCK_RENDER_TYPES = Util.make(new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(TYPE_BY_BLOCK.size(), 0.5F), map -> {
|
||||
@@ -75,7 +80,7 @@
|
||||
+ });
|
||||
+
|
||||
+ /** @deprecated Use {@link net.minecraft.client.resources.model.BakedModel#getRenderTypes(BlockState, net.minecraft.util.RandomSource, net.minecraftforge.client.model.data.ModelData)}. */
|
||||
+ //@Deprecated(since = "1.19", forRemoval = true)
|
||||
+ @Deprecated(since = "1.21.4", forRemoval = true)
|
||||
+ public static net.minecraftforge.client.ChunkRenderTypeSet getRenderLayers(BlockState state) {
|
||||
+ Block block = state.getBlock();
|
||||
+ if (block instanceof LeavesBlock) {
|
||||
@@ -86,20 +91,20 @@
|
||||
+ }
|
||||
+
|
||||
+ /** @deprecated Set your render type in your block model's JSON (eg. {@code "render_type": "cutout"}) or override {@link net.minecraft.client.resources.model.BakedModel#getRenderTypes(BlockState, net.minecraft.util.RandomSource, net.minecraftforge.client.model.data.ModelData)} */
|
||||
+ //@Deprecated(since = "1.19", forRemoval = true)
|
||||
+ @Deprecated(since = "1.21.4", forRemoval = true)
|
||||
+ public static void setRenderLayer(Block block, RenderType type) {
|
||||
+ com.google.common.base.Preconditions.checkArgument(type.getChunkLayerId() >= 0, "The argument must be a valid chunk render type returned by RenderType#chunkBufferLayers().");
|
||||
+ setRenderLayer(block, net.minecraftforge.client.ChunkRenderTypeSet.of(type));
|
||||
+ }
|
||||
+
|
||||
+ /** @deprecated Set your render type in your block model's JSON (eg. {@code "render_type": "cutout"}) or override {@link net.minecraft.client.resources.model.BakedModel#getRenderTypes(BlockState, net.minecraft.util.RandomSource, net.minecraftforge.client.model.data.ModelData)} */
|
||||
+ //@Deprecated(since = "1.19", forRemoval = true)
|
||||
+ @Deprecated(since = "1.21.4", forRemoval = true)
|
||||
+ public static synchronized void setRenderLayer(Block block, java.util.function.Predicate<RenderType> predicate) {
|
||||
+ setRenderLayer(block, createSetFromPredicate(predicate));
|
||||
+ }
|
||||
+
|
||||
+ /** @deprecated Set your render type in your block model's JSON (eg. {@code "render_type": "cutout"}) or override {@link net.minecraft.client.resources.model.BakedModel#getRenderTypes(BlockState, net.minecraft.util.RandomSource, net.minecraftforge.client.model.data.ModelData)} */
|
||||
+ //@Deprecated(since = "1.19", forRemoval = true)
|
||||
+ @Deprecated(since = "1.21.4", forRemoval = true)
|
||||
+ public static synchronized void setRenderLayer(Block block, net.minecraftforge.client.ChunkRenderTypeSet layers) {
|
||||
+ checkClientLoading();
|
||||
+ BLOCK_RENDER_TYPES.put(net.minecraftforge.registries.ForgeRegistries.BLOCKS.getDelegateOrThrow(block), layers);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
int k1 = Mth.log2(j1);
|
||||
int l1;
|
||||
- if (k1 < p_261919_) {
|
||||
+ if (false) { // Forge: Do not lower the mipmap level
|
||||
+ if (k1 < p_261919_ && net.minecraftforge.common.ForgeConfig.CLIENT.allowMipmapLowering()) { // Forge: Do not lower the mipmap level
|
||||
LOGGER.warn("{}: dropping miplevel from {} to {}, because of minimum power of two: {}", this.location, p_261919_, k1, j1);
|
||||
l1 = k1;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
--- a/net/minecraft/client/resources/model/ModelDiscovery.java
|
||||
+++ b/net/minecraft/client/resources/model/ModelDiscovery.java
|
||||
@@ -37,7 +_,8 @@
|
||||
private static Set<ModelResourceLocation> listMandatoryModels() {
|
||||
Set<ModelResourceLocation> set = new HashSet<>();
|
||||
BuiltInRegistries.ITEM.listElements().forEach(p_368638_ -> {
|
||||
- ResourceLocation resourcelocation = p_368638_.value().components().get(DataComponents.ITEM_MODEL);
|
||||
+ // Forge: Force use vanilla components without gathering mod components in the event
|
||||
+ ResourceLocation resourcelocation = p_368638_.value().vanillaComponents().get(DataComponents.ITEM_MODEL);
|
||||
if (resourcelocation != null) {
|
||||
set.add(ModelResourceLocation.inventory(resourcelocation));
|
||||
}
|
||||
+92
-23
@@ -1,74 +1,142 @@
|
||||
--- a/net/minecraft/client/resources/model/SimpleBakedModel.java
|
||||
+++ b/net/minecraft/client/resources/model/SimpleBakedModel.java
|
||||
@@ -25,6 +_,15 @@
|
||||
@@ -25,7 +_,32 @@
|
||||
protected final boolean usesBlockLight;
|
||||
protected final TextureAtlasSprite particleIcon;
|
||||
protected final ItemTransforms transforms;
|
||||
-
|
||||
+ /** Forge: Block render types to be used with {@linkplain net.minecraft.client.GraphicsStatus#FANCY fancy graphics} */
|
||||
+ protected final net.minecraftforge.client.ChunkRenderTypeSet blockRenderTypes;
|
||||
+ /** Forge: Block render types to be used with {@linkplain net.minecraft.client.GraphicsStatus#FAST fast graphics} */
|
||||
+ protected final net.minecraftforge.client.ChunkRenderTypeSet blockRenderTypesFast;
|
||||
+ /** Forge: Item render types to be used with {@linkplain net.minecraft.client.GraphicsStatus#FANCY fancy graphics} */
|
||||
+ protected final List<net.minecraft.client.renderer.RenderType> itemRenderTypes;
|
||||
+ /** Forge: Item render types to be used with {@linkplain net.minecraft.client.GraphicsStatus#FAST fast graphics} */
|
||||
+ protected final List<net.minecraft.client.renderer.RenderType> itemRenderTypesFast;
|
||||
+ /** Forge: Item render types to be used with {@linkplain net.minecraft.client.GraphicsStatus#FABULOUS fabulous graphics} */
|
||||
+ protected final List<net.minecraft.client.renderer.RenderType> fabulousItemRenderTypes;
|
||||
+ /** Forge: If this model's {@linkplain #blockRenderTypes block render types} are rendering cutout, to account for older leaves model JSONs */
|
||||
+ protected final boolean isRenderingCutout;
|
||||
+
|
||||
+ /** @deprecated Forge: Use {@linkplain #SimpleBakedModel(List, Map, boolean, boolean, boolean, TextureAtlasSprite, ItemTransforms, net.minecraftforge.client.RenderTypeGroup) variant with RenderTypeGroup} **/
|
||||
+ /** @deprecated Forge: Use {@linkplain #SimpleBakedModel(List, Map, boolean, boolean, boolean, TextureAtlasSprite, ItemTransforms, net.minecraftforge.client.RenderTypeGroup, net.minecraftforge.client.RenderTypeGroup) variant with RenderTypeGroup} **/
|
||||
+ @Deprecated
|
||||
+ public SimpleBakedModel(List<BakedQuad> p_119489_, Map<Direction, List<BakedQuad>> p_119490_, boolean p_119491_, boolean p_119492_, boolean p_119493_, TextureAtlasSprite p_119494_, ItemTransforms p_119495_) {
|
||||
+ this(p_119489_, p_119490_, p_119491_, p_119492_, p_119493_, p_119494_, p_119495_, net.minecraftforge.client.RenderTypeGroup.EMPTY);
|
||||
+ }
|
||||
|
||||
+
|
||||
+ /** @deprecated Forge: Use {@linkplain #SimpleBakedModel(List, Map, boolean, boolean, boolean, TextureAtlasSprite, ItemTransforms, net.minecraftforge.client.RenderTypeGroup, net.minecraftforge.client.RenderTypeGroup) variant with RenderTypeGroup for fast graphics} **/
|
||||
+ @Deprecated(forRemoval = true, since = "1.21.4")
|
||||
+ public SimpleBakedModel(List<BakedQuad> p_119489_, Map<Direction, List<BakedQuad>> p_119490_, boolean p_119491_, boolean p_119492_, boolean p_119493_, TextureAtlasSprite p_119494_, ItemTransforms p_119495_, net.minecraftforge.client.RenderTypeGroup renderTypes) {
|
||||
+ this(p_119489_, p_119490_, p_119491_, p_119492_, p_119493_, p_119494_, p_119495_, renderTypes, net.minecraftforge.client.RenderTypeGroup.EMPTY);
|
||||
+ }
|
||||
+
|
||||
+ /** Constructor with {@link net.minecraftforge.client.RenderTypeGroup RenderTypeGroup} for fancy and fast graphics. Preferred over {@link net.minecraft.client.renderer.ItemBlockRenderTypes#setRenderLayer(net.minecraft.world.level.block.Block, net.minecraft.client.renderer.RenderType) ItemBlockRenderTypes.setRenderLayer(Block, RenderType)}. */
|
||||
public SimpleBakedModel(
|
||||
List<BakedQuad> p_119489_,
|
||||
@@ -33,7 +_,8 @@
|
||||
Map<Direction, List<BakedQuad>> p_119490_,
|
||||
@@ -33,7 +_,9 @@
|
||||
boolean p_119492_,
|
||||
boolean p_119493_,
|
||||
TextureAtlasSprite p_119494_,
|
||||
- ItemTransforms p_119495_
|
||||
+ ItemTransforms p_119495_,
|
||||
+ net.minecraftforge.client.RenderTypeGroup renderTypes
|
||||
+ net.minecraftforge.client.RenderTypeGroup renderTypes,
|
||||
+ net.minecraftforge.client.RenderTypeGroup renderTypesFast
|
||||
) {
|
||||
this.unculledFaces = p_119489_;
|
||||
this.culledFaces = p_119490_;
|
||||
@@ -42,6 +_,9 @@
|
||||
@@ -42,6 +_,15 @@
|
||||
this.usesBlockLight = p_119492_;
|
||||
this.particleIcon = p_119494_;
|
||||
this.transforms = p_119495_;
|
||||
+ this.blockRenderTypes = !renderTypes.isEmpty() ? net.minecraftforge.client.ChunkRenderTypeSet.of(renderTypes.block()) : null;
|
||||
+ this.itemRenderTypes = !renderTypes.isEmpty() ? List.of(renderTypes.entity()) : null;
|
||||
+ this.fabulousItemRenderTypes = !renderTypes.isEmpty() ? List.of(renderTypes.entityFabulous()) : null;
|
||||
+
|
||||
+ boolean hasRenderTypes = renderTypes != null && !renderTypes.isEmpty();
|
||||
+ boolean hasRenderTypesFast = renderTypesFast != null && !renderTypesFast.isEmpty();
|
||||
+ this.blockRenderTypes = hasRenderTypes ? net.minecraftforge.client.ChunkRenderTypeSet.of(renderTypes.block()) : null;
|
||||
+ this.blockRenderTypesFast = hasRenderTypesFast ? net.minecraftforge.client.ChunkRenderTypeSet.of(renderTypesFast.block()) : null;
|
||||
+ this.itemRenderTypes = hasRenderTypes ? List.of(renderTypes.entity()) : null;
|
||||
+ this.itemRenderTypesFast = hasRenderTypesFast ? List.of(renderTypesFast.entity()) : null;
|
||||
+ this.fabulousItemRenderTypes = hasRenderTypes ? List.of(renderTypes.entityFabulous()) : null;
|
||||
+ this.isRenderingCutout = hasRenderTypes && (renderTypes.block() == net.minecraft.client.renderer.RenderType.cutout() || renderTypes.block() == net.minecraft.client.renderer.RenderType.cutoutMipped());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,6 +_,25 @@
|
||||
@@ -79,6 +_,38 @@
|
||||
return this.transforms;
|
||||
}
|
||||
|
||||
+ private static final net.minecraftforge.client.ChunkRenderTypeSet SOLID_BLOCK = net.minecraftforge.client.ChunkRenderTypeSet.of(net.minecraft.client.renderer.RenderType.solid());
|
||||
+ private static final List<net.minecraft.client.renderer.RenderType> SOLID_BLOCK_ITEM = List.of(net.minecraft.client.renderer.RenderType.solid());
|
||||
+
|
||||
+ @Override
|
||||
+ public net.minecraftforge.client.ChunkRenderTypeSet getRenderTypes(BlockState state, RandomSource rand, net.minecraftforge.client.model.data.ModelData data) {
|
||||
+ return blockRenderTypes != null ? blockRenderTypes : BakedModel.super.getRenderTypes(state, rand, data);
|
||||
+ if (!net.minecraft.client.renderer.ItemBlockRenderTypes.isFancy()) {
|
||||
+ if (blockRenderTypesFast != null)
|
||||
+ return blockRenderTypesFast;
|
||||
+ if (isRenderingCutout && state.getBlock() instanceof net.minecraft.world.level.block.LeavesBlock)
|
||||
+ return SOLID_BLOCK;
|
||||
+ }
|
||||
+ return blockRenderTypes != null ? blockRenderTypes : BakedModel.super.getRenderTypes(state, rand, data);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public List<net.minecraft.client.renderer.RenderType> getRenderTypes(net.minecraft.world.item.ItemStack itemStack, boolean fabulous) {
|
||||
+ if (!fabulous) {
|
||||
+ if (itemRenderTypes != null) {
|
||||
+ return itemRenderTypes;
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (fabulousItemRenderTypes != null) {
|
||||
+ return fabulousItemRenderTypes;
|
||||
+ }
|
||||
+ }
|
||||
+ return BakedModel.super.getRenderTypes(itemStack, fabulous);
|
||||
+ if (!fabulous) {
|
||||
+ if (!net.minecraft.client.renderer.ItemBlockRenderTypes.isFancy()) {
|
||||
+ if (itemRenderTypesFast != null)
|
||||
+ return itemRenderTypesFast;
|
||||
+ if (isRenderingCutout && itemStack.getItem() instanceof net.minecraft.world.item.BlockItem blockItem && blockItem.getBlock() instanceof net.minecraft.world.level.block.LeavesBlock)
|
||||
+ return SOLID_BLOCK_ITEM;
|
||||
+ }
|
||||
+ if (itemRenderTypes != null)
|
||||
+ return itemRenderTypes;
|
||||
+ } else {
|
||||
+ if (fabulousItemRenderTypes != null)
|
||||
+ return fabulousItemRenderTypes;
|
||||
+ }
|
||||
+ return BakedModel.super.getRenderTypes(itemStack, fabulous);
|
||||
+ }
|
||||
+
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public static class Builder {
|
||||
private final ImmutableList.Builder<BakedQuad> unculledFaces = ImmutableList.builder();
|
||||
@@ -124,13 +_,19 @@
|
||||
@@ -124,13 +_,50 @@
|
||||
return this;
|
||||
}
|
||||
|
||||
+ private net.minecraftforge.client.RenderTypeGroup renderTypes = net.minecraftforge.client.RenderTypeGroup.EMPTY;
|
||||
+ private net.minecraftforge.client.RenderTypeGroup renderTypesFast = net.minecraftforge.client.RenderTypeGroup.EMPTY;
|
||||
+
|
||||
+ /**
|
||||
+ * Sets the render type to be used for this model, and will be used for any graphics setting.
|
||||
+ * <p>
|
||||
+ * If you need to set a specific render type for
|
||||
+ * {@linkplain net.minecraft.client.GraphicsStatus#FAST fast graphics}, consider using
|
||||
+ * {@link #renderTypes(net.minecraftforge.client.RenderTypeGroup, net.minecraftforge.client.RenderTypeGroup)
|
||||
+ * renderTypes(RenderTypeGroup, RenderTypeGroup)} instead which allows choosing render types for both fancy and
|
||||
+ * fast graphics.
|
||||
+ *
|
||||
+ * @apiNote If this model is for {@linkplain net.minecraft.world.level.block.LeavesBlock leaves} and if the
|
||||
+ * given render type is either {@linkplain net.minecraft.client.renderer.RenderType#cutout() cutout} or
|
||||
+ * {@linkplain net.minecraft.client.renderer.RenderType#cutoutMipped() cutout mipped}, it will be overridden
|
||||
+ * with {@linkplain net.minecraft.client.renderer.RenderType#solid() solid} when fast graphics is enabled.
|
||||
+ * @see #renderTypes(net.minecraftforge.client.RenderTypeGroup, net.minecraftforge.client.RenderTypeGroup)
|
||||
+ * renderTypes(RenderTypeGroup, RenderTypeGroup)
|
||||
+ */
|
||||
+ public SimpleBakedModel.Builder renderTypes(net.minecraftforge.client.RenderTypeGroup renderTypes) {
|
||||
+ return this.renderTypes(renderTypes, net.minecraftforge.client.RenderTypeGroup.EMPTY);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Sets the render types to be used for this model: one for
|
||||
+ * {@linkplain net.minecraft.client.GraphicsStatus#FANCY fancy graphics} and one for
|
||||
+ * {@linkplain net.minecraft.client.GraphicsStatus#FAST fast graphics}.
|
||||
+ *
|
||||
+ * @see #renderTypes(net.minecraftforge.client.RenderTypeGroup)
|
||||
+ */
|
||||
+ public SimpleBakedModel.Builder renderTypes(net.minecraftforge.client.RenderTypeGroup renderTypes, net.minecraftforge.client.RenderTypeGroup renderTypesFast) {
|
||||
+ this.renderTypes = renderTypes;
|
||||
+ this.renderTypesFast = renderTypesFast;
|
||||
+ return this;
|
||||
+ }
|
||||
+
|
||||
@@ -79,7 +147,8 @@
|
||||
Map<Direction, List<BakedQuad>> map = Maps.transformValues(this.culledFaces, ImmutableList.Builder::build);
|
||||
return new SimpleBakedModel(
|
||||
- this.unculledFaces.build(), new EnumMap<>(map), this.hasAmbientOcclusion, this.usesBlockLight, this.isGui3d, this.particleIcon, this.transforms
|
||||
+ this.unculledFaces.build(), new EnumMap<>(map), this.hasAmbientOcclusion, this.usesBlockLight, this.isGui3d, this.particleIcon, this.transforms, this.renderTypes
|
||||
+ // Forge: Account for render types in model JSONs
|
||||
+ this.unculledFaces.build(), new EnumMap<>(map), this.hasAmbientOcclusion, this.usesBlockLight, this.isGui3d, this.particleIcon, this.transforms, this.renderTypes, this.renderTypesFast
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
--- a/net/minecraft/core/Direction.java
|
||||
+++ b/net/minecraft/core/Direction.java
|
||||
@@ -49,6 +_,10 @@
|
||||
@@ -49,6 +_,15 @@
|
||||
private final Vec3i normal;
|
||||
private final Vec3 normalVec3;
|
||||
private static final Direction[] VALUES = values();
|
||||
+ private static final List<Direction> VALUES_VIEW = java.util.Collections.unmodifiableList(Arrays.asList(VALUES));
|
||||
+ public static final List<Direction> valuesView() {
|
||||
+ return VALUES_VIEW;
|
||||
+ }
|
||||
+ // In vanilla updates are notified only on the horizontal plane, in Forge we also notify up and down
|
||||
+ private static final List<Direction> UPDATE_ORDER = java.util.Collections.unmodifiableList(Stream.concat(Plane.HORIZONTAL.stream(), Plane.VERTICAL.stream()).toList());
|
||||
+ public static final List<Direction> getUpdateOrder() {
|
||||
+ return UPDATE_ORDER;
|
||||
+ }
|
||||
private static final Direction[] BY_3D_DATA = Arrays.stream(VALUES)
|
||||
.sorted(Comparator.comparingInt(p_235687_ -> p_235687_.data3d))
|
||||
|
||||
@@ -9,3 +9,29 @@
|
||||
private final GameTestInfo testInfo;
|
||||
private boolean finalCheckAdded;
|
||||
|
||||
@@ -996,5 +_,25 @@
|
||||
if (either.right().isPresent()) {
|
||||
this.fail("Failed to set biome for test");
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Adds a cleanup handler that will be called when the test is done, pass or fail.
|
||||
+ */
|
||||
+ public void addCleanup(Consumer<Boolean> handler) {
|
||||
+ this.testInfo.addListener(new GameTestListener() {
|
||||
+ @Override
|
||||
+ public void testPassed(GameTestInfo info, GameTestRunner runner) {
|
||||
+ handler.accept(true);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void testFailed(GameTestInfo info, GameTestRunner runner) {
|
||||
+ handler.accept(false);
|
||||
+ }
|
||||
+
|
||||
+ @Override public void testStructureLoaded(GameTestInfo info) { }
|
||||
+ @Override public void testAddedForRerun(GameTestInfo oldInfo, GameTestInfo newInfo, GameTestRunner runner) { }
|
||||
+ });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/gametest/framework/GameTestInfo.java
|
||||
+++ b/net/minecraft/gametest/framework/GameTestInfo.java
|
||||
@@ -265,7 +_,7 @@
|
||||
BlockPos blockpos = this.getOrCalculateNorthwestCorner();
|
||||
this.structureBlockEntity = StructureUtils.prepareTestStructure(this, blockpos, this.getRotation(), this.level);
|
||||
this.structureBlockPos = this.structureBlockEntity.getBlockPos();
|
||||
- StructureUtils.addCommandBlockAndButtonToStartTest(this.structureBlockPos, new BlockPos(1, 0, -1), this.getRotation(), this.level);
|
||||
+ StructureUtils.addCommandBlockAndButtonToStartTest(this.structureBlockPos, new BlockPos(1, 0, -1), Rotation.NONE, this.level); // Forge: The control blocks are always in the North West Corner
|
||||
StructureUtils.encaseStructure(this.getStructureBounds(), this.level, !this.testFunction.skyAccess());
|
||||
this.listeners.forEach(p_127630_ -> p_127630_.testStructureLoaded(this));
|
||||
return this;
|
||||
@@ -0,0 +1,22 @@
|
||||
--- a/net/minecraft/gametest/framework/ReportGameListener.java
|
||||
+++ b/net/minecraft/gametest/framework/ReportGameListener.java
|
||||
@@ -151,7 +_,7 @@
|
||||
private static BlockPos getBeaconPos(GameTestInfo p_344999_) {
|
||||
BlockPos blockpos = p_344999_.getStructureBlockPos();
|
||||
BlockPos blockpos1 = new BlockPos(-1, -2, -1);
|
||||
- return StructureTemplate.transform(blockpos.offset(blockpos1), Mirror.NONE, p_344999_.getRotation(), blockpos);
|
||||
+ return blockpos.offset(blockpos1); // Forge: The control blocks are always in the North West Corner
|
||||
}
|
||||
|
||||
private static void updateBeaconGlass(GameTestInfo p_343978_, Block p_344076_) {
|
||||
@@ -167,8 +_,8 @@
|
||||
ServerLevel serverlevel = p_177739_.getLevel();
|
||||
BlockPos blockpos = p_177739_.getStructureBlockPos();
|
||||
BlockPos blockpos1 = new BlockPos(-1, 0, -1);
|
||||
- BlockPos blockpos2 = StructureTemplate.transform(blockpos.offset(blockpos1), Mirror.NONE, p_177739_.getRotation(), blockpos);
|
||||
- serverlevel.setBlockAndUpdate(blockpos2, Blocks.LECTERN.defaultBlockState().rotate(p_177739_.getRotation()));
|
||||
+ BlockPos blockpos2 = blockpos.offset(blockpos1); // Forge: The control blocks are always in the North West Corner
|
||||
+ serverlevel.setBlockAndUpdate(blockpos2, Blocks.LECTERN.defaultBlockState());
|
||||
BlockState blockstate = serverlevel.getBlockState(blockpos2);
|
||||
ItemStack itemstack = createBook(p_177739_.getTestName(), p_177739_.isRequired(), p_177740_);
|
||||
LecternBlock.tryPlaceBook(null, serverlevel, blockpos2, blockstate, itemstack);
|
||||
@@ -0,0 +1,21 @@
|
||||
--- a/net/minecraft/gametest/framework/StructureUtils.java
|
||||
+++ b/net/minecraft/gametest/framework/StructureUtils.java
|
||||
@@ -132,10 +_,16 @@
|
||||
.orElseThrow(() -> new IllegalStateException("Missing test structure: " + p_311701_.getStructureName()))
|
||||
.getSize();
|
||||
BoundingBox boundingbox = getStructureBoundingBox(p_311042_, vec3i, p_310584_);
|
||||
- BlockPos blockpos = getStartCorner(p_311701_, p_311042_, p_310584_, p_312330_);
|
||||
+ BlockPos blockpos = getStartCorner(p_311701_, p_311042_, Rotation.NONE, p_312330_); // Forge: The control blocks are always in the North West Corner
|
||||
forceLoadChunks(boundingbox, p_312330_);
|
||||
clearSpaceForStructure(boundingbox, p_312330_);
|
||||
- return createStructureBlock(p_311701_, blockpos.below(), p_310584_, p_312330_);
|
||||
+ var entity = createStructureBlock(p_311701_, blockpos.below(), p_310584_, p_312330_);
|
||||
+ //Forge: We need to offset the structure so that it will load within bounds.
|
||||
+ if (p_310584_ != Rotation.NONE) {
|
||||
+ var rotated = StructureTemplate.getZeroPositionWithTransform(BlockPos.ZERO, Mirror.NONE, p_310584_, entity.getStructureSize().getX(), entity.getStructureSize().getZ());
|
||||
+ entity.setStructurePos(rotated.offset(new BlockPos(0, 1, 0)));
|
||||
+ }
|
||||
+ return entity;
|
||||
}
|
||||
|
||||
public static void encaseStructure(AABB p_330422_, ServerLevel p_331249_, boolean p_328180_) {
|
||||
@@ -0,0 +1,15 @@
|
||||
--- a/net/minecraft/gametest/framework/TestCommand.java
|
||||
+++ b/net/minecraft/gametest/framework/TestCommand.java
|
||||
@@ -313,7 +_,11 @@
|
||||
return Optional.empty();
|
||||
} else {
|
||||
TestFunction testfunction = optional.get();
|
||||
- GameTestInfo gametestinfo = new GameTestInfo(testfunction, structureblockentity.getRotation(), p_328153_, p_330368_);
|
||||
+ // Forge: The rotation is stored in the structure block, and added in the GameTestInfo constructor.
|
||||
+ // So reverse it to find the manually specified rotation so the test runs the same every time.
|
||||
+ var steps = StructureUtils.getRotationStepsForRotation(structureblockentity.getRotation()) - StructureUtils.getRotationStepsForRotation(testfunction.rotation());
|
||||
+ if (steps < 0) steps += 4;
|
||||
+ GameTestInfo gametestinfo = new GameTestInfo(testfunction, StructureUtils.getRotationForRotationSteps(steps), p_328153_, p_330368_);
|
||||
gametestinfo.setStructureBlockPos(p_332856_);
|
||||
return !verifyStructureExists(p_328153_, gametestinfo.getStructureName()) ? Optional.empty() : Optional.of(gametestinfo);
|
||||
}
|
||||
@@ -27,7 +27,7 @@
|
||||
p_358539_.layers(), p_358539_.lookupWithUpdatedTags(), p_250212_, p_249301_, p_366334_, p_251126_
|
||||
);
|
||||
+ var listeners = new java.util.ArrayList<>(reloadableserverresources.listeners());
|
||||
+ listeners.addAll(net.minecraftforge.event.ForgeEventFactory.onResourceReload(reloadableserverresources, p_330376_.compositeAccess()));
|
||||
+ listeners.addAll(net.minecraftforge.event.ForgeEventFactory.onResourceReload(reloadableserverresources, p_358539_.lookupWithUpdatedTags(), p_330376_.compositeAccess()));
|
||||
return SimpleReloadInstance.create(
|
||||
- p_248588_, reloadableserverresources.listeners(), p_249136_, p_249601_, DATA_RELOAD_INITIAL_TASK, LOGGER.isDebugEnabled()
|
||||
+ p_248588_, listeners, p_249136_, p_249601_, DATA_RELOAD_INITIAL_TASK, LOGGER.isDebugEnabled()
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +_,11 @@
|
||||
@@ -275,6 +_,13 @@
|
||||
if (this.queryThreadGs4 != null) {
|
||||
this.queryThreadGs4.stop();
|
||||
}
|
||||
@@ -54,6 +54,8 @@
|
||||
+ this.dediLanPinger.interrupt();
|
||||
+ this.dediLanPinger = null;
|
||||
+ }
|
||||
+
|
||||
+ net.minecraftforge.fml.config.ConfigTracker.INSTANCE.forceUnload();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,6 +24,35 @@
|
||||
this.write(chunkpos, completablefuture::join).handle((p_358676_, p_358677_) -> {
|
||||
if (p_358677_ != null) {
|
||||
this.level.getServer().reportChunkSaveFailure(p_358677_, this.storageInfo(), chunkpos);
|
||||
@@ -934,10 +_,27 @@
|
||||
void forEachSpawnCandidateChunk(Consumer<ChunkHolder> p_364582_) {
|
||||
LongIterator longiterator = this.distanceManager.getSpawnCandidateChunks();
|
||||
|
||||
+ // FORGE: separated forEach logic into its own method
|
||||
+ this.forEachChunk(longiterator, this::anyPlayerCloseEnoughForSpawningInternal, p_364582_);
|
||||
+ }
|
||||
+
|
||||
+ /** FORGE: grab forced chunks, use forEachChunk to avoid duplicate logic */
|
||||
+ void forEachForcedChunk(Consumer<ChunkHolder> callback) {
|
||||
+ LongIterator longiterator = this.distanceManager.forcedTickets.keySet().iterator();
|
||||
+ this.forEachChunk(longiterator, pos -> true, callback);
|
||||
+ }
|
||||
+
|
||||
+ /** FORGE: check if there are any forced chunks */
|
||||
+ boolean hasForcedChunks() {
|
||||
+ return !this.distanceManager.forcedTickets.isEmpty();
|
||||
+ }
|
||||
+
|
||||
+ /** FORGE: commonize logic between forEachSpawnCandidateChunk and forEachForcedChunk */
|
||||
+ private void forEachChunk(LongIterator longiterator, java.util.function.Predicate<ChunkPos> filter, Consumer<ChunkHolder> p_364582_) {
|
||||
while (longiterator.hasNext()) {
|
||||
long i = longiterator.nextLong();
|
||||
ChunkHolder chunkholder = this.visibleChunkMap.get(i);
|
||||
- if (chunkholder != null && this.anyPlayerCloseEnoughForSpawningInternal(chunkholder.getPos())) {
|
||||
+ if (chunkholder != null && filter.test(chunkholder.getPos())) {
|
||||
p_364582_.accept(chunkholder);
|
||||
}
|
||||
}
|
||||
@@ -1102,7 +_,7 @@
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
final Executor mainThreadExecutor;
|
||||
private long ticketTickCounter;
|
||||
private int simulationDistance = 10;
|
||||
+ private final Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> forcedTickets = new Long2ObjectOpenHashMap<>();
|
||||
+ final Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> forcedTickets = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
protected DistanceManager(Executor p_140774_, Executor p_140775_) {
|
||||
TaskScheduler<Runnable> taskscheduler = TaskScheduler.wrapExecutor("player ticket throttler", p_140775_);
|
||||
@@ -44,7 +44,7 @@
|
||||
+ }
|
||||
+
|
||||
+ public <T> void addRegionTicket(TicketType<T> p_140841_, ChunkPos p_140842_, int p_140843_, T p_140844_, boolean forceTicks) {
|
||||
+ Ticket<T> ticket = new Ticket<>(p_140841_, ChunkLevel.byStatus(FullChunkStatus.FULL) - p_140843_, p_140844_, forceTicks);
|
||||
+ Ticket<T> ticket = new Ticket<>(p_140841_, ChunkLevel.byStatus(FullChunkStatus.FULL) - p_140843_, p_140844_, forceTicks);
|
||||
long i = p_140842_.toLong();
|
||||
this.addTicket(i, ticket);
|
||||
this.tickingTicketsTracker.addTicket(i, ticket);
|
||||
|
||||
@@ -11,6 +11,34 @@
|
||||
ChunkAccess chunkaccess1 = chunkholder.getChunkIfPresent(ChunkStatus.FULL);
|
||||
if (chunkaccess1 != null) {
|
||||
this.storeInCache(i, chunkaccess1, ChunkStatus.FULL);
|
||||
@@ -368,9 +_,25 @@
|
||||
}
|
||||
|
||||
private void collectTickingChunks(List<LevelChunk> p_368970_) {
|
||||
- this.chunkMap.forEachSpawnCandidateChunk(p_358696_ -> {
|
||||
+ // FORGE: If there are no forced chunks, just do the vanilla logic
|
||||
+ if (!this.chunkMap.hasForcedChunks()) {
|
||||
+ this.chunkMap.forEachSpawnCandidateChunk(this.getChunkCollector(p_368970_, chunk -> this.level.isNaturalSpawningAllowed(chunk.getPos())));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // FORGE: We have forced chunks? Ok, use a linked hash set to preserve order
|
||||
+ Set<LevelChunk> set = new java.util.LinkedHashSet<>();
|
||||
+ this.chunkMap.forEachSpawnCandidateChunk(this.getChunkCollector(set, chunk -> this.level.isNaturalSpawningAllowed(chunk.getPos())));
|
||||
+ this.chunkMap.forEachForcedChunk(this.getChunkCollector(set, chunk -> true));
|
||||
+ p_368970_.addAll(set);
|
||||
+ }
|
||||
+
|
||||
+ /** FORGE: Isolate chunk collection callback */
|
||||
+ private Consumer<ChunkHolder> getChunkCollector(java.util.Collection<LevelChunk> p_368970_, java.util.function.Predicate<LevelChunk> predicate) {
|
||||
+ return (p_358696_ -> {
|
||||
LevelChunk levelchunk = p_358696_.getTickingChunk();
|
||||
- if (levelchunk != null && this.level.isNaturalSpawningAllowed(p_358696_.getPos())) {
|
||||
+ // FORGE: Use custom predicate instead of only checking isNaturalSpawningAllowed
|
||||
+ if (levelchunk != null && predicate.test(levelchunk)) {
|
||||
p_368970_.add(levelchunk);
|
||||
}
|
||||
});
|
||||
@@ -466,11 +_,19 @@
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
--- a/net/minecraft/world/entity/npc/VillagerTrades.java
|
||||
+++ b/net/minecraft/world/entity/npc/VillagerTrades.java
|
||||
@@ -1359,6 +_,7 @@
|
||||
private final int villagerXp;
|
||||
|
||||
public EmeraldsForVillagerTypeItem(int p_35669_, int p_35670_, int p_35671_, Map<VillagerType, Item> p_35672_) {
|
||||
+ if (false) // FORGE: Modders can add custom villager types, so remove this validation
|
||||
BuiltInRegistries.VILLAGER_TYPE.stream().filter(p_35680_ -> !p_35672_.containsKey(p_35680_)).findAny().ifPresent(p_327046_ -> {
|
||||
throw new IllegalStateException("Missing trade for villager type: " + BuiltInRegistries.VILLAGER_TYPE.getKey(p_327046_));
|
||||
});
|
||||
@@ -1372,7 +_,9 @@
|
||||
@Override
|
||||
public MerchantOffer getOffer(Entity p_219685_, RandomSource p_219686_) {
|
||||
if (p_219685_ instanceof VillagerDataHolder villagerdataholder) {
|
||||
- ItemCost itemcost = new ItemCost(this.trades.get(villagerdataholder.getVillagerData().getType()), this.cost);
|
||||
+ Item item = this.trades.get(villagerdataholder.getVillagerData().getType());
|
||||
+ if (item == null) return null; // FORGE: Account for modded villager types by returning null if there is no trade
|
||||
+ ItemCost itemcost = new ItemCost(item, this.cost);
|
||||
return new MerchantOffer(itemcost, new ItemStack(Items.EMERALD), this.maxUses, this.villagerXp, 0.05F);
|
||||
} else {
|
||||
return null;
|
||||
@@ -0,0 +1,12 @@
|
||||
--- a/net/minecraft/world/entity/npc/VillagerType.java
|
||||
+++ b/net/minecraft/world/entity/npc/VillagerType.java
|
||||
@@ -69,4 +_,9 @@
|
||||
public static VillagerType byBiome(Holder<Biome> p_204074_) {
|
||||
return p_204074_.unwrapKey().map(BY_BIOME::get).orElse(PLAINS);
|
||||
}
|
||||
+
|
||||
+ /** FORGE: Registers the VillagerType that will spawn in the given biome. This method should be called during FMLCommonSetupEvent using event.enqueueWork() */
|
||||
+ public static void registerBiomeType(ResourceKey<Biome> biomeKey, VillagerType villagerType) {
|
||||
+ BY_BIOME.put(biomeKey, villagerType);
|
||||
+ }
|
||||
}
|
||||
@@ -129,7 +129,7 @@
|
||||
super.blockUsingShield(p_36295_);
|
||||
ItemStack itemstack = this.getItemBlockingWith();
|
||||
- if (p_36295_.canDisableShield() && itemstack != null) {
|
||||
+ if (itemstack != null && itemstack.canDisableShield(this.useItem, this, p_36295_)) {
|
||||
+ if (itemstack != null && p_36295_.getWeaponItem().canDisableShield(itemstack, this, p_36295_)) {
|
||||
this.disableShield(itemstack);
|
||||
}
|
||||
}
|
||||
@@ -142,6 +142,19 @@
|
||||
if (!this.level().isClientSide) {
|
||||
this.awardStat(Stats.ITEM_USED.get(this.useItem.getItem()));
|
||||
}
|
||||
@@ -928,8 +_,11 @@
|
||||
if (p_36383_ >= 3.0F) {
|
||||
int i = 1 + Mth.floor(p_36383_);
|
||||
InteractionHand interactionhand = this.getUsedItemHand();
|
||||
+ // FORGE: cache this.useItem -- if this.stopUsingItem() is called, it will be set to ItemStack.EMPTY
|
||||
+ ItemStack currentItem = this.useItem;
|
||||
this.useItem.hurtAndBreak(i, this, getSlotForHand(interactionhand));
|
||||
- if (this.useItem.isEmpty()) {
|
||||
+ // FORGE: use cached item since this.useItem could be ItemStack.EMPTY
|
||||
+ if (currentItem.isEmpty()) {
|
||||
if (interactionhand == InteractionHand.MAIN_HAND) {
|
||||
this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY);
|
||||
} else {
|
||||
@@ -946,10 +_,13 @@
|
||||
@Override
|
||||
protected void actuallyHurt(ServerLevel p_365751_, DamageSource p_36312_, float p_36313_) {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
if (optional.isEmpty()) {
|
||||
return InteractionResult.PASS;
|
||||
} else {
|
||||
@@ -115,5 +_,16 @@
|
||||
@@ -115,5 +_,21 @@
|
||||
private Optional<BlockState> getStripped(BlockState p_150691_) {
|
||||
return Optional.ofNullable(STRIPPABLES.get(p_150691_.getBlock()))
|
||||
.map(p_359378_ -> p_359378_.defaultBlockState().setValue(RotatedPillarBlock.AXIS, p_150691_.getValue(RotatedPillarBlock.AXIS)));
|
||||
@@ -25,5 +25,10 @@
|
||||
+ public static BlockState getAxeStrippingState(BlockState originalState) {
|
||||
+ Block block = STRIPPABLES.get(originalState.getBlock());
|
||||
+ return block != null ? block.defaultBlockState().setValue(RotatedPillarBlock.AXIS, originalState.getValue(RotatedPillarBlock.AXIS)) : null;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean canDisableShield(ItemStack stack, ItemStack shield, LivingEntity entity, LivingEntity attacker) {
|
||||
+ return true;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@@ -123,17 +_,25 @@
|
||||
@@ -123,17 +_,35 @@
|
||||
return this.builtInRegistryHolder;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,16 @@
|
||||
+ }
|
||||
+
|
||||
+ return builtComponents;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Forge: Hack fix used in
|
||||
+ * {@link net.minecraft.client.resources.model.ModelDiscovery#listMandatoryModels ModelBakery.listMandatoryModels},
|
||||
+ * during mod loading, before the Forge event bus is ready.
|
||||
+ */
|
||||
+ @org.jetbrains.annotations.ApiStatus.Internal
|
||||
+ public DataComponentMap vanillaComponents() {
|
||||
+ return components;
|
||||
}
|
||||
|
||||
public int getDefaultMaxStackSize() {
|
||||
|
||||
@@ -5,10 +5,27 @@
|
||||
import org.slf4j.Logger;
|
||||
|
||||
-public final class ItemStack implements DataComponentHolder {
|
||||
+public final class ItemStack implements DataComponentHolder, net.minecraftforge.common.extensions.IForgeItemStack {
|
||||
+public final class ItemStack extends net.minecraftforge.common.capabilities.CapabilityProvider<ItemStack> implements DataComponentHolder, net.minecraftforge.common.extensions.IForgeItemStack {
|
||||
public static final Codec<ItemStack> CODEC = Codec.lazyInitialized(
|
||||
() -> RecordCodecBuilder.create(
|
||||
p_359412_ -> p_359412_.group(
|
||||
@@ -252,13 +_,16 @@
|
||||
}
|
||||
|
||||
private ItemStack(ItemLike p_331826_, int p_332766_, PatchedDataComponentMap p_333722_) {
|
||||
+ super(ItemStack.class, true);
|
||||
this.item = p_331826_.asItem();
|
||||
this.count = p_332766_;
|
||||
this.components = p_333722_;
|
||||
this.getItem().verifyComponentsAfterLoad(this);
|
||||
+ gatherCapabilities(() -> this.item.getCapabilityProvider(this));
|
||||
}
|
||||
|
||||
private ItemStack(@Nullable Void p_282703_) {
|
||||
+ super(ItemStack.class, false);
|
||||
this.item = null;
|
||||
this.components = new PatchedDataComponentMap(DataComponentMap.EMPTY);
|
||||
}
|
||||
@@ -348,13 +_,22 @@
|
||||
}
|
||||
|
||||
@@ -33,6 +50,34 @@
|
||||
if (player != null && interactionresult instanceof InteractionResult.Success interactionresult$success && interactionresult$success.wasItemInteraction()) {
|
||||
player.awardStat(Stats.ITEM_USED.get(item));
|
||||
}
|
||||
@@ -459,18 +_,26 @@
|
||||
}
|
||||
|
||||
public void hurtAndBreak(int p_220158_, ServerLevel p_342197_, @Nullable ServerPlayer p_220160_, Consumer<Item> p_343361_) {
|
||||
- int i = this.processDurabilityChange(p_220158_, p_342197_, p_220160_);
|
||||
+ // FORGE: use context-sensitive sister of processDurabilityChange that calls IForgeItem.damageItem
|
||||
+ int i = this.processDurabilityChange(p_220158_, p_342197_, p_220160_, true, p_343361_);
|
||||
if (i != 0) {
|
||||
this.applyDamage(this.getDamageValue() + i, p_220160_, p_343361_);
|
||||
}
|
||||
}
|
||||
|
||||
private int processDurabilityChange(int p_362423_, ServerLevel p_364910_, @Nullable ServerPlayer p_365570_) {
|
||||
+ return this.processDurabilityChange(p_362423_, p_364910_, p_365570_, false, p_359411_ -> { });
|
||||
+ }
|
||||
+
|
||||
+ /** FORGE: context-sensitive sister of processDurabilityChange that calls IForgeItem.damageItem */
|
||||
+ private int processDurabilityChange(int p_362423_, ServerLevel p_364910_, @Nullable ServerPlayer p_365570_, boolean canBreak, Consumer<Item> onBreak) {
|
||||
if (!this.isDamageableItem()) {
|
||||
return 0;
|
||||
} else if (p_365570_ != null && p_365570_.hasInfiniteMaterials()) {
|
||||
return 0;
|
||||
} else {
|
||||
+ // FORGE: modify the base damage based on the item's impl of IForgeItem.damageItem
|
||||
+ p_362423_ = this.damageItem(p_362423_, p_364910_, p_365570_, canBreak, onBreak);
|
||||
return p_362423_ > 0 ? EnchantmentHelper.processDurabilityChange(p_364910_, this, p_362423_) : p_362423_;
|
||||
}
|
||||
}
|
||||
@@ -507,7 +_,13 @@
|
||||
p_41623_,
|
||||
serverlevel,
|
||||
@@ -41,7 +86,7 @@
|
||||
+ p_341563_ -> {
|
||||
+ if (p_41624_ instanceof Player player) {
|
||||
+ net.minecraftforge.event.ForgeEventFactory.onPlayerDestroyItem(player, this, p_335324_);
|
||||
+ player.stopUsingItem(); // Forge: fix MC-168573
|
||||
+ if (player.getUseItem() == this) player.stopUsingItem(); // Forge: fix MC-168573
|
||||
+ }
|
||||
+ p_41624_.onEquippedItemBroken(p_341563_, p_335324_);
|
||||
+ }
|
||||
|
||||
@@ -157,12 +157,12 @@
|
||||
T t = p_261885_.tryCast(enderdragonpart);
|
||||
if (t != null && p_261688_.test(t)) {
|
||||
p_262071_.add(t);
|
||||
@@ -919,16 +_,15 @@
|
||||
@@ -919,17 +_,16 @@
|
||||
public abstract Scoreboard getScoreboard();
|
||||
|
||||
public void updateNeighbourForOutputSignal(BlockPos p_46718_, Block p_46719_) {
|
||||
- for (Direction direction : Direction.Plane.HORIZONTAL) {
|
||||
+ for (Direction direction : Direction.valuesView()) {
|
||||
+ for (Direction direction : Direction.getUpdateOrder()) {
|
||||
BlockPos blockpos = p_46718_.relative(direction);
|
||||
if (this.hasChunkAt(blockpos)) {
|
||||
BlockState blockstate = this.getBlockState(blockpos);
|
||||
@@ -174,10 +174,12 @@
|
||||
blockpos = blockpos.relative(direction);
|
||||
blockstate = this.getBlockState(blockpos);
|
||||
- if (blockstate.is(Blocks.COMPARATOR)) {
|
||||
- this.neighborChanged(blockstate, blockpos, p_46719_, null, false);
|
||||
+ if (blockstate.getWeakChanges(this, blockpos)) {
|
||||
this.neighborChanged(blockstate, blockpos, p_46719_, null, false);
|
||||
+ blockstate.onNeighborChange(this, blockpos, p_46718_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1031,6 +_,20 @@
|
||||
}
|
||||
|
||||
|
||||
@@ -31,12 +31,12 @@
|
||||
p_368198_.addFreshEntity(primedtnt);
|
||||
}
|
||||
|
||||
+ @Deprecated //Forge: Prefer using IForgeBlock#catchFire
|
||||
+ @Deprecated //Forge: Prefer using IForgeBlock#onCaughtFire
|
||||
public static void explode(Level p_57434_, BlockPos p_57435_) {
|
||||
explode(p_57434_, p_57435_, null);
|
||||
}
|
||||
|
||||
+ @Deprecated //Forge: Prefer using IForgeBlock#catchFire
|
||||
+ @Deprecated //Forge: Prefer using IForgeBlock#onCaughtFire
|
||||
private static void explode(Level p_57437_, BlockPos p_57438_, @Nullable LivingEntity p_57439_) {
|
||||
if (!p_57437_.isClientSide) {
|
||||
PrimedTnt primedtnt = new PrimedTnt(
|
||||
|
||||
@@ -1,13 +1,24 @@
|
||||
--- a/net/minecraft/world/level/storage/LevelSummary.java
|
||||
+++ b/net/minecraft/world/level/storage/LevelSummary.java
|
||||
@@ -277,6 +_,10 @@
|
||||
}
|
||||
}
|
||||
@@ -14,7 +_,7 @@
|
||||
import net.minecraft.world.level.LevelSettings;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
+ public boolean isLifecycleExperimental() {
|
||||
+ return this.settings.getLifecycle().equals(com.mojang.serialization.Lifecycle.experimental());
|
||||
-public class LevelSummary implements Comparable<LevelSummary> {
|
||||
+public class LevelSummary implements Comparable<LevelSummary>, net.minecraftforge.common.extensions.IForgeLevelSummary {
|
||||
public static final Component PLAY_WORLD = Component.translatable("selectWorld.select");
|
||||
private final LevelSettings settings;
|
||||
private final LevelVersion levelVersion;
|
||||
@@ -275,6 +_,12 @@
|
||||
public boolean canRecreate() {
|
||||
return false;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ // TODO Forge: Remove in 1.22. It is kept here for binary compatibility, but already exists in IForgeLevelSummary
|
||||
+ @Deprecated(forRemoval = true, since = "1.21.4")
|
||||
+ public boolean isLifecycleExperimental() {
|
||||
+ return net.minecraftforge.common.extensions.IForgeLevelSummary.super.isLifecycleExperimental();
|
||||
}
|
||||
|
||||
public static class SymlinkLevelSummary extends LevelSummary {
|
||||
private static final Component MORE_INFO_BUTTON = Component.translatable("symlink_warning.more_info");
|
||||
private static final Component INFO = Component.translatable("symlink_warning.title").withColor(-65536);
|
||||
+9
-9
@@ -24,26 +24,26 @@ dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
libs {
|
||||
library('forgespi', 'net.minecraftforge:forgespi:7.1.5') // Needs modlauncher
|
||||
library('modlauncher', 'net.minecraftforge:modlauncher:10.2.2') // Needs securemodules
|
||||
library('securemodules', 'net.minecraftforge:securemodules:2.2.20') // Needs unsafe
|
||||
library('modlauncher', 'net.minecraftforge:modlauncher:10.2.4') // Needs securemodules
|
||||
library('securemodules', 'net.minecraftforge:securemodules:2.2.21') // Needs unsafe
|
||||
library('unsafe', 'net.minecraftforge:unsafe:0.9.2')
|
||||
library('accesstransformers', 'net.minecraftforge:accesstransformers:8.2.0')
|
||||
library('coremods', 'net.minecraftforge:coremods:5.2.1')
|
||||
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:6.2.8')
|
||||
library('eventbus', 'net.minecraftforge:eventbus:6.2.27')
|
||||
library('typetools', 'net.jodah:typetools:0.6.3') // Needed by EventBus because of lambdas
|
||||
library('mergetool-api', 'net.minecraftforge:mergetool-api:1.0')
|
||||
|
||||
library('mixin', 'org.spongepowered:mixin:0.8.7')
|
||||
library('nulls', 'org.jetbrains:annotations:24.1.0') // Got to have our null annotations!
|
||||
library('slf4j-api', 'org.slf4j:slf4j-api:2.0.7')
|
||||
library('maven-artifact', 'org.apache.maven:maven-artifact:3.8.5')
|
||||
library('maven-artifact', 'org.apache.maven:maven-artifact:3.8.8')
|
||||
|
||||
// Google's InMemory File System. Used by ForgeDev Tests for now, but could be useful for a lot of things.
|
||||
library('jimfs', 'com.google.jimfs:jimfs:1.3.0')
|
||||
bundle('jimfs', ['guava', 'failureaccess'])
|
||||
|
||||
version('bootstrap', '2.1.3')
|
||||
version('bootstrap', '2.1.8')
|
||||
library('bootstrap', 'net.minecraftforge', 'bootstrap' ).versionRef('bootstrap') // Needs modlauncher
|
||||
library('bootstrap-api', 'net.minecraftforge', 'bootstrap-api' ).versionRef('bootstrap')
|
||||
library('bootstrap-dev', 'net.minecraftforge', 'bootstrap-dev' ).versionRef('bootstrap')
|
||||
@@ -60,14 +60,14 @@ dependencyResolutionManagement {
|
||||
|
||||
// Terminal Console Appender.. essentually make pretty colors in the console, but it's been a PITA
|
||||
library('terminalconsoleappender', 'net.minecrell:terminalconsoleappender:1.2.0')
|
||||
version('jline', '3.12.1')
|
||||
version('jline', '3.25.1')
|
||||
library('jline-reader', 'org.jline', 'jline-reader' ).versionRef('jline')
|
||||
library('jline-terminal', 'org.jline', 'jline-terminal' ).versionRef('jline')
|
||||
library('jline-terminal-jna', 'org.jline', 'jline-terminal-jna').versionRef('jline') // Colors and tab completeion
|
||||
bundle('terminalconsoleappender', ['terminalconsoleappender', 'jline-reader', 'jline-terminal', 'jline-terminal-jna'])
|
||||
|
||||
// The core of our configuration system, it has many flaws, but it works for the most part
|
||||
version('night-config', '3.7.3')
|
||||
version('night-config', '3.7.4')
|
||||
library('night-config-toml', 'com.electronwill.night-config', 'toml').versionRef('night-config')
|
||||
library('night-config-core', 'com.electronwill.night-config', 'core').versionRef('night-config')
|
||||
bundle('night-config', ['night-config-toml', 'night-config-core'])
|
||||
|
||||
@@ -346,7 +346,10 @@ public class ForgeHooksClient {
|
||||
return LazyInit.INSTANCE;
|
||||
}
|
||||
|
||||
// NO-OP method, kept for bin-compat. Used in TitleScreen.java
|
||||
@Deprecated(forRemoval = true, since = "1.21.4")
|
||||
public static void renderMainMenu(TitleScreen gui, GuiGraphics graphics, Font font, int width, int height, int alpha) {
|
||||
/*
|
||||
VersionChecker.Status status = getForgeVersionStatus();
|
||||
|
||||
if (status == VersionChecker.Status.BETA || status == VersionChecker.Status.BETA_OUTDATED) {
|
||||
@@ -356,6 +359,7 @@ public class ForgeHooksClient {
|
||||
line = Component.translatable("forge.update.beta.2");
|
||||
graphics.drawCenteredString(font, line, width / 2, 4 + (font.lineHeight + 1), 0xFFFFFF | alpha);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public static String forgeStatusLine;
|
||||
|
||||
@@ -9,8 +9,6 @@ import com.mojang.blaze3d.platform.InputConstants;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.Cancelable;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
@@ -37,7 +35,6 @@ import java.util.function.Consumer;
|
||||
* @see MouseInput
|
||||
* @see KeyInput
|
||||
*/
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public abstract class ScreenEvent extends Event {
|
||||
private final Screen screen;
|
||||
|
||||
|
||||
@@ -12,14 +12,11 @@ import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.loading.FMLConfig;
|
||||
import net.minecraftforge.versions.forge.ForgeVersion;
|
||||
import net.minecraftforge.fml.VersionChecker;
|
||||
import net.minecraftforge.client.loading.ClientModLoader;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class TitleScreenModUpdateIndicator extends Screen {
|
||||
|
||||
private static final ResourceLocation VERSION_CHECK_ICONS = ResourceLocation.fromNamespaceAndPath(ForgeVersion.MOD_ID, "textures/gui/version_check_icons.png");
|
||||
|
||||
@@ -24,15 +24,12 @@ import org.apache.logging.log4j.Logger;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.ForgeConfig;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.client.gui.LoadingErrorScreen;
|
||||
import net.minecraftforge.resource.ResourcePackLoader;
|
||||
import net.minecraftforge.server.LanguageHook;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class ClientModLoader {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static boolean loading;
|
||||
|
||||
@@ -208,6 +208,7 @@ public class CompositeModel implements IUnbakedGeometry<CompositeModel> {
|
||||
private final ItemTransforms transforms;
|
||||
private TextureAtlasSprite particle;
|
||||
private RenderTypeGroup lastRenderTypes = RenderTypeGroup.EMPTY;
|
||||
private RenderTypeGroup lastRenderTypesFast = RenderTypeGroup.EMPTY;
|
||||
|
||||
private Builder(boolean isAmbientOcclusion, boolean isGui3d, boolean isSideLit, TextureAtlasSprite particle, ItemTransforms transforms) {
|
||||
this.isAmbientOcclusion = isAmbientOcclusion;
|
||||
@@ -218,23 +219,32 @@ public class CompositeModel implements IUnbakedGeometry<CompositeModel> {
|
||||
}
|
||||
|
||||
public void addLayer(BakedModel model) {
|
||||
flushQuads(null);
|
||||
flushQuads(RenderTypeGroup.EMPTY, RenderTypeGroup.EMPTY);
|
||||
children.add(model);
|
||||
}
|
||||
|
||||
private void addLayer(RenderTypeGroup renderTypes, List<BakedQuad> quads) {
|
||||
var modelBuilder = IModelBuilder.of(isAmbientOcclusion, isSideLit, isGui3d, transforms, particle, renderTypes);
|
||||
this.addLayer(renderTypes, RenderTypeGroup.EMPTY, quads);
|
||||
}
|
||||
|
||||
private void addLayer(RenderTypeGroup renderTypes, RenderTypeGroup renderTypesFast, List<BakedQuad> quads) {
|
||||
var modelBuilder = IModelBuilder.of(isAmbientOcclusion, isSideLit, isGui3d, transforms, particle, renderTypes, renderTypesFast);
|
||||
quads.forEach(modelBuilder::addUnculledFace);
|
||||
children.add(modelBuilder.build());
|
||||
}
|
||||
|
||||
private void flushQuads(RenderTypeGroup renderTypes) {
|
||||
this.flushQuads(renderTypes, RenderTypeGroup.EMPTY);
|
||||
}
|
||||
|
||||
private void flushQuads(RenderTypeGroup renderTypes, RenderTypeGroup renderTypesFast) {
|
||||
if (!Objects.equals(renderTypes, lastRenderTypes)) {
|
||||
if (!quads.isEmpty()) {
|
||||
addLayer(lastRenderTypes, quads);
|
||||
addLayer(lastRenderTypes, lastRenderTypesFast, quads);
|
||||
quads.clear();
|
||||
}
|
||||
lastRenderTypes = renderTypes;
|
||||
lastRenderTypesFast = renderTypesFast;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,20 +254,28 @@ public class CompositeModel implements IUnbakedGeometry<CompositeModel> {
|
||||
}
|
||||
|
||||
public Builder addQuads(RenderTypeGroup renderTypes, BakedQuad... quadsToAdd) {
|
||||
flushQuads(renderTypes);
|
||||
return this.addQuads(renderTypes, RenderTypeGroup.EMPTY, quadsToAdd);
|
||||
}
|
||||
|
||||
public Builder addQuads(RenderTypeGroup renderTypes, RenderTypeGroup renderTypesFast, BakedQuad... quadsToAdd) {
|
||||
flushQuads(renderTypes, renderTypesFast);
|
||||
Collections.addAll(quads, quadsToAdd);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addQuads(RenderTypeGroup renderTypes, Collection<BakedQuad> quadsToAdd) {
|
||||
flushQuads(renderTypes);
|
||||
return this.addQuads(renderTypes, RenderTypeGroup.EMPTY, quadsToAdd);
|
||||
}
|
||||
|
||||
public Builder addQuads(RenderTypeGroup renderTypes, RenderTypeGroup renderTypesFast, Collection<BakedQuad> quadsToAdd) {
|
||||
flushQuads(renderTypes, renderTypesFast);
|
||||
quads.addAll(quadsToAdd);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BakedModel build() {
|
||||
if (!quads.isEmpty()) {
|
||||
addLayer(lastRenderTypes, quads);
|
||||
addLayer(lastRenderTypes, lastRenderTypesFast, quads);
|
||||
}
|
||||
var childrenBuilder = ImmutableMap.<String, BakedModel>builder();
|
||||
var itemPassesBuilder = ImmutableList.<BakedModel>builder();
|
||||
|
||||
@@ -50,7 +50,7 @@ public class EmptyModel extends SimpleUnbakedGeometry<EmptyModel> {
|
||||
private static final Material MISSING_TEXTURE = new Material(InventoryMenu.BLOCK_ATLAS, MissingTextureAtlasSprite.getLocation());
|
||||
|
||||
public Baked() {
|
||||
super(List.of(), Map.of(), false, false, false, UnitTextureAtlasSprite.INSTANCE, ItemTransforms.NO_TRANSFORMS, RenderTypeGroup.EMPTY);
|
||||
super(List.of(), Map.of(), false, false, false, UnitTextureAtlasSprite.INSTANCE, ItemTransforms.NO_TRANSFORMS, RenderTypeGroup.EMPTY, RenderTypeGroup.EMPTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -73,6 +73,12 @@ public class ExtendedBlockModelDeserializer extends BlockModel.Deserializer
|
||||
model.customData.setRenderTypeHint(ResourceLocation.parse(renderTypeHintName));
|
||||
}
|
||||
|
||||
if (jsonobject.has("render_type_fast"))
|
||||
{
|
||||
var renderTypeHintName = GsonHelper.getAsString(jsonobject, "render_type_fast");
|
||||
model.customData.setRenderTypeFastHint(ResourceLocation.parse(renderTypeHintName));
|
||||
}
|
||||
|
||||
if (jsonobject.has("visibility"))
|
||||
{
|
||||
JsonObject visibility = GsonHelper.getAsJsonObject(jsonobject, "visibility");
|
||||
|
||||
@@ -32,6 +32,18 @@ public interface IModelBuilder<T extends IModelBuilder<T>> {
|
||||
return new Simple(hasAmbientOcclusion, usesBlockLight, isGui3d, transforms, particle, renderTypes);
|
||||
}
|
||||
|
||||
static IModelBuilder<?> of(
|
||||
boolean hasAmbientOcclusion,
|
||||
boolean usesBlockLight,
|
||||
boolean isGui3d,
|
||||
ItemTransforms transforms,
|
||||
TextureAtlasSprite particle,
|
||||
RenderTypeGroup renderTypes,
|
||||
RenderTypeGroup renderTypesFast
|
||||
){
|
||||
return new Simple(hasAmbientOcclusion, usesBlockLight, isGui3d, transforms, particle, renderTypes, renderTypesFast);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new model builder that collects quads to the provided list, returning
|
||||
* {@linkplain EmptyModel#BAKED an empty model} if you call {@link #build()}.
|
||||
@@ -49,12 +61,20 @@ public interface IModelBuilder<T extends IModelBuilder<T>> {
|
||||
class Simple implements IModelBuilder<Simple> {
|
||||
private final SimpleBakedModel.Builder builder;
|
||||
private final RenderTypeGroup renderTypes;
|
||||
private final RenderTypeGroup renderTypesFast;
|
||||
|
||||
private Simple(boolean hasAmbientOcclusion, boolean usesBlockLight, boolean isGui3d,
|
||||
ItemTransforms transforms, TextureAtlasSprite particle, RenderTypeGroup renderTypes
|
||||
) {
|
||||
this(hasAmbientOcclusion, usesBlockLight, isGui3d, transforms, particle, renderTypes, RenderTypeGroup.EMPTY);
|
||||
}
|
||||
|
||||
private Simple(boolean hasAmbientOcclusion, boolean usesBlockLight, boolean isGui3d,
|
||||
ItemTransforms transforms, TextureAtlasSprite particle, RenderTypeGroup renderTypes, RenderTypeGroup renderTypesFast
|
||||
) {
|
||||
this.builder = new SimpleBakedModel.Builder(hasAmbientOcclusion, usesBlockLight, isGui3d, transforms).particle(particle);
|
||||
this.renderTypes = renderTypes;
|
||||
this.renderTypesFast = renderTypesFast;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,10 +39,16 @@ public class ItemLayerModel implements IUnbakedGeometry<ItemLayerModel> {
|
||||
@Nullable
|
||||
private ImmutableList<Material> textures;
|
||||
private final Int2ObjectMap<ResourceLocation> renderTypeNames;
|
||||
private final Int2ObjectMap<ResourceLocation> renderTypeFastNames;
|
||||
|
||||
private ItemLayerModel(@Nullable ImmutableList<Material> textures, Int2ObjectMap<ResourceLocation> renderTypeNames) {
|
||||
this(textures, renderTypeNames, new Int2ObjectOpenHashMap<>());
|
||||
}
|
||||
|
||||
private ItemLayerModel(@Nullable ImmutableList<Material> textures, Int2ObjectMap<ResourceLocation> renderTypeNames, Int2ObjectMap<ResourceLocation> renderTypeFastNames) {
|
||||
this.textures = textures;
|
||||
this.renderTypeNames = renderTypeNames;
|
||||
this.renderTypeFastNames = renderTypeFastNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,7 +75,9 @@ public class ItemLayerModel implements IUnbakedGeometry<ItemLayerModel> {
|
||||
var quads = UnbakedGeometryHelper.bakeElements(unbaked, $ -> sprite, modelState);
|
||||
var renderTypeName = renderTypeNames.get(i);
|
||||
var renderTypes = renderTypeName != null ? context.getRenderType(renderTypeName) : null;
|
||||
builder.addQuads(renderTypes != null ? renderTypes : normalRenderTypes, quads);
|
||||
var renderTypeFastName = renderTypeFastNames.get(i);
|
||||
var renderTypesFast = renderTypeFastName != null ? context.getRenderType(renderTypeFastName) : null;
|
||||
builder.addQuads(renderTypes != null ? renderTypes : normalRenderTypes, renderTypesFast != null ? renderTypesFast : RenderTypeGroup.EMPTY, quads);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
@@ -80,20 +88,27 @@ public class ItemLayerModel implements IUnbakedGeometry<ItemLayerModel> {
|
||||
|
||||
@Override
|
||||
public ItemLayerModel read(JsonObject jsonObject, JsonDeserializationContext deserializationContext) {
|
||||
var renderTypeNames = readRenderTypeNames(jsonObject, "render_types");
|
||||
var renderTypeFastNames = readRenderTypeNames(jsonObject, "render_types_fast");
|
||||
return new ItemLayerModel(null, renderTypeNames, renderTypeFastNames);
|
||||
}
|
||||
|
||||
private static Int2ObjectMap<ResourceLocation> readRenderTypeNames(JsonObject jsonObject, String key) {
|
||||
var renderTypeNames = new Int2ObjectOpenHashMap<ResourceLocation>();
|
||||
if (jsonObject.has("render_types")) {
|
||||
var renderTypes = jsonObject.getAsJsonObject("render_types");
|
||||
if (jsonObject.has(key)) {
|
||||
var renderTypes = jsonObject.getAsJsonObject(key);
|
||||
for (var entry : renderTypes.entrySet()) {
|
||||
var renderType = ResourceLocation.parse(entry.getKey());
|
||||
for (var layer : entry.getValue().getAsJsonArray())
|
||||
if (renderTypeNames.put(layer.getAsInt(), renderType) != null)
|
||||
throw new JsonParseException("Registered duplicate render type for layer " + layer);
|
||||
throw new JsonParseException("Registered duplicate " + key + " for layer " + layer);
|
||||
}
|
||||
}
|
||||
|
||||
return new ItemLayerModel(null, renderTypeNames);
|
||||
return renderTypeNames;
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true, since = "1.21.4")
|
||||
protected void readLayerData(JsonObject jsonObject, String name, Int2ObjectOpenHashMap<ResourceLocation> renderTypeNames, Int2ObjectMap<ForgeFaceData> layerData, boolean logWarning) {
|
||||
if (!jsonObject.has(name))
|
||||
return;
|
||||
|
||||
@@ -250,6 +250,42 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
models().cubeColumnHorizontal(name(block) + "_horizontal", side, end).renderType(renderType));
|
||||
}
|
||||
|
||||
public void axisBlockWithRenderTypeAndFast(RotatedPillarBlock block, String renderType, String renderTypeFast) {
|
||||
axisBlockWithRenderTypeAndFast(block, blockTexture(block), renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void logBlockWithRenderTypeAndFast(RotatedPillarBlock block, String renderType, String renderTypeFast) {
|
||||
axisBlockWithRenderTypeAndFast(block, blockTexture(block), extend(blockTexture(block), "_top"), renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void axisBlockWithRenderTypeAndFast(RotatedPillarBlock block, ResourceLocation baseName, String renderType, String renderTypeFast) {
|
||||
axisBlockWithRenderTypeAndFast(block, extend(baseName, "_side"), extend(baseName, "_end"), renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void axisBlockWithRenderTypeAndFast(RotatedPillarBlock block, ResourceLocation side, ResourceLocation end, String renderType, String renderTypeFast) {
|
||||
axisBlock(block,
|
||||
models().cubeColumn(name(block), side, end).renderType(renderType, renderTypeFast),
|
||||
models().cubeColumnHorizontal(name(block) + "_horizontal", side, end).renderType(renderType, renderTypeFast));
|
||||
}
|
||||
|
||||
public void axisBlockWithRenderTypeAndFast(RotatedPillarBlock block, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
axisBlockWithRenderTypeAndFast(block, blockTexture(block), renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void logBlockWithRenderType(RotatedPillarBlock block, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
axisBlockWithRenderTypeAndFast(block, blockTexture(block), extend(blockTexture(block), "_top"), renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void axisBlockWithRenderTypeAndFast(RotatedPillarBlock block, ResourceLocation baseName, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
axisBlockWithRenderTypeAndFast(block, extend(baseName, "_side"), extend(baseName, "_end"), renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void axisBlockWithRenderTypeAndFast(RotatedPillarBlock block, ResourceLocation side, ResourceLocation end, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
axisBlock(block,
|
||||
models().cubeColumn(name(block), side, end).renderType(renderType, renderTypeFast),
|
||||
models().cubeColumnHorizontal(name(block) + "_horizontal", side, end).renderType(renderType, renderTypeFast));
|
||||
}
|
||||
|
||||
public void axisBlock(RotatedPillarBlock block, ModelFile vertical, ModelFile horizontal) {
|
||||
getVariantBuilder(block)
|
||||
.partialState().with(RotatedPillarBlock.AXIS, Axis.Y)
|
||||
@@ -381,6 +417,38 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
stairsBlockInternalWithRenderType(block, name + "_stairs", side, bottom, top, renderType);
|
||||
}
|
||||
|
||||
public void stairsBlockWithRenderTypeAndFast(StairBlock block, ResourceLocation texture, String renderType, String renderTypeFast) {
|
||||
stairsBlockWithRenderTypeAndFast(block, texture, texture, texture, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void stairsBlockWithRenderTypeAndFast(StairBlock block, String name, ResourceLocation texture, String renderType, String renderTypeFast) {
|
||||
stairsBlockWithRenderTypeAndFast(block, name, texture, texture, texture, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void stairsBlockWithRenderTypeAndFast(StairBlock block, ResourceLocation side, ResourceLocation bottom, ResourceLocation top, String renderType, String renderTypeFast) {
|
||||
stairsBlockInternalWithRenderTypeAndFast(block, key(block).toString(), side, bottom, top, ResourceLocation.tryParse(renderType), ResourceLocation.tryParse(renderTypeFast));
|
||||
}
|
||||
|
||||
public void stairsBlockWithRenderTypeAndFast(StairBlock block, String name, ResourceLocation side, ResourceLocation bottom, ResourceLocation top, String renderType, String renderTypeFast) {
|
||||
stairsBlockInternalWithRenderTypeAndFast(block, name + "_stairs", side, bottom, top, ResourceLocation.tryParse(renderType), ResourceLocation.tryParse(renderTypeFast));
|
||||
}
|
||||
|
||||
public void stairsBlockWithRenderTypeAndFast(StairBlock block, ResourceLocation texture, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
stairsBlockWithRenderTypeAndFast(block, texture, texture, texture, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void stairsBlockWithRenderTypeAndFast(StairBlock block, String name, ResourceLocation texture, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
stairsBlockWithRenderTypeAndFast(block, name, texture, texture, texture, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void stairsBlockWithRenderTypeAndFast(StairBlock block, ResourceLocation side, ResourceLocation bottom, ResourceLocation top, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
stairsBlockInternalWithRenderTypeAndFast(block, key(block).toString(), side, bottom, top, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void stairsBlockWithRenderTypeAndFast(StairBlock block, String name, ResourceLocation side, ResourceLocation bottom, ResourceLocation top, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
stairsBlockInternalWithRenderTypeAndFast(block, name + "_stairs", side, bottom, top, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
private void stairsBlockInternal(StairBlock block, String baseName, ResourceLocation side, ResourceLocation bottom, ResourceLocation top) {
|
||||
ModelFile stairs = models().stairs(baseName, side, bottom, top);
|
||||
ModelFile stairsInner = models().stairsInner(baseName + "_inner", side, bottom, top);
|
||||
@@ -395,6 +463,13 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
stairsBlock(block, stairs, stairsInner, stairsOuter);
|
||||
}
|
||||
|
||||
private void stairsBlockInternalWithRenderTypeAndFast(StairBlock block, String baseName, ResourceLocation side, ResourceLocation bottom, ResourceLocation top, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
ModelFile stairs = models().stairs(baseName, side, bottom, top).renderType(renderType, renderTypeFast);
|
||||
ModelFile stairsInner = models().stairsInner(baseName + "_inner", side, bottom, top).renderType(renderType, renderTypeFast);
|
||||
ModelFile stairsOuter = models().stairsOuter(baseName + "_outer", side, bottom, top).renderType(renderType, renderTypeFast);
|
||||
stairsBlock(block, stairs, stairsInner, stairsOuter);
|
||||
}
|
||||
|
||||
public void stairsBlock(StairBlock block, ModelFile stairs, ModelFile stairsInner, ModelFile stairsOuter) {
|
||||
getVariantBuilder(block)
|
||||
.forAllStatesExcept(state -> {
|
||||
@@ -532,6 +607,32 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
models().fenceSide(name + "_fence_side", texture).renderType(renderType));
|
||||
}
|
||||
|
||||
public void fenceBlockWithRenderTypeAndFast(FenceBlock block, ResourceLocation texture, String renderType, String renderTypeFast) {
|
||||
String baseName = key(block).toString();
|
||||
fourWayBlock(block,
|
||||
models().fencePost(baseName + "_post", texture).renderType(renderType, renderTypeFast),
|
||||
models().fenceSide(baseName + "_side", texture).renderType(renderType, renderTypeFast));
|
||||
}
|
||||
|
||||
public void fenceBlockWithRenderTypeAndFast(FenceBlock block, String name, ResourceLocation texture, String renderType, String renderTypeFast) {
|
||||
fourWayBlock(block,
|
||||
models().fencePost(name + "_fence_post", texture).renderType(renderType, renderTypeFast),
|
||||
models().fenceSide(name + "_fence_side", texture).renderType(renderType, renderTypeFast));
|
||||
}
|
||||
|
||||
public void fenceBlockWithRenderTypeAndFast(FenceBlock block, ResourceLocation texture, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
String baseName = key(block).toString();
|
||||
fourWayBlock(block,
|
||||
models().fencePost(baseName + "_post", texture).renderType(renderType, renderTypeFast),
|
||||
models().fenceSide(baseName + "_side", texture).renderType(renderType, renderTypeFast));
|
||||
}
|
||||
|
||||
public void fenceBlockWithRenderTypeAndFast(FenceBlock block, String name, ResourceLocation texture, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
fourWayBlock(block,
|
||||
models().fencePost(name + "_fence_post", texture).renderType(renderType, renderTypeFast),
|
||||
models().fenceSide(name + "_fence_side", texture).renderType(renderType, renderTypeFast));
|
||||
}
|
||||
|
||||
public void fenceGateBlock(FenceGateBlock block, ResourceLocation texture) {
|
||||
fenceGateBlockInternal(block, key(block).toString(), texture);
|
||||
}
|
||||
@@ -556,6 +657,22 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
fenceGateBlockInternalWithRenderType(block, name + "_fence_gate", texture, renderType);
|
||||
}
|
||||
|
||||
public void fenceGateBlockWithRenderTypeAndFast(FenceGateBlock block, ResourceLocation texture, String renderType, String renderTypeFast) {
|
||||
fenceGateBlockInternalWithRenderTypeAndFast(block, key(block).toString(), texture, ResourceLocation.tryParse(renderType), ResourceLocation.tryParse(renderTypeFast));
|
||||
}
|
||||
|
||||
public void fenceGateBlockWithRenderTypeAndFast(FenceGateBlock block, String name, ResourceLocation texture, String renderType, String renderTypeFast) {
|
||||
fenceGateBlockInternalWithRenderTypeAndFast(block, name + "_fence_gate", texture, ResourceLocation.tryParse(renderType), ResourceLocation.tryParse(renderTypeFast));
|
||||
}
|
||||
|
||||
public void fenceGateBlockWithRenderTypeAndFast(FenceGateBlock block, ResourceLocation texture, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
fenceGateBlockInternalWithRenderTypeAndFast(block, key(block).toString(), texture, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void fenceGateBlockWithRenderTypeAndFast(FenceGateBlock block, String name, ResourceLocation texture, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
fenceGateBlockInternalWithRenderTypeAndFast(block, name + "_fence_gate", texture, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
private void fenceGateBlockInternal(FenceGateBlock block, String baseName, ResourceLocation texture) {
|
||||
ModelFile gate = models().fenceGate(baseName, texture);
|
||||
ModelFile gateOpen = models().fenceGateOpen(baseName + "_open", texture);
|
||||
@@ -572,6 +689,14 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
fenceGateBlock(block, gate, gateOpen, gateWall, gateWallOpen);
|
||||
}
|
||||
|
||||
private void fenceGateBlockInternalWithRenderTypeAndFast(FenceGateBlock block, String baseName, ResourceLocation texture, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
ModelFile gate = models().fenceGate(baseName, texture).renderType(renderType, renderTypeFast);
|
||||
ModelFile gateOpen = models().fenceGateOpen(baseName + "_open", texture).renderType(renderType, renderTypeFast);
|
||||
ModelFile gateWall = models().fenceGateWall(baseName + "_wall", texture).renderType(renderType, renderTypeFast);
|
||||
ModelFile gateWallOpen = models().fenceGateWallOpen(baseName + "_wall_open", texture).renderType(renderType, renderTypeFast);
|
||||
fenceGateBlock(block, gate, gateOpen, gateWall, gateWallOpen);
|
||||
}
|
||||
|
||||
public void fenceGateBlock(FenceGateBlock block, ModelFile gate, ModelFile gateOpen, ModelFile gateWall, ModelFile gateWallOpen) {
|
||||
getVariantBuilder(block).forAllStatesExcept(state -> {
|
||||
ModelFile model = gate;
|
||||
@@ -613,6 +738,22 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
wallBlockInternalWithRenderType(block, name + "_wall", texture, renderType);
|
||||
}
|
||||
|
||||
public void wallBlockWithRenderTypeAndFast(WallBlock block, ResourceLocation texture, String renderType, String renderTypeFast) {
|
||||
wallBlockInternalWithRenderTypeAndFast(block, key(block).toString(), texture, ResourceLocation.tryParse(renderType), ResourceLocation.tryParse(renderTypeFast));
|
||||
}
|
||||
|
||||
public void wallBlockWithRenderTypeAndFast(WallBlock block, String name, ResourceLocation texture, String renderType, String renderTypeFast) {
|
||||
wallBlockInternalWithRenderTypeAndFast(block, name + "_wall", texture, ResourceLocation.tryParse(renderType), ResourceLocation.tryParse(renderTypeFast));
|
||||
}
|
||||
|
||||
public void wallBlockWithRenderTypeAndFast(WallBlock block, ResourceLocation texture, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
wallBlockInternalWithRenderTypeAndFast(block, key(block).toString(), texture, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void wallBlockWithRenderTypeAndFast(WallBlock block, String name, ResourceLocation texture, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
wallBlockInternalWithRenderTypeAndFast(block, name + "_wall", texture, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
private void wallBlockInternal(WallBlock block, String baseName, ResourceLocation texture) {
|
||||
wallBlock(block, models().wallPost(baseName + "_post", texture),
|
||||
models().wallSide(baseName + "_side", texture),
|
||||
@@ -625,6 +766,12 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
models().wallSideTall(baseName + "_side_tall", texture).renderType(renderType));
|
||||
}
|
||||
|
||||
private void wallBlockInternalWithRenderTypeAndFast(WallBlock block, String baseName, ResourceLocation texture, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
wallBlock(block, models().wallPost(baseName + "_post", texture).renderType(renderType, renderTypeFast),
|
||||
models().wallSide(baseName + "_side", texture).renderType(renderType, renderTypeFast),
|
||||
models().wallSideTall(baseName + "_side_tall", texture).renderType(renderType, renderTypeFast));
|
||||
}
|
||||
|
||||
public static final ImmutableMap<Direction, Property<WallSide>> WALL_PROPS = ImmutableMap.<Direction, Property<WallSide>>builder()
|
||||
.put(Direction.EAST, BlockStateProperties.EAST_WALL)
|
||||
.put(Direction.NORTH, BlockStateProperties.NORTH_WALL)
|
||||
@@ -677,6 +824,22 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
paneBlockInternalWithRenderType(block, name + "_pane", pane, edge, renderType);
|
||||
}
|
||||
|
||||
public void paneBlockWithRenderTypeAndFast(IronBarsBlock block, ResourceLocation pane, ResourceLocation edge, String renderType, String renderTypeFast) {
|
||||
paneBlockInternalWithRenderTypeAndFast(block, key(block).toString(), pane, edge, ResourceLocation.tryParse(renderType), ResourceLocation.tryParse(renderTypeFast));
|
||||
}
|
||||
|
||||
public void paneBlockWithRenderTypeAndFast(IronBarsBlock block, String name, ResourceLocation pane, ResourceLocation edge, String renderType, String renderTypeFast) {
|
||||
paneBlockInternalWithRenderTypeAndFast(block, name + "_pane", pane, edge, ResourceLocation.tryParse(renderType), ResourceLocation.tryParse(renderTypeFast));
|
||||
}
|
||||
|
||||
public void paneBlockWithRenderTypeAndFast(IronBarsBlock block, ResourceLocation pane, ResourceLocation edge, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
paneBlockInternalWithRenderTypeAndFast(block, key(block).toString(), pane, edge, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void paneBlockWithRenderTypeAndFast(IronBarsBlock block, String name, ResourceLocation pane, ResourceLocation edge, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
paneBlockInternalWithRenderTypeAndFast(block, name + "_pane", pane, edge, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
private void paneBlockInternal(IronBarsBlock block, String baseName, ResourceLocation pane, ResourceLocation edge) {
|
||||
ModelFile post = models().panePost(baseName + "_post", pane, edge);
|
||||
ModelFile side = models().paneSide(baseName + "_side", pane, edge);
|
||||
@@ -695,6 +858,15 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
paneBlock(block, post, side, sideAlt, noSide, noSideAlt);
|
||||
}
|
||||
|
||||
private void paneBlockInternalWithRenderTypeAndFast(IronBarsBlock block, String baseName, ResourceLocation pane, ResourceLocation edge, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
ModelFile post = models().panePost(baseName + "_post", pane, edge).renderType(renderType, renderTypeFast);
|
||||
ModelFile side = models().paneSide(baseName + "_side", pane, edge).renderType(renderType, renderTypeFast);
|
||||
ModelFile sideAlt = models().paneSideAlt(baseName + "_side_alt", pane, edge).renderType(renderType, renderTypeFast);
|
||||
ModelFile noSide = models().paneNoSide(baseName + "_noside", pane).renderType(renderType, renderTypeFast);
|
||||
ModelFile noSideAlt = models().paneNoSideAlt(baseName + "_noside_alt", pane).renderType(renderType, renderTypeFast);
|
||||
paneBlock(block, post, side, sideAlt, noSide, noSideAlt);
|
||||
}
|
||||
|
||||
public void paneBlock(IronBarsBlock block, ModelFile post, ModelFile side, ModelFile sideAlt, ModelFile noSide, ModelFile noSideAlt) {
|
||||
MultiPartBlockStateBuilder builder = getMultipartBuilder(block)
|
||||
.part().modelFile(post).addModel().end();
|
||||
@@ -734,6 +906,22 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
doorBlockInternalWithRenderType(block, name + "_door", bottom, top, renderType);
|
||||
}
|
||||
|
||||
public void doorBlockWithRenderTypeAndFast(DoorBlock block, ResourceLocation bottom, ResourceLocation top, String renderType, String renderTypeFast) {
|
||||
doorBlockInternalWithRenderTypeAndFast(block, key(block).toString(), bottom, top, ResourceLocation.tryParse(renderType), ResourceLocation.tryParse(renderTypeFast));
|
||||
}
|
||||
|
||||
public void doorBlockWithRenderTypeAndFast(DoorBlock block, String name, ResourceLocation bottom, ResourceLocation top, String renderType, String renderTypeFast) {
|
||||
doorBlockInternalWithRenderTypeAndFast(block, name + "_door", bottom, top, ResourceLocation.tryParse(renderType), ResourceLocation.tryParse(renderTypeFast));
|
||||
}
|
||||
|
||||
public void doorBlockWithRenderTypeAndFast(DoorBlock block, ResourceLocation bottom, ResourceLocation top, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
doorBlockInternalWithRenderTypeAndFast(block, key(block).toString(), bottom, top, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void doorBlockWithRenderTypeAndFast(DoorBlock block, String name, ResourceLocation bottom, ResourceLocation top, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
doorBlockInternalWithRenderTypeAndFast(block, name + "_door", bottom, top, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
private void doorBlockInternal(DoorBlock block, String baseName, ResourceLocation bottom, ResourceLocation top) {
|
||||
ModelFile bottomLeft = models().doorBottomLeft(baseName + "_bottom_left", bottom, top);
|
||||
ModelFile bottomLeftOpen = models().doorBottomLeftOpen(baseName + "_bottom_left_open", bottom, top);
|
||||
@@ -758,6 +946,18 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
doorBlock(block, bottomLeft, bottomLeftOpen, bottomRight, bottomRightOpen, topLeft, topLeftOpen, topRight, topRightOpen);
|
||||
}
|
||||
|
||||
private void doorBlockInternalWithRenderTypeAndFast(DoorBlock block, String baseName, ResourceLocation bottom, ResourceLocation top, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
ModelFile bottomLeft = models().doorBottomLeft(baseName + "_bottom_left", bottom, top).renderType(renderType, renderTypeFast);
|
||||
ModelFile bottomLeftOpen = models().doorBottomLeftOpen(baseName + "_bottom_left_open", bottom, top).renderType(renderType, renderTypeFast);
|
||||
ModelFile bottomRight = models().doorBottomRight(baseName + "_bottom_right", bottom, top).renderType(renderType, renderTypeFast);
|
||||
ModelFile bottomRightOpen = models().doorBottomRightOpen(baseName + "_bottom_right_open", bottom, top).renderType(renderType, renderTypeFast);
|
||||
ModelFile topLeft = models().doorTopLeft(baseName + "_top_left", bottom, top).renderType(renderType, renderTypeFast);
|
||||
ModelFile topLeftOpen = models().doorTopLeftOpen(baseName + "_top_left_open", bottom, top).renderType(renderType, renderTypeFast);
|
||||
ModelFile topRight = models().doorTopRight(baseName + "_top_right", bottom, top).renderType(renderType, renderTypeFast);
|
||||
ModelFile topRightOpen = models().doorTopRightOpen(baseName + "_top_right_open", bottom, top).renderType(renderType, renderTypeFast);
|
||||
doorBlock(block, bottomLeft, bottomLeftOpen, bottomRight, bottomRightOpen, topLeft, topLeftOpen, topRight, topRightOpen);
|
||||
}
|
||||
|
||||
public void doorBlock(DoorBlock block, ModelFile bottomLeft, ModelFile bottomLeftOpen, ModelFile bottomRight, ModelFile bottomRightOpen, ModelFile topLeft, ModelFile topLeftOpen, ModelFile topRight, ModelFile topRightOpen) {
|
||||
getVariantBuilder(block).forAllStatesExcept(state -> {
|
||||
int yRot = ((int) state.getValue(DoorBlock.FACING).toYRot()) + 90;
|
||||
@@ -824,6 +1024,22 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
trapdoorBlockInternalWithRenderType(block, name + "_trapdoor", texture, orientable, renderType);
|
||||
}
|
||||
|
||||
public void trapdoorBlockWithRenderTypeAndFast(TrapDoorBlock block, ResourceLocation texture, boolean orientable, String renderType, String renderTypeFast) {
|
||||
trapdoorBlockInternalWithRenderTypeAndFast(block, key(block).toString(), texture, orientable, ResourceLocation.tryParse(renderType), ResourceLocation.tryParse(renderTypeFast));
|
||||
}
|
||||
|
||||
public void trapdoorBlockWithRenderTypeAndFast(TrapDoorBlock block, String name, ResourceLocation texture, boolean orientable, String renderType, String renderTypeFast) {
|
||||
trapdoorBlockInternalWithRenderTypeAndFast(block, name + "_trapdoor", texture, orientable, ResourceLocation.tryParse(renderType), ResourceLocation.tryParse(renderTypeFast));
|
||||
}
|
||||
|
||||
public void trapdoorBlockWithRenderTypeAndFast(TrapDoorBlock block, ResourceLocation texture, boolean orientable, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
trapdoorBlockInternalWithRenderTypeAndFast(block, key(block).toString(), texture, orientable, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
public void trapdoorBlockWithRenderTypeAndFast(TrapDoorBlock block, String name, ResourceLocation texture, boolean orientable, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
trapdoorBlockInternalWithRenderTypeAndFast(block, name + "_trapdoor", texture, orientable, renderType, renderTypeFast);
|
||||
}
|
||||
|
||||
private void trapdoorBlockInternal(TrapDoorBlock block, String baseName, ResourceLocation texture, boolean orientable) {
|
||||
ModelFile bottom = orientable ? models().trapdoorOrientableBottom(baseName + "_bottom", texture) : models().trapdoorBottom(baseName + "_bottom", texture);
|
||||
ModelFile top = orientable ? models().trapdoorOrientableTop(baseName + "_top", texture) : models().trapdoorTop(baseName + "_top", texture);
|
||||
@@ -838,6 +1054,13 @@ public abstract class BlockStateProvider implements DataProvider {
|
||||
trapdoorBlock(block, bottom, top, open, orientable);
|
||||
}
|
||||
|
||||
private void trapdoorBlockInternalWithRenderTypeAndFast(TrapDoorBlock block, String baseName, ResourceLocation texture, boolean orientable, ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
ModelFile bottom = orientable ? models().trapdoorOrientableBottom(baseName + "_bottom", texture).renderType(renderType, renderTypeFast) : models().trapdoorBottom(baseName + "_bottom", texture).renderType(renderType, renderTypeFast);
|
||||
ModelFile top = orientable ? models().trapdoorOrientableTop(baseName + "_top", texture).renderType(renderType, renderTypeFast) : models().trapdoorTop(baseName + "_top", texture).renderType(renderType, renderTypeFast);
|
||||
ModelFile open = orientable ? models().trapdoorOrientableOpen(baseName + "_open", texture).renderType(renderType, renderTypeFast) : models().trapdoorOpen(baseName + "_open", texture).renderType(renderType, renderTypeFast);
|
||||
trapdoorBlock(block, bottom, top, open, orientable);
|
||||
}
|
||||
|
||||
public void trapdoorBlock(TrapDoorBlock block, ModelFile bottom, ModelFile top, ModelFile open, boolean orientable) {
|
||||
getVariantBuilder(block).forAllStatesExcept(state -> {
|
||||
int xRot = 0;
|
||||
|
||||
@@ -60,6 +60,7 @@ public class ModelBuilder<T extends ModelBuilder<T>> extends ModelFile {
|
||||
protected final ExistingFileHelper existingFileHelper;
|
||||
|
||||
protected String renderType = null;
|
||||
protected String renderTypeFast = null;
|
||||
protected boolean ambientOcclusion = true;
|
||||
protected GuiLight guiLight = null;
|
||||
|
||||
@@ -149,12 +150,17 @@ public class ModelBuilder<T extends ModelBuilder<T>> extends ModelFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the render type for this model.
|
||||
* Set the render type for this model. Any render types to be used must be registered via
|
||||
* {@link net.minecraftforge.client.event.RegisterNamedRenderTypesEvent RegisterNamedRenderTypesEvent}.
|
||||
* <p>
|
||||
* Consider using {@linkplain #renderType(String, String)} if you need to set a render type for
|
||||
* {@linkplain net.minecraft.client.GraphicsStatus#FAST fast graphics}.
|
||||
*
|
||||
* @param renderType the render type. Must be registered via
|
||||
* {@link net.minecraftforge.client.event.RegisterNamedRenderTypesEvent}
|
||||
* @param renderType the render type
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code renderType} is {@code null}
|
||||
*
|
||||
* @throws NullPointerException if {@code renderType} is {@code null}
|
||||
* @see #renderType(String, String)
|
||||
*/
|
||||
public T renderType(String renderType) {
|
||||
Preconditions.checkNotNull(renderType, "Render type must not be null");
|
||||
@@ -162,16 +168,55 @@ public class ModelBuilder<T extends ModelBuilder<T>> extends ModelFile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the render type for this model.
|
||||
* Set the render types for this model. Any render types to be used must be registered via
|
||||
* {@link net.minecraftforge.client.event.RegisterNamedRenderTypesEvent RegisterNamedRenderTypesEvent}.
|
||||
*
|
||||
* @param renderType the render type. Must be registered via
|
||||
* {@link net.minecraftforge.client.event.RegisterNamedRenderTypesEvent}
|
||||
* @param renderType the render type for {@linkplain net.minecraft.client.GraphicsStatus#FANCY fancy graphics}
|
||||
* @param renderTypeFast the render type for {@linkplain net.minecraft.client.GraphicsStatus#FAST fast graphics}
|
||||
* @return this builder
|
||||
* @throws NullPointerException if {@code renderType} is {@code null}
|
||||
*
|
||||
* @throws NullPointerException if {@code renderType} is {@code null}
|
||||
*/
|
||||
public T renderType(String renderType, String renderTypeFast) {
|
||||
Preconditions.checkNotNull(renderType, "Render type must not be null");
|
||||
return renderType(ResourceLocation.parse(renderType), ResourceLocation.parse(renderTypeFast));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the render type for this model. Any render types to be used must be registered via
|
||||
* {@link net.minecraftforge.client.event.RegisterNamedRenderTypesEvent RegisterNamedRenderTypesEvent}.
|
||||
* <p>
|
||||
* Consider using {@linkplain #renderType(ResourceLocation, ResourceLocation)} if you need to set a render type for
|
||||
* {@linkplain net.minecraft.client.GraphicsStatus#FAST fast graphics}.
|
||||
*
|
||||
* @param renderType the render type
|
||||
* @return this builder
|
||||
*
|
||||
* @throws NullPointerException if {@code renderType} is {@code null}
|
||||
* @see #renderType(ResourceLocation, ResourceLocation)
|
||||
*/
|
||||
public T renderType(ResourceLocation renderType) {
|
||||
Preconditions.checkNotNull(renderType, "Render type must not be null");
|
||||
this.renderType = renderType.toString();
|
||||
this.renderTypeFast = null;
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the render types for this model. Any render types to be used must be registered via
|
||||
* {@link net.minecraftforge.client.event.RegisterNamedRenderTypesEvent RegisterNamedRenderTypesEvent}.
|
||||
*
|
||||
* @param renderType the render type for {@linkplain net.minecraft.client.GraphicsStatus#FANCY fancy graphics}
|
||||
* @param renderTypeFast the render type for {@linkplain net.minecraft.client.GraphicsStatus#FAST fast graphics}
|
||||
* @return this builder
|
||||
*
|
||||
* @throws NullPointerException if {@code renderType} is {@code null}
|
||||
*/
|
||||
public T renderType(ResourceLocation renderType, ResourceLocation renderTypeFast) {
|
||||
Preconditions.checkNotNull(renderType, "Render type must not be null");
|
||||
Preconditions.checkNotNull(renderTypeFast, "Fast graphics render type must not be null");
|
||||
this.renderType = renderType.toString();
|
||||
this.renderTypeFast = renderType.toString();
|
||||
return self();
|
||||
}
|
||||
|
||||
|
||||
@@ -366,7 +366,8 @@ public abstract class ModelProvider<T extends ModelBuilder<T>> implements DataPr
|
||||
}
|
||||
|
||||
public T leaves(String name, ResourceLocation texture) {
|
||||
return singleTexture(name, BLOCK_FOLDER + "/leaves", "all", texture);
|
||||
return singleTexture(name, BLOCK_FOLDER + "/leaves", "all", texture)
|
||||
.renderType("cutout_mipped", "solid");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+45
@@ -34,6 +34,7 @@ public class ItemLayerModelBuilder<T extends ModelBuilder<T>> extends CustomLoad
|
||||
|
||||
private final Int2ObjectMap<ForgeFaceData> faceData = new Int2ObjectOpenHashMap<>();
|
||||
private final Map<ResourceLocation, IntSet> renderTypes = new LinkedHashMap<>();
|
||||
private final Map<ResourceLocation, IntSet> renderTypesFast = new LinkedHashMap<>();
|
||||
private final IntSet layersWithRenderTypes = new IntOpenHashSet();
|
||||
|
||||
protected ItemLayerModelBuilder(T parent, ExistingFileHelper existingFileHelper)
|
||||
@@ -116,6 +117,23 @@ public class ItemLayerModelBuilder<T extends ModelBuilder<T>> extends CustomLoad
|
||||
return renderType(asLoc, layers);
|
||||
}
|
||||
|
||||
public ItemLayerModelBuilder<T> renderType(String renderType, String renderTypeFast, int... layers)
|
||||
{
|
||||
Preconditions.checkNotNull(renderType, "Render type must not be null");
|
||||
Preconditions.checkNotNull(renderTypeFast, "Fast graphics render type must not be null");
|
||||
ResourceLocation asLoc;
|
||||
if (renderType.contains(":"))
|
||||
asLoc = ResourceLocation.parse(renderType);
|
||||
else
|
||||
asLoc = parent.getLocation().withPath(renderType);
|
||||
ResourceLocation asLocFast;
|
||||
if (renderTypeFast.contains(":"))
|
||||
asLocFast = ResourceLocation.parse(renderTypeFast);
|
||||
else
|
||||
asLocFast = parent.getLocation().withPath(renderTypeFast);
|
||||
return renderType(asLoc, asLocFast, layers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the render type for a set of layers.
|
||||
*
|
||||
@@ -145,6 +163,25 @@ public class ItemLayerModelBuilder<T extends ModelBuilder<T>> extends CustomLoad
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemLayerModelBuilder<T> renderType(ResourceLocation renderType, ResourceLocation renderTypeFast, int... layers)
|
||||
{
|
||||
Preconditions.checkNotNull(renderType, "Render type must not be null");
|
||||
Preconditions.checkNotNull(renderTypeFast, "Fast graphics render type must not be null");
|
||||
Preconditions.checkNotNull(layers, "Layers must not be null");
|
||||
Preconditions.checkArgument(layers.length > 0, "At least one layer must be specified");
|
||||
Preconditions.checkArgument(Arrays.stream(layers).allMatch(i -> i >= 0), "All layers must be >= 0");
|
||||
var alreadyAssigned = Arrays.stream(layers).filter(layersWithRenderTypes::contains).toArray();
|
||||
Preconditions.checkArgument(alreadyAssigned.length == 0, "Attempted to re-assign layer render types: " + Arrays.toString(alreadyAssigned));
|
||||
var renderTypeLayers = renderTypes.computeIfAbsent(renderType, $ -> new IntOpenHashSet());
|
||||
var renderTypeFastLayers = renderTypesFast.computeIfAbsent(renderType, $ -> new IntOpenHashSet());
|
||||
Arrays.stream(layers).forEach(layer -> {
|
||||
renderTypeLayers.add(layer);
|
||||
renderTypeFastLayers.add(layer);
|
||||
layersWithRenderTypes.add(layer);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject toJson(JsonObject json)
|
||||
{
|
||||
@@ -171,6 +208,14 @@ public class ItemLayerModelBuilder<T extends ModelBuilder<T>> extends CustomLoad
|
||||
});
|
||||
json.add("render_types", renderTypes);
|
||||
|
||||
JsonObject renderTypesFast = new JsonObject();
|
||||
this.renderTypesFast.forEach((renderTypeFast, layers) -> {
|
||||
JsonArray array = new JsonArray();
|
||||
layers.intStream().sorted().forEach(array::add);
|
||||
renderTypesFast.add(renderTypeFast.toString(), array);
|
||||
});
|
||||
json.add("render_types_fast", renderTypesFast);
|
||||
|
||||
return json;
|
||||
}
|
||||
}
|
||||
+15
@@ -34,6 +34,8 @@ public class BlockGeometryBakingContext implements IGeometryBakingContext {
|
||||
private Transformation rootTransform;
|
||||
@Nullable
|
||||
private ResourceLocation renderTypeHint;
|
||||
@Nullable
|
||||
private ResourceLocation renderTypeFastHint;
|
||||
private boolean gui3d = true;
|
||||
|
||||
@ApiStatus.Internal
|
||||
@@ -115,10 +117,22 @@ public class BlockGeometryBakingContext implements IGeometryBakingContext {
|
||||
return owner.parent != null ? owner.parent.customData.getRenderTypeHint() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ResourceLocation getRenderTypeFastHint() {
|
||||
if (renderTypeFastHint != null)
|
||||
return renderTypeFastHint;
|
||||
return owner.parent != null ? owner.parent.customData.getRenderTypeFastHint() : null;
|
||||
}
|
||||
|
||||
public void setRenderTypeHint(ResourceLocation renderTypeHint) {
|
||||
this.renderTypeHint = renderTypeHint;
|
||||
}
|
||||
|
||||
public void setRenderTypeFastHint(ResourceLocation renderTypeFastHint) {
|
||||
this.renderTypeFastHint = renderTypeFastHint;
|
||||
}
|
||||
|
||||
public void setGui3d(boolean gui3d) {
|
||||
this.gui3d = gui3d;
|
||||
}
|
||||
@@ -128,6 +142,7 @@ public class BlockGeometryBakingContext implements IGeometryBakingContext {
|
||||
this.rootTransform = other.rootTransform;
|
||||
this.visibilityData.copyFrom(other.visibilityData);
|
||||
this.renderTypeHint = other.renderTypeHint;
|
||||
this.renderTypeFastHint = other.renderTypeFastHint;
|
||||
this.gui3d = other.gui3d;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,13 @@ public interface IGeometryBakingContext {
|
||||
@Nullable
|
||||
ResourceLocation getRenderTypeHint();
|
||||
|
||||
/**
|
||||
* @return a hint of the {@linkplain net.minecraft.client.GraphicsStatus#FAST fast graphics} render type this model
|
||||
* should use. Custom loaders may ignore this.
|
||||
*/
|
||||
@Nullable
|
||||
default ResourceLocation getRenderTypeFastHint() { return null; }
|
||||
|
||||
/**
|
||||
* Queries the visibility of a component of this model.
|
||||
*
|
||||
@@ -89,4 +96,20 @@ public interface IGeometryBakingContext {
|
||||
default RenderTypeGroup getRenderType(ResourceLocation name) {
|
||||
return NamedRenderTypeManager.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a {@link RenderTypeGroup} from the {@link #getRenderTypeHint()}, or the empty group if not found.}
|
||||
*/
|
||||
default RenderTypeGroup getRenderType() {
|
||||
var hint = getRenderTypeHint();
|
||||
return hint == null ? RenderTypeGroup.EMPTY : getRenderType(hint);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return a {@link RenderTypeGroup} from the {@link #getRenderTypeHint()}, or the empty group if not found.}
|
||||
*/
|
||||
default RenderTypeGroup getRenderTypeFast() {
|
||||
var hint = getRenderTypeFastHint();
|
||||
return hint == null ? RenderTypeGroup.EMPTY : getRenderType(hint);
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,8 @@ public abstract class SimpleUnbakedGeometry<T extends SimpleUnbakedGeometry<T>>
|
||||
public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function<Material, TextureAtlasSprite> spriteGetter, ModelState modelState) {
|
||||
TextureAtlasSprite particle = spriteGetter.apply(context.getMaterial("particle"));
|
||||
|
||||
var renderTypeHint = context.getRenderTypeHint();
|
||||
var renderTypes = renderTypeHint != null ? context.getRenderType(renderTypeHint) : RenderTypeGroup.EMPTY;
|
||||
IModelBuilder<?> builder = IModelBuilder.of(context.useAmbientOcclusion(), context.useBlockLight(), context.isGui3d(),
|
||||
context.getTransforms(), particle, renderTypes);
|
||||
context.getTransforms(), particle, context.getRenderType(), context.getRenderTypeFast());
|
||||
|
||||
addQuads(context, builder, baker, spriteGetter, modelState);
|
||||
|
||||
|
||||
+31
-1
@@ -51,6 +51,8 @@ public class StandaloneGeometryBakingContext implements IGeometryBakingContext {
|
||||
private final Transformation rootTransform;
|
||||
@Nullable
|
||||
private final ResourceLocation renderTypeHint;
|
||||
@Nullable
|
||||
private final ResourceLocation renderTypeFastHint;
|
||||
private final BiPredicate<String, Boolean> visibilityTest;
|
||||
|
||||
private StandaloneGeometryBakingContext(
|
||||
@@ -60,6 +62,18 @@ public class StandaloneGeometryBakingContext implements IGeometryBakingContext {
|
||||
ItemTransforms transforms, Transformation rootTransform,
|
||||
@Nullable ResourceLocation renderTypeHint,
|
||||
BiPredicate<String, Boolean> visibilityTest
|
||||
) {
|
||||
this(modelName, materialCheck, materialLookup, isGui3d, useBlockLight, useAmbientOcclusion, transforms, rootTransform, renderTypeHint, null, visibilityTest);
|
||||
}
|
||||
|
||||
private StandaloneGeometryBakingContext(
|
||||
ResourceLocation modelName, Predicate<String> materialCheck,
|
||||
Function<String, Material> materialLookup, boolean isGui3d,
|
||||
boolean useBlockLight, boolean useAmbientOcclusion,
|
||||
ItemTransforms transforms, Transformation rootTransform,
|
||||
@Nullable ResourceLocation renderTypeHint,
|
||||
@Nullable ResourceLocation renderTypeFastHint,
|
||||
BiPredicate<String, Boolean> visibilityTest
|
||||
) {
|
||||
this.modelName = modelName;
|
||||
this.materialCheck = materialCheck;
|
||||
@@ -70,6 +84,7 @@ public class StandaloneGeometryBakingContext implements IGeometryBakingContext {
|
||||
this.transforms = transforms;
|
||||
this.rootTransform = rootTransform;
|
||||
this.renderTypeHint = renderTypeHint;
|
||||
this.renderTypeFastHint = renderTypeFastHint;
|
||||
this.visibilityTest = visibilityTest;
|
||||
}
|
||||
|
||||
@@ -119,6 +134,12 @@ public class StandaloneGeometryBakingContext implements IGeometryBakingContext {
|
||||
return renderTypeHint;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ResourceLocation getRenderTypeFastHint() {
|
||||
return renderTypeFastHint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComponentVisible(String component, boolean fallback) {
|
||||
return visibilityTest.test(component, fallback);
|
||||
@@ -143,6 +164,8 @@ public class StandaloneGeometryBakingContext implements IGeometryBakingContext {
|
||||
private Transformation rootTransform = Transformation.identity();
|
||||
@Nullable
|
||||
private ResourceLocation renderTypeHint;
|
||||
@Nullable
|
||||
private ResourceLocation renderTypeFastHint;
|
||||
private BiPredicate<String, Boolean> visibilityTest = (c, def) -> def;
|
||||
|
||||
private Builder() { }
|
||||
@@ -156,6 +179,7 @@ public class StandaloneGeometryBakingContext implements IGeometryBakingContext {
|
||||
this.transforms = parent.getTransforms();
|
||||
this.rootTransform = parent.getRootTransform();
|
||||
this.renderTypeHint = parent.getRenderTypeHint();
|
||||
this.renderTypeFastHint = parent.getRenderTypeFastHint();
|
||||
this.visibilityTest = parent::isComponentVisible;
|
||||
}
|
||||
|
||||
@@ -205,6 +229,12 @@ public class StandaloneGeometryBakingContext implements IGeometryBakingContext {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withRenderTypeHint(ResourceLocation renderTypeHint, ResourceLocation renderTypeFastHint) {
|
||||
this.renderTypeHint = renderTypeHint;
|
||||
this.renderTypeFastHint = renderTypeFastHint;
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public Builder withVisibleComponents(Object2BooleanMap<String> parts) {
|
||||
this.visibilityTest = parts::getOrDefault;
|
||||
@@ -212,7 +242,7 @@ public class StandaloneGeometryBakingContext implements IGeometryBakingContext {
|
||||
}
|
||||
|
||||
public StandaloneGeometryBakingContext build(ResourceLocation modelName) {
|
||||
return new StandaloneGeometryBakingContext(modelName, materialCheck, materialLookup, isGui3d, useBlockLight, useAmbientOcclusion, transforms, rootTransform, renderTypeHint, visibilityTest);
|
||||
return new StandaloneGeometryBakingContext(modelName, materialCheck, materialLookup, isGui3d, useBlockLight, useAmbientOcclusion, transforms, rootTransform, renderTypeHint, renderTypeFastHint, visibilityTest);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,6 +98,8 @@ public class ForgeConfig {
|
||||
|
||||
public final BooleanValue showLoadWarnings;
|
||||
|
||||
public final BooleanValue allowMipmapLowering;
|
||||
|
||||
Client(ForgeConfigSpec.Builder builder) {
|
||||
builder.comment("Client only settings, mostly things related to rendering")
|
||||
.push("client");
|
||||
@@ -112,6 +114,11 @@ public class ForgeConfig {
|
||||
.translation("forge.configgui.showLoadWarnings")
|
||||
.define("showLoadWarnings", true);
|
||||
|
||||
allowMipmapLowering = builder
|
||||
.comment("When enabled, Forge will allow mipmaps to be lowered in real-time. This is the default behavior in vanilla. Use this if you experience issues with resource packs that use textures lower than 8x8.")
|
||||
.translation("forge.configgui.allowMipmapLowering")
|
||||
.define("allowMipmapLowering", false);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
|
||||
@@ -120,6 +127,10 @@ public class ForgeConfig {
|
||||
public final boolean showLoadWarnings() {
|
||||
return clientSpec.isLoaded() ? showLoadWarnings.get() : showLoadWarnings.getDefault();
|
||||
}
|
||||
|
||||
public final boolean allowMipmapLowering() {
|
||||
return clientSpec.isLoaded() ? allowMipmapLowering.get() : allowMipmapLowering.getDefault();
|
||||
}
|
||||
}
|
||||
|
||||
static final ForgeConfigSpec clientSpec;
|
||||
|
||||
@@ -94,7 +94,7 @@ public class ForgeInternalHandler {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onResourceReload(AddReloadListenerEvent event) {
|
||||
INSTANCE = new LootModifierManager(event.getRegistryAccess());
|
||||
INSTANCE = new LootModifierManager(event.getRegistries());
|
||||
event.addListener(INSTANCE);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,49 +5,130 @@
|
||||
|
||||
package net.minecraftforge.common.extensions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.gametest.framework.GameTestAssertException;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.ConnectionProtocol;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import net.minecraft.network.protocol.game.GameProtocols;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.CommonListenerCookie;
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraftforge.common.ForgeI18n;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
|
||||
public interface IForgeGameTestHelper {
|
||||
private GameTestHelper self() {
|
||||
return (GameTestHelper)this;
|
||||
return (GameTestHelper) this;
|
||||
}
|
||||
|
||||
default void say(String message) {
|
||||
this.say(message, Style.EMPTY);
|
||||
}
|
||||
|
||||
default void say(String message, Style style) {
|
||||
this.say(this.getMessage(message).withStyle(style));
|
||||
}
|
||||
|
||||
default void say(String message, ChatFormatting style) {
|
||||
this.say(this.getMessage(message).withStyle(style));
|
||||
}
|
||||
|
||||
default void say(String message, int color) {
|
||||
this.say(this.getMessage(message).withColor(color));
|
||||
}
|
||||
|
||||
default void say(Component component) {
|
||||
this.self().getLevel().players().forEach(p -> p.sendSystemMessage(component));
|
||||
}
|
||||
|
||||
private MutableComponent getMessage(String message) {
|
||||
return ForgeI18n.getPattern(message) != null ? Component.translatable(message) : Component.literal(message);
|
||||
}
|
||||
|
||||
default void assertTrue(boolean value, Supplier<String> message) {
|
||||
if (!value)
|
||||
throw new GameTestAssertException(message.get());
|
||||
if (!value)
|
||||
throw new GameTestAssertException(message.get());
|
||||
}
|
||||
|
||||
default void assertFalse(boolean value, Supplier<String> message) {
|
||||
if (value)
|
||||
throw new GameTestAssertException(message.get());
|
||||
if (value)
|
||||
throw new GameTestAssertException(message.get());
|
||||
}
|
||||
|
||||
default <N> void assertValueEqual(N expected, N actual, String name, String message) {
|
||||
this.assertValueEqual(expected, actual, name, () -> message);
|
||||
}
|
||||
|
||||
default <N> void assertValueEqual(N expected, N actual, String name, Supplier<String> message) {
|
||||
if (!Objects.equals(expected, actual))
|
||||
throw new GameTestAssertException("%s -- Expected %s to be %s, but was %s".formatted(message.get(), name, expected, actual));
|
||||
}
|
||||
|
||||
default <N> void assertValueEqual(N[] expected, N[] actual, String name, String message) {
|
||||
this.assertValueEqual(expected, actual, name, () -> message);
|
||||
}
|
||||
|
||||
default <N> void assertValueEqual(N[] expected, N[] actual, String name, Supplier<String> message) {
|
||||
if (!Objects.deepEquals(expected, actual))
|
||||
throw new GameTestAssertException("%s -- Expected %s to be %s, but was %s".formatted(message.get(), name, Arrays.toString(expected), Arrays.toString(actual)));
|
||||
}
|
||||
|
||||
default <N> void assertValueNotEqual(N expected, N actual, String name, String message) {
|
||||
this.assertValueNotEqual(expected, actual, name, () -> message);
|
||||
}
|
||||
|
||||
default <N> void assertValueNotEqual(N expected, N actual, String name, Supplier<String> message) {
|
||||
if (Objects.equals(expected, actual))
|
||||
throw new GameTestAssertException("%s -- Expected %s to NOT be %s, but was".formatted(message.get(), name, expected));
|
||||
}
|
||||
|
||||
default <N> void assertValueNotEqual(N[] expected, N[] actual, String name, String message) {
|
||||
this.assertValueNotEqual(expected, actual, name, () -> message);
|
||||
}
|
||||
|
||||
default <N> void assertValueNotEqual(N[] expected, N[] actual, String name, Supplier<String> message) {
|
||||
if (!Objects.deepEquals(expected, actual))
|
||||
throw new GameTestAssertException("%s -- Expected %s to NOT be %s, but was".formatted(message.get(), name, Arrays.toString(expected)));
|
||||
}
|
||||
|
||||
default <E> Registry<E> registryLookup(ResourceKey<? extends Registry<? extends E>> registryKey) {
|
||||
return this.self().getLevel().registryAccess().lookupOrThrow(registryKey);
|
||||
}
|
||||
|
||||
default ServerPlayer makeMockServerPlayer() {
|
||||
var level = self().getLevel();
|
||||
var cookie = CommonListenerCookie.createInitial(new GameProfile(UUID.randomUUID(), "test-mock-player"), false);
|
||||
var player = new ServerPlayer(level.getServer(), level, cookie.gameProfile(), cookie.clientInformation()) {
|
||||
public boolean isSpectator() {
|
||||
return false;
|
||||
}
|
||||
public boolean isSpectator() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isCreative() {
|
||||
return true;
|
||||
}
|
||||
public boolean isCreative() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
var connection = new Connection(PacketFlow.SERVERBOUND);
|
||||
@SuppressWarnings("unused") // The constructor has side effects
|
||||
@@ -59,4 +140,232 @@ public interface IForgeGameTestHelper {
|
||||
connection.setupInboundProtocol(info, listener);
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an event listener that will be unregistered when the test is finished running.
|
||||
*/
|
||||
default <E extends Event> void addEventListener(Consumer<E> consumer) {
|
||||
MinecraftForge.EVENT_BUS.addListener(consumer);
|
||||
self().addCleanup(success -> MinecraftForge.EVENT_BUS.unregister(consumer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an event listener that will be unregistered when the test is finished running.
|
||||
*/
|
||||
default void registerEventListener(Object handler) {
|
||||
MinecraftForge.EVENT_BUS.register(handler);
|
||||
self().addCleanup(success -> MinecraftForge.EVENT_BUS.unregister(handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a floor of stone blocks at the bottom of the test area.
|
||||
*/
|
||||
default void makeFloor() {
|
||||
makeFloor(Blocks.STONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a floor of the specified block under the test area.
|
||||
*/
|
||||
default void makeFloor(Block block) {
|
||||
makeFloor(block, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a floor of the specified block at the specified height.
|
||||
*/
|
||||
default void makeFloor(Block block, int height) {
|
||||
var bounds = self().getBounds();
|
||||
var pos = new BlockPos.MutableBlockPos();
|
||||
for (int x = 0; x < (int) bounds.getXsize(); x++) {
|
||||
for (int y = 0; y < (int) bounds.getZsize(); y++) {
|
||||
pos.set(x, height, y);
|
||||
if (self().getBlockState(pos).is(Blocks.AIR))
|
||||
self().setBlock(pos, block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default <T> Flag<T> flag(String name) {
|
||||
return new Flag<>(name);
|
||||
}
|
||||
|
||||
default IntFlag intFlag(String name) {
|
||||
return new IntFlag(name);
|
||||
}
|
||||
|
||||
default IntFlag intFlag(String name, int value) {
|
||||
return this.intFlag(name, (long) value);
|
||||
}
|
||||
|
||||
default IntFlag intFlag(String name, long value) {
|
||||
return Util.make(new IntFlag(name), flag -> flag.set(value));
|
||||
}
|
||||
|
||||
default BoolFlag boolFlag(String name) {
|
||||
return new BoolFlag(name);
|
||||
}
|
||||
|
||||
public static class Flag<T> {
|
||||
private final String name;
|
||||
@Nullable
|
||||
protected T value = null;
|
||||
|
||||
public Flag(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void set(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T get() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public void assertUnset() {
|
||||
this.assertUnset((Supplier<String>) null);
|
||||
}
|
||||
|
||||
public void assertUnset(String message) {
|
||||
this.assertUnset(message != null ? () -> message : null);
|
||||
}
|
||||
|
||||
public void assertUnset(Supplier<String> message) {
|
||||
if (this.value != null) {
|
||||
String s = message != null ? message.get() + " -- " : "";
|
||||
throw new GameTestAssertException(s + "Expected " + name + " to be null, but was " + this.value);
|
||||
}
|
||||
}
|
||||
|
||||
public void assertSet() {
|
||||
this.assertSet((Supplier<String>) null);
|
||||
}
|
||||
|
||||
public void assertSet(String message) {
|
||||
this.assertSet(message != null ? () -> message : null);
|
||||
}
|
||||
|
||||
public void assertSet(Supplier<String> message) {
|
||||
if (this.value == null) {
|
||||
String s = message != null ? message.get() + " -- " : "";
|
||||
throw new GameTestAssertException(s + "Flag " + name + " was never set");
|
||||
}
|
||||
}
|
||||
|
||||
public void assertEquals(T expected) {
|
||||
this.assertEquals(expected, (Supplier<String>) null);
|
||||
}
|
||||
|
||||
public void assertEquals(T expected, String message) {
|
||||
this.assertEquals(expected, message != null ? () -> message : null);
|
||||
}
|
||||
|
||||
public void assertEquals(T expected, Supplier<String> message) {
|
||||
assertSet(message);
|
||||
if (expected != null && !expected.equals(this.value)) {
|
||||
String s = message != null ? message.get() + " -- " : "";
|
||||
throw new GameTestAssertException(s + "Expected " + name + " to be " + expected + ", but was " + this.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class IntFlag extends Flag<Long> {
|
||||
public IntFlag(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public void set(long value) {
|
||||
super.set(value);
|
||||
}
|
||||
|
||||
public void increment() {
|
||||
this.decrement(1L);
|
||||
}
|
||||
|
||||
public void increment(int amount) {
|
||||
this.decrement((long) amount);
|
||||
}
|
||||
|
||||
public void increment(long amount) {
|
||||
if (this.value != null)
|
||||
this.set(this.value + amount);
|
||||
}
|
||||
|
||||
public void decrement() {
|
||||
this.decrement(1L);
|
||||
}
|
||||
|
||||
public void decrement(int amount) {
|
||||
this.decrement((long) amount);
|
||||
}
|
||||
|
||||
public void decrement(long amount) {
|
||||
if (this.value != null)
|
||||
this.set(this.value - amount);
|
||||
}
|
||||
|
||||
public byte getByte() {
|
||||
return this.value == null ? -1 : this.value.byteValue();
|
||||
}
|
||||
|
||||
public int getInt() {
|
||||
return this.value == null ? -1 : this.value.intValue();
|
||||
}
|
||||
|
||||
public long getLong() {
|
||||
return this.value == null ? -1 : this.value.longValue();
|
||||
}
|
||||
|
||||
public void assertEquals(int expected) {
|
||||
super.assertEquals((long) expected);
|
||||
}
|
||||
|
||||
public void assertEquals(long expected) {
|
||||
super.assertEquals(expected);
|
||||
}
|
||||
|
||||
public void assertEquals(int expected, String message) {
|
||||
super.assertEquals((long) expected, message);
|
||||
}
|
||||
|
||||
public void assertEquals(long expected, String message) {
|
||||
super.assertEquals(expected, message);
|
||||
}
|
||||
|
||||
public void assertEquals(int expected, Supplier<String> message) {
|
||||
super.assertEquals((long) expected, message);
|
||||
}
|
||||
|
||||
public void assertEquals(long expected, Supplier<String> message) {
|
||||
super.assertEquals(expected, message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class BoolFlag extends Flag<Boolean> {
|
||||
public BoolFlag(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public void set(boolean value) {
|
||||
super.set(value);
|
||||
}
|
||||
|
||||
public boolean getBool() {
|
||||
return this.value != null && this.value;
|
||||
}
|
||||
|
||||
public void assertEquals(boolean expected) {
|
||||
super.assertEquals(expected);
|
||||
}
|
||||
|
||||
public void assertEquals(boolean expected, String message) {
|
||||
super.assertEquals(expected, message);
|
||||
}
|
||||
|
||||
public void assertEquals(boolean expected, Supplier<String> message) {
|
||||
super.assertEquals(expected, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ package net.minecraftforge.common.extensions;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.item.*;
|
||||
@@ -31,6 +33,7 @@ import net.minecraft.world.level.Level;
|
||||
import net.minecraftforge.common.ForgeHooks;
|
||||
import net.minecraftforge.common.ToolAction;
|
||||
import net.minecraftforge.common.ToolActions;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.registries.IForgeRegistry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -459,7 +462,7 @@ public interface IForgeItem {
|
||||
* @return True if this ItemStack can disable the shield in question.
|
||||
*/
|
||||
default boolean canDisableShield(ItemStack stack, ItemStack shield, LivingEntity entity, LivingEntity attacker) {
|
||||
return this instanceof AxeItem;
|
||||
return attacker.canDisableShield();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -481,6 +484,28 @@ public interface IForgeItem {
|
||||
*/
|
||||
default void onHorseArmorTick(ItemStack stack, Level level, Mob horse) { }
|
||||
|
||||
/**
|
||||
* Called when this item is to be damaged, such as use a tool being used or a shield blocking damage. The damage
|
||||
* parameter has not yet been processed, so enchantments are not taken into account.
|
||||
*
|
||||
* @param stack The processed amount of damage the item will take
|
||||
* @param damage The amount of damage the item will take before processing
|
||||
* @param level The level where the damage is taking place
|
||||
* @param player The player holding the item
|
||||
* @param canBreak If the item can break from this damage instance ({@code true} if this is called from
|
||||
* {@link ItemStack#hurtAndBreak(int, ServerLevel, ServerPlayer, Consumer)}, {@code false} if from
|
||||
* {@link ItemStack#hurtWithoutBreaking(int, Player)})
|
||||
* @param onBroken The callback for when an item is broken (use this if you plan on cancelling damage that will
|
||||
* break an item)
|
||||
* @return The amount of damage the item should take, after processing
|
||||
*
|
||||
* @apiNote If the item stack is not {@linkplain ItemStack#isDamageableItem() damageable} or the player
|
||||
* {@linkplain Player#hasInfiniteMaterials() has infinite materials}, this method will not be called.
|
||||
*/
|
||||
default int damageItem(ItemStack stack, int damage, ServerLevel level, @Nullable ServerPlayer player, boolean canBreak, Consumer<Item> onBroken) {
|
||||
return damage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an item entity for this stack is destroyed. Note: The {@link ItemStack} can be retrieved from the item entity.
|
||||
*
|
||||
@@ -587,4 +612,12 @@ public interface IForgeItem {
|
||||
default boolean canGrindstoneRepair(ItemStack stack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Default Capability Provider for this item, if any, accounting for the given itemstack.
|
||||
*/
|
||||
@Nullable
|
||||
default ICapabilityProvider getCapabilityProvider(ItemStack stack) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
package net.minecraftforge.common.extensions;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.monster.Monster;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
@@ -32,6 +34,8 @@ import net.minecraftforge.common.ToolActions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/*
|
||||
* Extension added to ItemStack that bounces to ItemSack sensitive Item methods. Typically this is just for convince.
|
||||
*/
|
||||
@@ -377,6 +381,28 @@ public interface IForgeItemStack {
|
||||
return self().getItem().getSweepHitBox(self(), player, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this item is to be damaged, such as use a tool being used or a shield blocking damage. The damage
|
||||
* parameter has not yet been processed, so enchantments are not taken into account.
|
||||
*
|
||||
* @param damage The amount of damage the item will take before processing
|
||||
* @param level The level where the damage is taking place
|
||||
* @param player The player holding the item
|
||||
* @param canBreak If the item can break from this damage instance ({@code true} if this is called from
|
||||
* {@link ItemStack#hurtAndBreak(int, ServerLevel, ServerPlayer, Consumer)}, {@code false} if from
|
||||
* {@link ItemStack#hurtWithoutBreaking(int, Player)})
|
||||
* @param onBroken The callback for when an item is broken (use this if you plan on cancelling damage that will
|
||||
* break an item)
|
||||
* @return The amount of damage the item should take
|
||||
*
|
||||
* @apiNote If the item stack is not {@linkplain ItemStack#isDamageableItem() damageable} or the player
|
||||
* {@linkplain Player#hasInfiniteMaterials() has infinite materials}, this method will not be called.
|
||||
* @see IForgeItem#damageItem(ItemStack, int, ServerLevel, ServerPlayer, boolean, Consumer)
|
||||
*/
|
||||
default int damageItem(int damage, ServerLevel level, @Nullable ServerPlayer player, boolean canBreak, Consumer<Item> onBroken) {
|
||||
return self().getItem().damageItem(self(), damage, level, player, canBreak, onBroken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an item entity for this stack is destroyed. Note: The {@link ItemStack} can be retrieved from the item entity.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.extensions;
|
||||
|
||||
import net.minecraft.world.level.storage.LevelSummary;
|
||||
|
||||
public interface IForgeLevelSummary {
|
||||
private LevelSummary self() {
|
||||
return (LevelSummary) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Forge lifecycle of this level is experimental. This is used to render the experimental warning
|
||||
* tooltip on the level select screen.
|
||||
*
|
||||
* @return {@code true} if the level is experimental
|
||||
*/
|
||||
default boolean isLifecycleExperimental() {
|
||||
// NOTE: Because CorruptedLevelSummary and SymlinkLevelSummary can have null settings, we need to check it
|
||||
var settings = this.self().getSettings();
|
||||
|
||||
return settings != null && settings.getLifecycle().equals(com.mojang.serialization.Lifecycle.experimental());
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,10 @@
|
||||
package net.minecraftforge.event;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.LayeredRegistryAccess;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.server.RegistryLayer;
|
||||
import net.minecraft.server.ReloadableServerResources;
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
@@ -29,10 +32,31 @@ import java.util.concurrent.Executor;
|
||||
public class AddReloadListenerEvent extends Event {
|
||||
private final List<PreparableReloadListener> listeners = new ArrayList<>();
|
||||
private final ReloadableServerResources serverResources;
|
||||
|
||||
private final HolderLookup.Provider registries;
|
||||
@Deprecated(forRemoval = true, since = "1.21.4")
|
||||
private final RegistryAccess registryAccess;
|
||||
|
||||
public AddReloadListenerEvent(ReloadableServerResources serverResources, RegistryAccess registryAccess) {
|
||||
/** @deprecated Does not provide additional context. Use {@link #AddReloadListenerEvent(ReloadableServerResources, HolderLookup.Provider, RegistryAccess)} instead. */
|
||||
@Deprecated(forRemoval = true, since = "1.21.4")
|
||||
public AddReloadListenerEvent(
|
||||
ReloadableServerResources serverResources,
|
||||
RegistryAccess registryAccess
|
||||
) {
|
||||
this(
|
||||
serverResources,
|
||||
registryAccess,
|
||||
registryAccess
|
||||
);
|
||||
}
|
||||
|
||||
public AddReloadListenerEvent(
|
||||
ReloadableServerResources serverResources,
|
||||
HolderLookup.Provider registries,
|
||||
@Deprecated(forRemoval = true, since = "1.21.4") RegistryAccess registryAccess
|
||||
) {
|
||||
this.serverResources = serverResources;
|
||||
this.registries = registries;
|
||||
this.registryAccess = registryAccess;
|
||||
}
|
||||
|
||||
@@ -62,21 +86,27 @@ public class AddReloadListenerEvent extends Event {
|
||||
return serverResources.getConditionContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A holder lookup provider containing the registries with updated tags.
|
||||
*
|
||||
* @see net.minecraft.server.ReloadableServerRegistries.LoadResult#lookupWithUpdatedTags()
|
||||
*/
|
||||
public HolderLookup.Provider getRegistries() {
|
||||
return registries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to the loaded registries associated with these server resources.
|
||||
* All built-in and dynamic registries are loaded and frozen by this point.
|
||||
* @return The RegistryAccess context for the currently active reload.
|
||||
* @deprecated Does not contain updated tags. Use {@link #getRegistries()} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.21.4")
|
||||
public RegistryAccess getRegistryAccess() {
|
||||
return registryAccess;
|
||||
}
|
||||
|
||||
private static class WrappedStateAwareListener implements PreparableReloadListener {
|
||||
private final PreparableReloadListener wrapped;
|
||||
|
||||
private WrappedStateAwareListener(final PreparableReloadListener wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
private record WrappedStateAwareListener(PreparableReloadListener wrapped) implements PreparableReloadListener {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> reload(final PreparationBarrier stage, final ResourceManager resourceManager, final Executor backgroundExecutor, final Executor gameExecutor) {
|
||||
@@ -85,5 +115,10 @@ public class AddReloadListenerEvent extends Event {
|
||||
else
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return wrapped.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,10 @@ import java.util.Optional;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.LayeredRegistryAccess;
|
||||
import net.minecraft.core.component.DataComponentMap;
|
||||
import net.minecraft.server.RegistryLayer;
|
||||
import net.minecraft.world.entity.*;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.chunk.storage.SerializableChunkData;
|
||||
@@ -712,10 +715,15 @@ public final class ForgeEventFactory {
|
||||
return fire(new SleepFinishedTimeEvent(level, newTime, minTime)).getNewTime();
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true, since = "1.21.4")
|
||||
public static List<PreparableReloadListener> onResourceReload(ReloadableServerResources serverResources, RegistryAccess registryAccess) {
|
||||
return fire(new AddReloadListenerEvent(serverResources, registryAccess)).getListeners();
|
||||
}
|
||||
|
||||
public static List<PreparableReloadListener> onResourceReload(ReloadableServerResources serverResources, HolderLookup.Provider lookupProvider, @Deprecated(forRemoval = true, since = "1.21.4") RegistryAccess registryAccess) {
|
||||
return fire(new AddReloadListenerEvent(serverResources, lookupProvider, registryAccess)).getListeners();
|
||||
}
|
||||
|
||||
public static void onCommandRegister(CommandDispatcher<CommandSourceStack> dispatcher, Commands.CommandSelection environment, CommandBuildContext context) {
|
||||
post(new RegisterCommandsEvent(dispatcher, environment, context));
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package net.minecraftforge.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.ObjIntConsumer;
|
||||
@@ -17,6 +18,9 @@ import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.versions.forge.ForgeVersion;
|
||||
import net.minecraftforge.versions.mcp.MCPVersion;
|
||||
|
||||
/**
|
||||
* Controls the title screen brandings for the game.
|
||||
*/
|
||||
public final class BrandingControl {
|
||||
private BrandingControl() {}
|
||||
|
||||
@@ -25,13 +29,29 @@ public final class BrandingControl {
|
||||
|
||||
private static void computeBranding() {
|
||||
if (brandings == null) {
|
||||
brandings = List.of(
|
||||
"Forge " + ForgeVersion.getVersion(),
|
||||
"Minecraft " + MCPVersion.getMCVersion(),
|
||||
"MCP " + MCPVersion.getMCPVersion(),
|
||||
ForgeI18n.parseMessage("fml.menu.loadingmods", ModList.get().size())
|
||||
);
|
||||
brandingsNoMC = brandings.subList(1, brandings.size());
|
||||
var list = new ArrayList<String>();
|
||||
|
||||
// Forge
|
||||
var forge = ForgeVersion.getVersion().split("-", 2);
|
||||
|
||||
// I don't want to use VersionChecker to check for this, so I'm just going to use the version string.
|
||||
// We only have Forge Betas on the "XX.0.XX" versions anyways.
|
||||
boolean beta = "0".equals(forge[0].split("\\.")[1]);
|
||||
var name = beta ? "§eForge Beta§f " : "Forge ";
|
||||
list.add(name + forge[0] + " (" + ForgeI18n.parseMessage("fml.menu.loadingmods", ModList.get().size()) + ")");
|
||||
|
||||
// Extra forge version info (like branch)
|
||||
if (forge.length > 1) list.add("Branch " + forge[1]);
|
||||
|
||||
// TODO [Forge][FML] When FML is rewritten, add its version here behind a config value to show it (debugBrandingVersions)
|
||||
// this is how to check if we are in ForgeDev:
|
||||
// FMLLoader.launcherHandlerName().startsWith("forge_dev")
|
||||
|
||||
// Minecraft
|
||||
list.add("Minecraft " + MCPVersion.getMCVersion());
|
||||
|
||||
brandings = List.copyOf(list);
|
||||
brandingsNoMC = brandings.subList(0, brandings.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +63,11 @@ public final class BrandingControl {
|
||||
return reverse ? brandingsNoMC.reversed() : brandingsNoMC;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the branding lines to display over the copyright line. This is usually a message when Forge has an update.
|
||||
*
|
||||
* @return The branding lines to display
|
||||
*/
|
||||
public static List<String> getOverCopyrightBrandings() {
|
||||
final class LazyInit {
|
||||
private static final List<String> INSTANCE = ForgeHooksClient.forgeStatusLine == null
|
||||
@@ -55,22 +80,46 @@ public final class BrandingControl {
|
||||
return LazyInit.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over each branding line, passing the line and its index to the consumer.
|
||||
*
|
||||
* @param includeMC Whether to include the Minecraft version line
|
||||
* @param reverse Whether to iterate in reverse order
|
||||
* @param lineConsumer The consumer to accept each line and its index
|
||||
*/
|
||||
public static void forEachLine(boolean includeMC, boolean reverse, ObjIntConsumer<String> lineConsumer) {
|
||||
var brandings = getBrandings(includeMC, reverse);
|
||||
for (int idx = 0; idx < brandings.size(); idx++)
|
||||
lineConsumer.accept(brandings.get(idx), idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over each branding line that should be displayed above the copyright line, passing the line and its
|
||||
* index to the consumer.
|
||||
*
|
||||
* @param lineConsumer The consumer to accept each line and its index
|
||||
*/
|
||||
public static void forEachAboveCopyrightLine(ObjIntConsumer<String> lineConsumer) {
|
||||
var overCopyrightBrandings = getOverCopyrightBrandings();
|
||||
for (int idx = 0; idx < overCopyrightBrandings.size(); idx++)
|
||||
lineConsumer.accept(overCopyrightBrandings.get(idx), idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the branding to use in place of the default {@code "vanilla"} branding.
|
||||
*
|
||||
* @return The branding to use
|
||||
*/
|
||||
public static String getBranding() {
|
||||
return "forge";
|
||||
}
|
||||
|
||||
/**
|
||||
* The reload listener for the branding control. On reload, the brandings are recomputed in
|
||||
* {@link #computeBranding()}.
|
||||
*
|
||||
* @return The reload listener
|
||||
*/
|
||||
public static ResourceManagerReloadListener resourceManagerReloadListener() {
|
||||
return BrandingControl::onResourceManagerReload;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,19 @@ import java.lang.annotation.Target;
|
||||
/**
|
||||
* ObjectHolder can be used to automatically populate public static final fields with entries
|
||||
* from the registry. These values can then be referred within mod code directly.
|
||||
* @deprecated Use {@link DeferredRegister} or {@link RegistryObject} instead. Refer to the MDK for more detailed examples.
|
||||
* <br>
|
||||
* Example usage of alternatives:
|
||||
* <pre>{@code
|
||||
* // To register something
|
||||
* public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
|
||||
* public static final RegistryObject<Item> EXAMPLE_ITEM = ITEMS.register("example_item", () -> new Item(new Item.Properties()));
|
||||
*
|
||||
* // To get a RegistryObject that you didn't register yourself:
|
||||
* public static final RegistryObject<Block> DIRT = RegistryObject.create(ResourceLocation.withDefaultNamespace("dirt"), ForgeRegistries.BLOCKS);
|
||||
* }</pre>
|
||||
*/
|
||||
@Deprecated(since = "1.21.4", forRemoval = true)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ObjectHolder
|
||||
|
||||
@@ -89,6 +89,9 @@ public class ObjectHolderRegistry
|
||||
.filter(a -> OBJECT_HOLDER.equals(a.annotationType()) || MOD.equals(a.annotationType()))
|
||||
.toList();
|
||||
|
||||
if (annotations.stream().noneMatch(a -> OBJECT_HOLDER.equals(a.annotationType())))
|
||||
return; // No object holders found, skip the rest of the processing
|
||||
|
||||
Map<Type, String> classModIds = Maps.newHashMap();
|
||||
Map<Type, Class<?>> classCache = Maps.newHashMap();
|
||||
|
||||
|
||||
@@ -141,8 +141,6 @@
|
||||
"commands.config.getwithtype": "Config for %s of type %s found at %s",
|
||||
"commands.config.noconfig": "Config for %s of type %s not found",
|
||||
|
||||
"forge.update.beta.1": "%sWARNING: %sForge Beta",
|
||||
"forge.update.beta.2": "Major issues may arise, verify before reporting.",
|
||||
"forge.update.newversion": "New Forge version available: %s",
|
||||
"forge.menu.updatescreen.title": "Mod Update",
|
||||
|
||||
@@ -185,6 +183,8 @@
|
||||
"forge.configgui.selectiveResourceReloadEnabled": "Enable Selective Resource Loading",
|
||||
"forge.configgui.showLoadWarnings.tooltip": "When enabled, Forge will show any warnings that occurred during loading.",
|
||||
"forge.configgui.showLoadWarnings": "Show Load Warnings",
|
||||
"forge.configgui.allowMipmapLowering.tooltip": "When enabled, Forge will allow mipmaps to be lowered in real-time. This is the default behavior in vanilla. Use this if you experience issues with resource packs that use textures lower than 8x8.",
|
||||
"forge.configgui.allowMipmapLowering": "Allow mipmap lowering",
|
||||
|
||||
"forge.configgui.disableVersionCheck.tooltip": "Set to true to disable Forge version check mechanics. Forge queries a small json file on our server for version information. For more details see the ForgeVersion class in our github.",
|
||||
"forge.configgui.disableVersionCheck": "Disable Forge Version Check",
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
var ASMAPI = Java.type('net.minecraftforge.coremod.api.ASMAPI')
|
||||
|
||||
function initializeCoreMod() {
|
||||
|
||||
@@ -1,25 +1,144 @@
|
||||
[
|
||||
"net/minecraft/server/commands/RaidCommand",
|
||||
"net/minecraft/server/commands/SummonCommand",
|
||||
"net/minecraft/world/entity/EntityType",
|
||||
"net/minecraft/world/entity/ai/village/VillageSiege",
|
||||
"net/minecraft/world/entity/animal/frog/Tadpole",
|
||||
"net/minecraft/world/entity/animal/horse/SkeletonTrapGoal",
|
||||
"net/minecraft/world/entity/monster/Evoker$EvokerSummonSpellGoal",
|
||||
"net/minecraft/world/entity/monster/Spider",
|
||||
"net/minecraft/world/entity/monster/Strider",
|
||||
"net/minecraft/world/entity/monster/Zombie",
|
||||
"net/minecraft/world/entity/monster/ZombieVillager",
|
||||
"net/minecraft/world/entity/npc/CatSpawner",
|
||||
"net/minecraft/world/entity/npc/Villager",
|
||||
"net/minecraft/world/entity/raid/Raid",
|
||||
"net/minecraft/world/level/NaturalSpawner",
|
||||
"net/minecraft/world/level/block/entity/trialspawner/TrialSpawner",
|
||||
"net/minecraft/world/level/levelgen/PatrolSpawner",
|
||||
"net/minecraft/world/level/levelgen/PhantomSpawner",
|
||||
"net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces$OceanMonumentPiece",
|
||||
"net/minecraft/world/level/levelgen/structure/structures/OceanRuinPieces$OceanRuinPiece",
|
||||
"net/minecraft/world/level/levelgen/structure/structures/SwampHutPiece",
|
||||
"net/minecraft/world/level/levelgen/structure/structures/WoodlandMansionPieces$WoodlandMansionPiece",
|
||||
"net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate"
|
||||
{
|
||||
"class": "net/minecraft/server/commands/RaidCommand",
|
||||
"methods": [
|
||||
"spawnLeader(Lnet/minecraft/commands/CommandSourceStack;)I"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/server/commands/SummonCommand",
|
||||
"methods": [
|
||||
"createEntity(Lnet/minecraft/commands/CommandSourceStack;Lnet/minecraft/core/Holder$Reference;Lnet/minecraft/world/phys/Vec3;Lnet/minecraft/nbt/CompoundTag;Z)Lnet/minecraft/world/entity/Entity;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/EntityType",
|
||||
"methods": [
|
||||
"create(Lnet/minecraft/server/level/ServerLevel;Ljava/util/function/Consumer;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/entity/EntitySpawnReason;ZZ)Lnet/minecraft/world/entity/Entity;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/ai/village/VillageSiege",
|
||||
"methods": [
|
||||
"trySpawn(Lnet/minecraft/server/level/ServerLevel;)V"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/animal/frog/Tadpole",
|
||||
"methods": [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/animal/horse/SkeletonTrapGoal",
|
||||
"methods": [
|
||||
"createHorse(Lnet/minecraft/world/DifficultyInstance;)Lnet/minecraft/world/entity/animal/horse/AbstractHorse;",
|
||||
"createSkeleton(Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/animal/horse/AbstractHorse;)Lnet/minecraft/world/entity/monster/Skeleton;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/monster/Evoker$EvokerSummonSpellGoal",
|
||||
"methods": [
|
||||
"performSpellCasting()V"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/monster/Spider",
|
||||
"methods": [
|
||||
"finalizeSpawn(Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/EntitySpawnReason;Lnet/minecraft/world/entity/SpawnGroupData;)Lnet/minecraft/world/entity/SpawnGroupData;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/monster/Strider",
|
||||
"methods": [
|
||||
"spawnJockey(Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/Mob;Lnet/minecraft/world/entity/SpawnGroupData;)Lnet/minecraft/world/entity/SpawnGroupData;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/monster/Zombie",
|
||||
"methods": [
|
||||
"hurtServer(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;F)Z",
|
||||
"finalizeSpawn(Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/EntitySpawnReason;Lnet/minecraft/world/entity/SpawnGroupData;)Lnet/minecraft/world/entity/SpawnGroupData;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/monster/ZombieVillager",
|
||||
"methods": [
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/npc/CatSpawner",
|
||||
"methods": [
|
||||
"spawnCat(Lnet/minecraft/core/BlockPos;Lnet/minecraft/server/level/ServerLevel;)I"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/npc/Villager",
|
||||
"methods": [
|
||||
"getBreedOffspring(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/entity/AgeableMob;)Lnet/minecraft/world/entity/npc/Villager;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/entity/raid/Raid",
|
||||
"methods": [
|
||||
"joinRaid(ILnet/minecraft/world/entity/raid/Raider;Lnet/minecraft/core/BlockPos;Z)V"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/level/NaturalSpawner",
|
||||
"methods": [
|
||||
"spawnCategoryForPosition(Lnet/minecraft/world/entity/MobCategory;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/chunk/ChunkAccess;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/NaturalSpawner$SpawnPredicate;Lnet/minecraft/world/level/NaturalSpawner$AfterSpawnCallback;)V",
|
||||
"spawnMobsForChunkGeneration(Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/core/Holder;Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/util/RandomSource;)V"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/level/block/entity/trialspawner/TrialSpawner",
|
||||
"methods": [
|
||||
"spawnMob(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;)Ljava/util/Optional;"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/level/levelgen/PatrolSpawner",
|
||||
"methods": [
|
||||
"spawnPatrolMember(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;Lnet/minecraft/util/RandomSource;Z)Z"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/level/levelgen/PhantomSpawner",
|
||||
"methods": [
|
||||
"tick(Lnet/minecraft/server/level/ServerLevel;ZZ)I"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces$OceanMonumentPiece",
|
||||
"methods": [
|
||||
"spawnElder(Lnet/minecraft/world/level/WorldGenLevel;Lnet/minecraft/world/level/levelgen/structure/BoundingBox;III)V"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/level/levelgen/structure/structures/OceanRuinPieces$OceanRuinPiece",
|
||||
"methods": [
|
||||
"handleDataMarker(Ljava/lang/String;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/util/RandomSource;Lnet/minecraft/world/level/levelgen/structure/BoundingBox;)V"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/level/levelgen/structure/structures/SwampHutPiece",
|
||||
"methods": [
|
||||
"postProcess(Lnet/minecraft/world/level/WorldGenLevel;Lnet/minecraft/world/level/StructureManager;Lnet/minecraft/world/level/chunk/ChunkGenerator;Lnet/minecraft/util/RandomSource;Lnet/minecraft/world/level/levelgen/structure/BoundingBox;Lnet/minecraft/world/level/ChunkPos;Lnet/minecraft/core/BlockPos;)V",
|
||||
"spawnCat(Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/level/levelgen/structure/BoundingBox;)V"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/level/levelgen/structure/structures/WoodlandMansionPieces$WoodlandMansionPiece",
|
||||
"methods": [
|
||||
"handleDataMarker(Ljava/lang/String;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/util/RandomSource;Lnet/minecraft/world/level/levelgen/structure/BoundingBox;)V"
|
||||
]
|
||||
},
|
||||
{
|
||||
"class": "net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate",
|
||||
"methods": [
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -1,77 +1,114 @@
|
||||
var ASMAPI = Java.type('net.minecraftforge.coremod.api.ASMAPI');
|
||||
var Opcodes = Java.type('org.objectweb.asm.Opcodes');
|
||||
var Handle = Java.type('org.objectweb.asm.Handle');
|
||||
var MethodInsnNode = Java.type('org.objectweb.asm.tree.MethodInsnNode');
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
function finalizeSpawnNode(node){
|
||||
return new MethodInsnNode(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"net/minecraftforge/event/ForgeEventFactory",
|
||||
"onFinalizeSpawn",
|
||||
"(Lnet/minecraft/world/entity/Mob;Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/EntitySpawnReason;Lnet/minecraft/world/entity/SpawnGroupData;)Lnet/minecraft/world/entity/SpawnGroupData;",
|
||||
false);
|
||||
}
|
||||
'use strict';
|
||||
|
||||
function contains(list, target) {
|
||||
for(var i = 0; i < list.length; i++) {
|
||||
if(list[i] == target) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const ASMAPI = Java.type('net.minecraftforge.coremod.api.ASMAPI');
|
||||
const Opcodes = Java.type('org.objectweb.asm.Opcodes');
|
||||
|
||||
function search(className, node, replacements) {
|
||||
for(var i = 0; i < replacements.length; i++){
|
||||
var r = replacements[i];
|
||||
if(contains(r.targets, className) && node.getOpcode() == r.opcode && node.name == r.name && node.desc == r.desc) {
|
||||
return r;
|
||||
// we can't run ASMAPI.loadData in the global context, so we do it here
|
||||
// this function is called inside of initializeCoreMod
|
||||
const replacements = [];
|
||||
function initReplacements() {
|
||||
ASMAPI.log('DEBUG', 'Gathering Forge method redirector replacements');
|
||||
replacements.push({
|
||||
// finalizeSpawn redirection to ForgeEventFactory.onFinalizeSpawn
|
||||
'type': ASMAPI.MethodType.VIRTUAL,
|
||||
'name': 'finalizeSpawn',
|
||||
'desc': '(Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/EntitySpawnReason;Lnet/minecraft/world/entity/SpawnGroupData;)Lnet/minecraft/world/entity/SpawnGroupData;',
|
||||
'targets': ASMAPI.loadData('coremods/finalize_spawn_targets.json'),
|
||||
'factory': function(insn) {
|
||||
return ASMAPI.buildMethodCall(
|
||||
ASMAPI.MethodType.STATIC,
|
||||
'net/minecraftforge/event/ForgeEventFactory',
|
||||
'onFinalizeSpawn',
|
||||
'(Lnet/minecraft/world/entity/Mob;Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/EntitySpawnReason;Lnet/minecraft/world/entity/SpawnGroupData;)Lnet/minecraft/world/entity/SpawnGroupData;');
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
var replacements = [
|
||||
{
|
||||
'opcode': Opcodes.INVOKEVIRTUAL,
|
||||
'name': ASMAPI.mapMethod('m_6518_'),
|
||||
'desc': '(Lnet/minecraft/world/level/ServerLevelAccessor;Lnet/minecraft/world/DifficultyInstance;Lnet/minecraft/world/entity/MobSpawnType;Lnet/minecraft/world/entity/SpawnGroupData;)Lnet/minecraft/world/entity/SpawnGroupData;',
|
||||
'targets': 'coremods/finalize_spawn_targets.json',
|
||||
'factory': finalizeSpawnNode
|
||||
}
|
||||
];
|
||||
|
||||
function initializeCoreMod() {
|
||||
var mergedTargets = [];
|
||||
for(var i = 0; i < replacements.length; i++){
|
||||
var r = replacements[i];
|
||||
r.targets = ASMAPI.loadData(r.targets);
|
||||
for(var k = 0; k < r.targets.length; k++){
|
||||
mergedTargets.push(r.targets[k]);
|
||||
}
|
||||
}
|
||||
initReplacements();
|
||||
|
||||
return {
|
||||
'forge_method_redirector': {
|
||||
'target': {
|
||||
'type': 'CLASS',
|
||||
'names': function(listofclasses) {
|
||||
return mergedTargets
|
||||
}
|
||||
'names': getTargets
|
||||
},
|
||||
'transformer': function(classNode) {
|
||||
var methods = classNode.methods;
|
||||
var count = 0;
|
||||
for(var i = 0; i < methods.size(); i++){
|
||||
var instr = methods.get(i).instructions;
|
||||
for(var ix = 0; ix < instr.size(); ix++){
|
||||
var node = instr.get(ix);
|
||||
var temp = search(classNode.name, node, replacements);
|
||||
if (temp != null) {
|
||||
instr.set(node, temp.factory(node));
|
||||
}
|
||||
}
|
||||
'transformer': applyMethodRedirects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTargets(classes) {
|
||||
const mergedTargets = [];
|
||||
for (let replacement of replacements) {
|
||||
for (let target of replacement.targets) {
|
||||
mergedTargets.push(target.class);
|
||||
}
|
||||
}
|
||||
|
||||
return mergedTargets;
|
||||
}
|
||||
|
||||
function applyMethodRedirects(clazz) {
|
||||
for (let replacement of replacements) {
|
||||
for (let methodString of getClassTargetMethods(clazz, replacement)) {
|
||||
// cut up the string since we've put both the name and desc as the target
|
||||
const splitPos = methodString.indexOf('(');
|
||||
const methodName = methodString.substring(0, splitPos);
|
||||
const methodDesc = methodString.substring(splitPos);
|
||||
const method = ASMAPI.findMethodNode(clazz, methodName, methodDesc);
|
||||
|
||||
// if we can't find the method, get out now!
|
||||
if (method === null) {
|
||||
ASMAPI.log('ERROR', 'Failed to redirect method call for {}! Method {} not found in class {}! This is a Forge bug, and is likely due to a Minecraft update changing something.', replacement.name, methodString, clazz.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let insn of method.instructions) {
|
||||
if (shouldReplace(insn, replacement)) {
|
||||
const redirection = replacement.factory(insn);
|
||||
ASMAPI.log('DEBUG', 'Redirecting method call {}{} to {}{} inside of {}.{}', insn.name, insn.desc, redirection.name, redirection.desc, clazz.name, method.name);
|
||||
ASMAPI.insertInsn(method, insn, redirection, ASMAPI.InsertMode.REMOVE_ORIGINAL);
|
||||
}
|
||||
return classNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clazz;
|
||||
}
|
||||
|
||||
|
||||
/* HELPER FUNCTIONS FOR TARGET SEARCHING */
|
||||
|
||||
function shouldReplace(insn, replacement) {
|
||||
return insn.getOpcode() === replacement.type.toOpcode()
|
||||
&& insn.name === replacement.name
|
||||
&& insn.desc === replacement.desc;
|
||||
}
|
||||
|
||||
function getClassTargetMethods(clazz, replacement) {
|
||||
for (let t of replacement.targets) {
|
||||
if (t.class === clazz.name) {
|
||||
const targets = [];
|
||||
|
||||
// declared methods
|
||||
for (let method of t.methods) {
|
||||
targets.push(method);
|
||||
}
|
||||
|
||||
// synthetic methods
|
||||
for (let method of clazz.methods) {
|
||||
if ((method.access & Opcodes.ACC_SYNTHETIC) != 0) {
|
||||
targets.push(method.name + method.desc);
|
||||
}
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
{
|
||||
"condition": "minecraft:match_tool",
|
||||
"predicate": {
|
||||
"items": "minecraft:shears"
|
||||
"items": "#c:tools/shear"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "model_render_type:block/old_leaves"
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"parent": "minecraft:block/leaves",
|
||||
"render_type": "minecraft:cutout_mipped",
|
||||
"textures": {
|
||||
"all": "minecraft:block/oak_leaves"
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"parent": "model_render_type:block/old_leaves"
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.debug.client;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.LeavesBlock;
|
||||
import net.minecraft.world.level.block.SoundType;
|
||||
import net.minecraft.world.level.block.state.BlockBehaviour;
|
||||
import net.minecraft.world.level.material.MapColor;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
import net.minecraftforge.client.model.data.ModelData;
|
||||
import net.minecraftforge.client.model.generators.BlockStateProvider;
|
||||
import net.minecraftforge.client.model.generators.ItemModelProvider;
|
||||
import net.minecraftforge.data.event.GatherDataEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.gametest.GameTestHolder;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
import net.minecraftforge.test.BaseTestMod;
|
||||
|
||||
@Mod(ModelRenderLayerTest.MODID)
|
||||
@GameTestHolder("forge." + ModelRenderLayerTest.MODID)
|
||||
public class ModelRenderLayerTest extends BaseTestMod {
|
||||
public static final String MODID = "model_render_type";
|
||||
|
||||
private static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(Registries.BLOCK, MODID);
|
||||
|
||||
private static final String OLD_LEAVES_NAME = "old_leaves";
|
||||
private static final BlockBehaviour.StatePredicate FALSE = (state, level, pos) -> false;
|
||||
public static final RegistryObject<LeavesBlock> OLD_LEAVES = BLOCKS.register(OLD_LEAVES_NAME,
|
||||
() -> new LeavesBlock(
|
||||
BlockBehaviour.Properties.of()
|
||||
.setId(BLOCKS.key(OLD_LEAVES_NAME))
|
||||
.mapColor(MapColor.PLANT)
|
||||
.strength(0.2F)
|
||||
.randomTicks()
|
||||
.sound(SoundType.GRASS)
|
||||
.noOcclusion()
|
||||
.isSuffocating(FALSE)
|
||||
.isViewBlocking(FALSE)
|
||||
.ignitedByLava()
|
||||
.pushReaction(PushReaction.DESTROY)
|
||||
.isRedstoneConductor(FALSE)
|
||||
)
|
||||
);
|
||||
|
||||
private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(Registries.ITEM, MODID);
|
||||
public static final RegistryObject<BlockItem> OLD_LEAVES_ITEM = ITEMS.register(OLD_LEAVES_NAME, () -> new BlockItem(OLD_LEAVES.get(), new Item.Properties().setId(ITEMS.key(OLD_LEAVES_NAME))));
|
||||
|
||||
public ModelRenderLayerTest(FMLJavaModLoadingContext context) {
|
||||
super(context);
|
||||
testItem(lookup -> new ItemStack(OLD_LEAVES_ITEM.get()));
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void runData(GatherDataEvent event) {
|
||||
event.getGenerator().addProvider(event.includeClient(), new BlockStates(event));
|
||||
event.getGenerator().addProvider(event.includeClient(), new ItemModels(event));
|
||||
}
|
||||
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void old_leaves_render_type(GameTestHelper helper) {
|
||||
var manager = Minecraft.getInstance().getModelManager();
|
||||
|
||||
var state = OLD_LEAVES.get().defaultBlockState();
|
||||
var model = manager.getBlockModelShaper().getBlockModel(state);
|
||||
var random = helper.getLevel().random;
|
||||
boolean originalRenderState = ItemBlockRenderTypes.isFancy();
|
||||
helper.addCleanup(passed -> ItemBlockRenderTypes.setFancy(originalRenderState));
|
||||
|
||||
ItemBlockRenderTypes.setFancy(true);
|
||||
var layer = model.getRenderTypes(state, random, ModelData.EMPTY);
|
||||
helper.assertTrue(layer.contains(RenderType.cutoutMipped()), "Block model does not contain the correct render type for fancy graphics. Expected: cutout_mipped");
|
||||
|
||||
ItemBlockRenderTypes.setFancy(false);
|
||||
layer = model.getRenderTypes(state, random, ModelData.EMPTY);
|
||||
helper.assertTrue(layer.contains(RenderType.solid()), "Block model does not contain the correct render type for fast graphics. Expected: solid");
|
||||
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
public static class BlockStates extends BlockStateProvider {
|
||||
public BlockStates(GatherDataEvent event) {
|
||||
super(event.getGenerator().getPackOutput(), MODID, event.getExistingFileHelper());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerStatesAndModels() {
|
||||
this.simpleBlock(
|
||||
OLD_LEAVES.get(),
|
||||
this.models()
|
||||
.leaves(OLD_LEAVES_NAME, blockTexture(Blocks.OAK_LEAVES))
|
||||
// only define the single render type. this test checks if "solid" is implicitly the fast graphics render type
|
||||
.renderType("cutout_mipped")
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
private static class ItemModels extends ItemModelProvider {
|
||||
public ItemModels(GatherDataEvent event) {
|
||||
super(event.getGenerator().getPackOutput(), MODID, event.getExistingFileHelper());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerModels() {
|
||||
this.withExistingParent(OLD_LEAVES_NAME, OLD_LEAVES.getKey().location().withPrefix(BLOCK_FOLDER + '/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.debug.gameplay.item;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.component.DataComponentType;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.ExtraCodecs;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.ForgeCapabilities;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.energy.EnergyStorage;
|
||||
import net.minecraftforge.energy.IEnergyStorage;
|
||||
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.gametest.GameTestHolder;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
import net.minecraftforge.test.BaseTestMod;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@GameTestHolder("forge." + ItemCapabilityTest.MOD_ID)
|
||||
@Mod(ItemCapabilityTest.MOD_ID)
|
||||
public class ItemCapabilityTest extends BaseTestMod {
|
||||
public static final String MOD_ID = "item_caps";
|
||||
|
||||
public static final DeferredRegister<DataComponentType<?>> DATA_COMPONENTS = DeferredRegister.create(Registries.DATA_COMPONENT_TYPE, MOD_ID);
|
||||
public static final RegistryObject<DataComponentType<Integer>> STORAGE = DATA_COMPONENTS.register("energy_storage", () ->
|
||||
DataComponentType.<Integer>builder()
|
||||
.persistent(ExtraCodecs.POSITIVE_INT)
|
||||
.networkSynchronized(ByteBufCodecs.VAR_INT)
|
||||
.build()
|
||||
);
|
||||
|
||||
public ItemCapabilityTest(FMLJavaModLoadingContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onEvent(AttachCapabilitiesEvent<ItemStack> event) {
|
||||
if (event.getObject().getItem() == Items.COPPER_INGOT) {
|
||||
event.addCapability(
|
||||
ResourceLocation.fromNamespaceAndPath("forge", "test"),
|
||||
new MyProvider(event.getObject())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void testItemCap(GameTestHelper helper) {
|
||||
|
||||
helper.registerEventListener(ItemCapabilityTest.class);
|
||||
|
||||
ItemStack stack = new ItemStack(Items.COPPER_INGOT);
|
||||
AtomicReference<IEnergyStorage> storageAtomicReference = new AtomicReference<>();
|
||||
|
||||
stack.getCapability(ForgeCapabilities.ENERGY).ifPresent(storage -> {
|
||||
storage.receiveEnergy(1, false);
|
||||
storageAtomicReference.set(storage);
|
||||
});
|
||||
|
||||
helper.assertTrue(
|
||||
storageAtomicReference.get() != null,
|
||||
"Unable to find ForgeCapabilities.ENERGY Capability"
|
||||
);
|
||||
|
||||
var registry = RegistryFriendlyByteBuf.decorator(helper.getLevel().registryAccess()).apply(new FriendlyByteBuf(Unpooled.buffer()));
|
||||
|
||||
ItemStack.STREAM_CODEC.encode(registry, stack);
|
||||
var stackOverWire = ItemStack.STREAM_CODEC.decode(registry);
|
||||
|
||||
AtomicReference<IEnergyStorage> storage = new AtomicReference<>();
|
||||
stackOverWire.getCapability(ForgeCapabilities.ENERGY).ifPresent(storage::set);
|
||||
|
||||
helper.assertValueEqual(
|
||||
storage.get() == null ? -1 : storage.get().getEnergyStored(),
|
||||
11,
|
||||
"Value did not Sync to client"
|
||||
);
|
||||
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
public static final class MyEnergyStorage extends EnergyStorage {
|
||||
|
||||
private final Consumer<EnergyStorage> consumer;
|
||||
|
||||
public MyEnergyStorage(int capacity, Consumer<EnergyStorage> consumer) {
|
||||
super(capacity);
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
public MyEnergyStorage(int capacity, int maxTransfer, Consumer<EnergyStorage> consumer) {
|
||||
super(capacity, maxTransfer);
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
public MyEnergyStorage(int capacity, int maxReceive, int maxExtract, Consumer<EnergyStorage> consumer) {
|
||||
super(capacity, maxReceive, maxExtract);
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
public MyEnergyStorage(int capacity, int maxReceive, int maxExtract, int energy, Consumer<EnergyStorage> consumer) {
|
||||
super(capacity, maxReceive, maxExtract, energy);
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
static <T, V> T doAndReturn(T value, V value2, Consumer<V> consumer) {
|
||||
consumer.accept(value2);
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int receiveEnergy(int maxReceive, boolean simulate) {
|
||||
return doAndReturn(super.receiveEnergy(maxReceive, simulate), this, consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extractEnergy(int maxExtract, boolean simulate) {
|
||||
return doAndReturn(super.extractEnergy(maxExtract, simulate), this, consumer);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class MyProvider implements ICapabilityProvider {
|
||||
private final ItemStack stack;
|
||||
private final EnergyStorage storage;
|
||||
private final LazyOptional<EnergyStorage> storageLazyOptional;
|
||||
|
||||
public MyProvider(ItemStack stack) {
|
||||
this.stack = stack;
|
||||
this.storage = new MyEnergyStorage(1000, 10, 10, stack.getOrDefault(STORAGE.get(), 10), storage -> {
|
||||
stack.set(STORAGE.get(), storage.getEnergyStored());
|
||||
});
|
||||
this.storageLazyOptional = LazyOptional.of(() -> storage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
|
||||
if (cap == ForgeCapabilities.ENERGY) {
|
||||
return storageLazyOptional.cast();
|
||||
}
|
||||
return LazyOptional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.debug.gameplay.item;
|
||||
|
||||
import net.minecraft.commands.arguments.EntityAnchorArgument;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.damagesource.DamageTypes;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.ShieldItem;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraftforge.event.entity.living.LivingEntityUseItemEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.gametest.GameTestHolder;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
import net.minecraftforge.test.BaseTestMod;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Mod(PreventItemDamageTest.MOD_ID)
|
||||
@GameTestHolder("forge." + PreventItemDamageTest.MOD_ID)
|
||||
public class PreventItemDamageTest extends BaseTestMod {
|
||||
static final String MOD_ID = "prevent_item_damage";
|
||||
|
||||
private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MOD_ID);
|
||||
private static final RegistryObject<FakeShieldItem> FAKE_SHIELD = ITEMS.register("fake_shield", FakeShieldItem::new);
|
||||
|
||||
public PreventItemDamageTest(FMLJavaModLoadingContext context) {
|
||||
super(context);
|
||||
this.testItem(lookup -> FAKE_SHIELD.get().getDefaultInstance());
|
||||
}
|
||||
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void player_fake_shield_took_modified_damage(GameTestHelper helper) {
|
||||
helper.makeFloor();
|
||||
|
||||
// setup player
|
||||
var player = helper.makeMockPlayer(GameType.SURVIVAL);
|
||||
|
||||
// setup shield
|
||||
var shield = FAKE_SHIELD.get().getDefaultInstance();
|
||||
|
||||
// setup events
|
||||
var firedLivingEntityUseItem = helper.boolFlag("fired LivingEntityUseItemEvent");
|
||||
var firedPlayerDestroyItem = helper.boolFlag("fired PlayerDestroyItemEvent");
|
||||
helper.<LivingEntityUseItemEvent.Start>addEventListener(event -> {
|
||||
if (event.getEntity() != player) return;
|
||||
|
||||
if (event.getItem() != shield)
|
||||
helper.fail("Player is using an item, but it's not the fake shield! Check the game test impl.");
|
||||
|
||||
// Artificially pass 5 seconds from start of the shield
|
||||
// This is because the first 5 ticks, the player is still vulnerable
|
||||
firedLivingEntityUseItem.set(true);
|
||||
event.setDuration(event.getDuration() - 100);
|
||||
});
|
||||
helper.<PlayerDestroyItemEvent>addEventListener(event -> {
|
||||
if (event.getEntity() != player) return;
|
||||
|
||||
if (event.getOriginal() != shield)
|
||||
helper.fail("Player destroyed an item, but it's not the fake shield! Check the game test impl.");
|
||||
|
||||
firedPlayerDestroyItem.set(true);
|
||||
});
|
||||
|
||||
// start using shield
|
||||
player.setItemInHand(InteractionHand.MAIN_HAND, shield);
|
||||
player.startUsingItem(InteractionHand.MAIN_HAND);
|
||||
int initialDamage = shield.getDamageValue();
|
||||
|
||||
// setup enemy
|
||||
var enemy = helper.spawnWithNoFreeWill(EntityType.HUSK, new BlockPos(2, 0, 2));
|
||||
player.lookAt(EntityAnchorArgument.Anchor.EYES, enemy.position());
|
||||
|
||||
// hit the player
|
||||
var attack = helper.registryLookup(Registries.DAMAGE_TYPE).getOrThrow(DamageTypes.MOB_ATTACK);
|
||||
var damage = new DamageSource(attack, enemy) {
|
||||
@Override
|
||||
public boolean scalesWithDifficulty() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
player.hurtServer(helper.getLevel(), damage, 5.0F);
|
||||
|
||||
// ok, run the tests
|
||||
firedLivingEntityUseItem.assertEquals(true);
|
||||
firedPlayerDestroyItem.assertEquals(true);
|
||||
helper.assertValueEqual(initialDamage + 1, shield.getDamageValue(), "shield damage value", "Fake shield did not take precisely 1 damage! Check IForgeItem#damageItem.");
|
||||
helper.assertValueEqual(player.getItemInHand(InteractionHand.MAIN_HAND), shield, "player shield", "Fake shield was removed from player's hand! Check Player#hurtCurrentlyUsedShield.");
|
||||
helper.assertValueNotEqual(player.getUseItem(), shield, "player use item", "Player should not be using the shield! The onBreak callback was never invoked. Check FakeShieldItem or IForgeItem#damageItem.");
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void fake_shield_damage_item_impl(GameTestHelper helper) {
|
||||
helper.makeFloor();
|
||||
|
||||
// setup player
|
||||
var player = helper.makeMockServerPlayer();
|
||||
|
||||
// setup shield
|
||||
var shield = FAKE_SHIELD.get().getDefaultInstance();
|
||||
int initialDamage = shield.getDamageValue();
|
||||
|
||||
// test hurt and break
|
||||
var damaged = helper.<Item>flag("damaged shield");
|
||||
shield.hurtAndBreak(1, helper.getLevel(), player, damaged::set);
|
||||
damaged.assertEquals(FAKE_SHIELD.get(), "Fake shield was not damaged! Check IForgeItem#damageItem.");
|
||||
helper.assertValueEqual(initialDamage + 1, shield.getDamageValue(), "shield damage value", "Fake shield did not take precisely 1 damage! Check IForgeItem#damageItem.");
|
||||
|
||||
// test hurt without breaking
|
||||
initialDamage = shield.getDamageValue();
|
||||
shield.hurtWithoutBreaking(1, player);
|
||||
helper.assertValueEqual(initialDamage, shield.getDamageValue(), "shield damage value", "Fake shield took damage even though hurtWithoutBreak test should set damage taken to 0! Check FakeShieldItem or IForgeItem#damageItem.");
|
||||
|
||||
// finished
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
private static final class FakeShieldItem extends ShieldItem {
|
||||
public FakeShieldItem() {
|
||||
super(new Item.Properties().setId(ITEMS.key("fake_shield")).durability(10));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int damageItem(ItemStack stack, int damage, ServerLevel level, @Nullable ServerPlayer player, boolean canBreak, Consumer<Item> onBroken) {
|
||||
if (canBreak) {
|
||||
onBroken.accept(this);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.debug.gameplay.item;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.commands.arguments.EntityAnchorArgument;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.damagesource.DamageTypes;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraftforge.event.entity.living.LivingEntityUseItemEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.gametest.GameTestHolder;
|
||||
import net.minecraftforge.test.BaseTestMod;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@Mod(ShieldDisablingTest.MOD_ID)
|
||||
@GameTestHolder("forge." + ShieldDisablingTest.MOD_ID)
|
||||
public final class ShieldDisablingTest extends BaseTestMod {
|
||||
public static final String MOD_ID = "shield_disabling";
|
||||
|
||||
public ShieldDisablingTest(FMLJavaModLoadingContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void player_shield_disabled_by_axe(GameTestHelper helper) {
|
||||
player_shield_disabled_common(helper, h -> Util.make(
|
||||
h.spawnWithNoFreeWill(EntityType.HUSK, new BlockPos(2, 0, 2)),
|
||||
enemy -> enemy.setItemInHand(InteractionHand.MAIN_HAND, new ItemStack(Items.IRON_AXE))
|
||||
));
|
||||
}
|
||||
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void player_shield_disabled_by_warden(GameTestHelper helper) {
|
||||
player_shield_disabled_common(helper, h -> h.spawnWithNoFreeWill(EntityType.WARDEN, new BlockPos(2, 0, 2)));
|
||||
}
|
||||
|
||||
private static void player_shield_disabled_common(GameTestHelper helper, Function<GameTestHelper, LivingEntity> enemyGetter) {
|
||||
helper.makeFloor();
|
||||
|
||||
// setup player
|
||||
var player = helper.makeMockPlayer(GameType.SURVIVAL);
|
||||
helper.<LivingEntityUseItemEvent.Start>addEventListener(event -> {
|
||||
// Artificially pass 5 seconds from start of the shield
|
||||
// This is because the first 5 ticks, the player is still vulnerable
|
||||
if (event.getEntity() == player) {
|
||||
event.setDuration(event.getDuration() - 100);
|
||||
}
|
||||
});
|
||||
|
||||
// start using shield
|
||||
var shield = Items.SHIELD.getDefaultInstance();
|
||||
player.setItemInHand(InteractionHand.MAIN_HAND, shield);
|
||||
player.startUsingItem(InteractionHand.MAIN_HAND);
|
||||
|
||||
// setup enemy
|
||||
var enemy = enemyGetter.apply(helper);
|
||||
player.lookAt(EntityAnchorArgument.Anchor.EYES, enemy.position());
|
||||
|
||||
// hit the player
|
||||
var attack = helper.registryLookup(Registries.DAMAGE_TYPE).getOrThrow(DamageTypes.MOB_ATTACK);
|
||||
var damage = new DamageSource(attack, enemy) {
|
||||
@Override
|
||||
public boolean scalesWithDifficulty() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
player.hurtServer(helper.getLevel(), damage, 5.0F);
|
||||
|
||||
// shield on cooldown?
|
||||
helper.assertTrue(player.getCooldowns().isOnCooldown(shield), "shield should be on cooldown");
|
||||
helper.succeed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.debug.gameplay.level;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.server.level.ChunkLevel;
|
||||
import net.minecraft.server.level.FullChunkStatus;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.gametest.GameTestHolder;
|
||||
import net.minecraftforge.test.BaseTestMod;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@GameTestHolder("forge." + ForcedChunkLoadingTest.MOD_ID)
|
||||
@Mod(ForcedChunkLoadingTest.MOD_ID)
|
||||
public class ForcedChunkLoadingTest extends BaseTestMod {
|
||||
static final String MOD_ID = "forced_chunk_loading";
|
||||
|
||||
public ForcedChunkLoadingTest(FMLJavaModLoadingContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
private static final int MAX_CHUNK_LOCATION_ATTEMPTS = 5;
|
||||
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void force_far_away_chunk(GameTestHelper helper) {
|
||||
var random = RandomSource.create();
|
||||
var level = helper.getLevel();
|
||||
var chunkSource = level.getChunkSource();
|
||||
|
||||
// try to find a far away chunk that is not already ticking
|
||||
int attempts;
|
||||
var origin = helper.absolutePos(BlockPos.ZERO);
|
||||
ChunkAccess chunk = null;
|
||||
for (attempts = 0; attempts < MAX_CHUNK_LOCATION_ATTEMPTS && chunk == null; attempts++) {
|
||||
int x = random.nextInt(1, 10) * 10000;
|
||||
int z = random.nextInt(1, 10) * 10000;
|
||||
chunk = level.getChunk(origin.offset(x, 0, z));
|
||||
|
||||
// If the chunk is already ticking, we can't force it
|
||||
chunk = chunkSource.isPositionTicking(chunk.getPos().toLong()) ? null : chunk;
|
||||
}
|
||||
|
||||
if (chunk == null) {
|
||||
helper.fail("Failed to find a far away chunk that is not already ticking after " + MAX_CHUNK_LOCATION_ATTEMPTS + " attempts");
|
||||
return;
|
||||
} else if (attempts > 1) {
|
||||
helper.say("WARNING: Finding a far away chunk took " + attempts + " attempts.", ChatFormatting.YELLOW);
|
||||
}
|
||||
|
||||
var pos = chunk.getPos();
|
||||
helper.say("Attempting to force far away chunk: " + pos, ChatFormatting.YELLOW);
|
||||
chunkSource.chunkMap.getDistanceManager().addRegionTicket(TicketType.FORCED, pos, ChunkLevel.byStatus(FullChunkStatus.FULL), pos, true);
|
||||
|
||||
helper.runAfterDelay(20, () -> {
|
||||
helper.assertTrue(chunkSource.chunkMap.getDistanceManager().shouldForceTicks(pos.toLong()), "Chunk is not ticketed as a forced loaded chunk");
|
||||
helper.assertTrue(chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(pos.toLong()), "Forced chunk cannot tick entities");
|
||||
helper.assertTrue(chunkSource.chunkMap.getDistanceManager().inBlockTickingRange(pos.toLong()), "Forced chunk cannot tick blocks");
|
||||
helper.assertTrue(chunkSource.hasChunk(pos.x, pos.z), "Chunk is not loaded despite being ticketed as a force loaded chunk");
|
||||
helper.succeed();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,7 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.MatchTool;
|
||||
import net.minecraftforge.common.Tags;
|
||||
import net.minecraftforge.common.crafting.conditions.IConditionBuilder;
|
||||
import net.minecraftforge.common.data.DatapackBuiltinEntriesProvider;
|
||||
import net.minecraftforge.common.data.GlobalLootModifierProvider;
|
||||
@@ -231,7 +232,8 @@ public class GlobalLootModifiersTest extends BaseTestMod {
|
||||
// This has no game test because it relies on random number generation and I can't be bothered to try and force that right now.
|
||||
add("wheat_harvest", new WheatSeedsConverterModifier(
|
||||
new LootItemCondition[] {
|
||||
MatchTool.toolMatches(ItemPredicate.Builder.item().of(items, Items.SHEARS)).build(),
|
||||
// the #c:tools_shear tag is used here to test parsing of tags in LootModiferManager
|
||||
MatchTool.toolMatches(ItemPredicate.Builder.item().of(items, Tags.Items.TOOLS_SHEAR)).build(),
|
||||
LootItemBlockStatePropertyCondition.hasBlockStateProperties(Blocks.WHEAT).build()
|
||||
},
|
||||
3, Items.WHEAT_SEEDS, Items.WHEAT)
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.debug.gameplay.redstone;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BarrelBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.ChestBlockEntity;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.test.BaseTestMod;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.gametest.GameTestHolder;
|
||||
|
||||
/**
|
||||
* Redstone update orders are important in many contraptions as there are pistons and other things that could conflict with each other.
|
||||
* These tests verify just that, two pistons pushing the same block.
|
||||
*
|
||||
* Partial verification/fix for https://github.com/MinecraftForge/MinecraftForge/issues/9973
|
||||
*/
|
||||
@Mod(UpdateOrder.MODID)
|
||||
@GameTestHolder("forge." + UpdateOrder.MODID)
|
||||
public class UpdateOrder extends BaseTestMod {
|
||||
public static final String MODID = "update_order";
|
||||
private static final int PISTON_DELAY = 4;
|
||||
|
||||
public UpdateOrder(FMLJavaModLoadingContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@GameTest(template = "update_order:update_order", rotationSteps = 0)
|
||||
public static void south(GameTestHelper helper) {
|
||||
var chestPos = new BlockPos(0, 1, 0);
|
||||
helper.assertBlockPresent(Blocks.CHEST, chestPos);
|
||||
var chest = helper.<ChestBlockEntity>getBlockEntity(chestPos);
|
||||
chest.setItem(0, new ItemStack(Items.DIRT));
|
||||
|
||||
helper.runAfterDelay(PISTON_DELAY, () -> {
|
||||
var expectedPos = new BlockPos(2, 1, 3);
|
||||
helper.assertBlockPresent(Blocks.WHITE_WOOL, expectedPos);
|
||||
helper.succeed();
|
||||
});
|
||||
}
|
||||
|
||||
@GameTest(template = "update_order:update_order", rotationSteps = 1)
|
||||
public static void west(GameTestHelper helper) {
|
||||
var chestPos = new BlockPos(0, 1, 0);
|
||||
helper.assertBlockPresent(Blocks.CHEST, chestPos);
|
||||
var chest = helper.<ChestBlockEntity>getBlockEntity(chestPos);
|
||||
chest.setItem(0, new ItemStack(Items.DIRT));
|
||||
|
||||
helper.runAfterDelay(PISTON_DELAY, () -> {
|
||||
var expectedPos = new BlockPos(2, 1, 3);
|
||||
helper.assertBlockPresent(Blocks.WHITE_WOOL, expectedPos);
|
||||
helper.succeed();
|
||||
});
|
||||
}
|
||||
|
||||
@GameTest(template = "update_order:update_order", rotationSteps = 2)
|
||||
public static void north(GameTestHelper helper) {
|
||||
var chestPos = new BlockPos(0, 1, 0);
|
||||
helper.assertBlockPresent(Blocks.CHEST, chestPos);
|
||||
var chest = helper.<ChestBlockEntity>getBlockEntity(chestPos);
|
||||
chest.setItem(0, new ItemStack(Items.DIRT));
|
||||
|
||||
helper.runAfterDelay(4, () -> {
|
||||
var expectedPos = new BlockPos(3, 1, 2);
|
||||
helper.assertBlockPresent(Blocks.WHITE_WOOL, expectedPos);
|
||||
helper.succeed();
|
||||
});
|
||||
}
|
||||
|
||||
@GameTest(template = "update_order:update_order", rotationSteps = 3)
|
||||
public static void east(GameTestHelper helper) {
|
||||
var chestPos = new BlockPos(0, 1, 0);
|
||||
helper.assertBlockPresent(Blocks.CHEST, chestPos);
|
||||
var chest = helper.<ChestBlockEntity>getBlockEntity(chestPos);
|
||||
chest.setItem(0, new ItemStack(Items.DIRT));
|
||||
|
||||
helper.runAfterDelay(PISTON_DELAY, () -> {
|
||||
var expectedPos = new BlockPos(2, 1, 3);
|
||||
helper.assertBlockPresent(Blocks.WHITE_WOOL, expectedPos);
|
||||
helper.succeed();
|
||||
});
|
||||
}
|
||||
|
||||
@GameTest(template = "update_order:upward_update")
|
||||
public static void up(GameTestHelper helper) {
|
||||
var chestPos = new BlockPos(0, 1, 2);
|
||||
var expectedPos = new BlockPos(2, 1, 0);
|
||||
var unexpectedPos = new BlockPos(0, 4, 0);
|
||||
helper.assertBlockPresent(Blocks.BARREL, chestPos);
|
||||
helper.assertBlockPresent(Blocks.AIR, expectedPos);
|
||||
helper.assertBlockPresent(Blocks.AIR, unexpectedPos);
|
||||
var chest = helper.<BarrelBlockEntity>getBlockEntity(chestPos);
|
||||
chest.setItem(0, new ItemStack(Items.DIRT));
|
||||
|
||||
helper.runAfterDelay(10, () -> { // There are a lot of things happening, give it a few ticks
|
||||
helper.assertBlockNotPresent(Blocks.WHITE_WOOL, unexpectedPos);
|
||||
helper.assertBlockPresent(Blocks.WHITE_WOOL, expectedPos);
|
||||
helper.succeed();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.debug.gameplay.villager;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.gametest.framework.GameTest;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.npc.Villager;
|
||||
import net.minecraft.world.entity.npc.VillagerTrades;
|
||||
import net.minecraft.world.entity.npc.VillagerType;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.gametest.GameTestHolder;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
import net.minecraftforge.test.BaseTestMod;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@GameTestHolder("forge." + VillagerTypeTestMod.MOD_ID)
|
||||
@Mod(VillagerTypeTestMod.MOD_ID)
|
||||
public class VillagerTypeTestMod extends BaseTestMod {
|
||||
public static final String MOD_ID = "villager_type_test";
|
||||
|
||||
private static final DeferredRegister<VillagerType> VILLAGER_TYPES = DeferredRegister.create(Registries.VILLAGER_TYPE, MOD_ID);
|
||||
|
||||
private static final RegistryObject<VillagerType> TEST_VILLAGER_TYPE = VILLAGER_TYPES.register("test_villager_type", () -> new VillagerType("test_villager_type"));
|
||||
|
||||
public VillagerTypeTestMod(FMLJavaModLoadingContext context) {
|
||||
super(context);
|
||||
context.getModEventBus().addListener(this::onCommonSetup);
|
||||
}
|
||||
|
||||
private void onCommonSetup(final FMLCommonSetupEvent event) {
|
||||
event.enqueueWork(() -> VillagerType.registerBiomeType(Biomes.PLAINS, TEST_VILLAGER_TYPE.get()));
|
||||
}
|
||||
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void biome_type(GameTestHelper helper) {
|
||||
RegistryAccess access = helper.getLevel().registryAccess();
|
||||
VillagerType type = access.lookupOrThrow(Registries.VILLAGER_TYPE).getValue(TEST_VILLAGER_TYPE.getId());
|
||||
if (type == null)
|
||||
helper.fail("Failed to find test_villager_type");
|
||||
helper.assertValueEqual(type, TEST_VILLAGER_TYPE.get(), "Loaded entry does not contain expected value");
|
||||
|
||||
Holder<Biome> biome = access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.PLAINS);
|
||||
helper.assertValueEqual(VillagerType.byBiome(biome), TEST_VILLAGER_TYPE.get(), "VillagerType.byBiome did not return the expected value");
|
||||
|
||||
helper.succeed();
|
||||
}
|
||||
|
||||
/** Test verifies NPE not thrown when looking up a villager type in the trade that doesn't contain that type.*/
|
||||
@GameTest(template = "forge:empty3x3x3")
|
||||
public static void emeralds_for_villager_type(GameTestHelper helper) {
|
||||
var trade = new VillagerTrades.EmeraldsForVillagerTypeItem(1, 12, 30, Map.of(TEST_VILLAGER_TYPE.get(), Items.DIRT));
|
||||
var rnd = helper.getLevel().getRandom();
|
||||
|
||||
// Should be a successful trade for dirt for our test villager
|
||||
var test = new Villager(EntityType.VILLAGER, helper.getLevel(), TEST_VILLAGER_TYPE.get());
|
||||
var test_offer = trade.getOffer(test, rnd);
|
||||
helper.assertFalse(test_offer == null, "Failed to retreive trade value for test profession");
|
||||
helper.assertValueEqual(test_offer.getItemCostA().itemStack().getItem(), Items.DIRT, "Offer did not return the expected item");
|
||||
|
||||
var plains = new Villager(EntityType.VILLAGER, helper.getLevel(), VillagerType.PLAINS);
|
||||
// This will NPE on unpatched code, we need to test that it returns null correctly
|
||||
var plains_offer = trade.getOffer(plains, rnd);
|
||||
helper.assertTrue(plains_offer == null, "Offer should not be available for a plains villager");
|
||||
|
||||
helper.succeed();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user