mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-14 09:05:17 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
664a2c06ad | ||
|
|
e85295a837 | ||
|
|
21582ef641 | ||
|
|
020ec3d51b | ||
|
|
774b79e609 | ||
|
|
ee6022b006 | ||
|
|
37efd2b8ae | ||
|
|
cf3d42ef38 | ||
|
|
03a505a3f2 | ||
|
|
bdc856edf5 | ||
|
|
6eebea0896 | ||
|
|
9218e08955 | ||
|
|
7006fe4f29 | ||
|
|
301e7252b0 | ||
|
|
2b5852072c | ||
|
|
0b38373a45 | ||
|
|
07e538f3dd | ||
|
|
b39e300c1b | ||
|
|
50db51a366 | ||
|
|
df80acc49d |
No files matched your search
Vendored
+1
-1
@@ -38,7 +38,7 @@ project {
|
||||
params {
|
||||
text("docker_jdk_version", "17", label = "Gradle version", description = "The version of the JDK to use during execution of tasks in a JDK.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("docker_gradle_version", "7.5.1", label = "Gradle version", description = "The version of Gradle to use during execution of Gradle tasks.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("git_main_branch", "1.19.x", label = "Git Main Branch", description = "The git main or default branch to use in VCS operations.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("git_main_branch", "1.20.x", label = "Git Main Branch", description = "The git main or default branch to use in VCS operations.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("git_branch_spec", """
|
||||
+:refs/heads/(main*)
|
||||
+:refs/heads/(master*)
|
||||
|
||||
+6
-2
@@ -27,8 +27,8 @@ plugins {
|
||||
id 'net.minecraftforge.gradleutils' version '2.+'
|
||||
id 'eclipse'
|
||||
id 'de.undercouch.download' version '5.1.3'
|
||||
id 'net.minecraftforge.gradle.patcher' version '[5.1.50,5.2.0)' apply false
|
||||
id 'net.minecraftforge.gradle.mcp' version '[5.1.50,5.2.0)' apply false
|
||||
id 'net.minecraftforge.gradle.patcher' version '[5.1.77,5.2.0)' apply false
|
||||
id 'net.minecraftforge.gradle.mcp' version '[5.1.77,5.2.0)' apply false
|
||||
}
|
||||
|
||||
Util.init() //Init all our extension methods!
|
||||
@@ -884,6 +884,10 @@ project(':forge') {
|
||||
apply plugin: 'net.minecraftforge.gradle.patcher'
|
||||
apply plugin: 'org.cadixdev.licenser'
|
||||
|
||||
applyPatches {
|
||||
level 'WARNING'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
|
||||
+3
-2
@@ -10,8 +10,9 @@ Forge is a free, open-source modding API all of your favourite mods use!
|
||||
|
||||
| Version | Support |
|
||||
|---------| ------------- |
|
||||
| 1.19.x | Active |
|
||||
| 1.18.x | LTS |
|
||||
| 1.20.x | Active |
|
||||
| 1.19.4 | Legacy |
|
||||
| 1.19.2 | Legacy |
|
||||
|
||||
* [Download]
|
||||
* [Forum]
|
||||
|
||||
@@ -11,11 +11,17 @@ import net.minecraftforge.forgespi.language.IModInfo;
|
||||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.stream.Collectors.*;
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
|
||||
|
||||
public class UniqueModListBuilder
|
||||
{
|
||||
@@ -66,28 +72,35 @@ public class UniqueModListBuilder
|
||||
|
||||
// Its theoretically possible that some mod has somehow moved an id to a secondary place, thus causing a dupe.
|
||||
// We can't handle this
|
||||
final List<IModInfo> dupedMods = modIds.values().stream()
|
||||
final List<String> dupedModErrors = modIds.values().stream()
|
||||
.filter(modInfos -> modInfos.size() > 1)
|
||||
.map(modInfos -> modInfos.get(0))
|
||||
.toList();
|
||||
.map(mods -> String.format("\tMod ID: '%s' from mod files: %s",
|
||||
mods.get(0).getModId(),
|
||||
mods.stream()
|
||||
.map(modInfo -> modInfo.getOwningFile().getFile().getFileName()).collect(joining(", "))
|
||||
)).toList();
|
||||
|
||||
if (!dupedMods.isEmpty()) {
|
||||
final List<EarlyLoadingException.ExceptionData> duplicateModErrors = dupedMods.stream()
|
||||
.map(dm -> new EarlyLoadingException.ExceptionData("fml.modloading.dupedmod", dm, Objects.toString(dm)))
|
||||
.toList();
|
||||
throw new EarlyLoadingException("Duplicate mods found", null, duplicateModErrors);
|
||||
if (!dupedModErrors.isEmpty()) {
|
||||
LOGGER.error(LOADING, "Found duplicate mods:\n{}", dupedModErrors.stream().collect(joining("\n")));
|
||||
throw new EarlyLoadingException("Duplicate mods found", null, dupedModErrors.stream()
|
||||
.map(s -> new EarlyLoadingException.ExceptionData(s))
|
||||
.toList());
|
||||
}
|
||||
|
||||
final List<ModFile> dupedLibs = versionedLibIds.values().stream()
|
||||
.filter(modFiles -> modFiles.size() > 1)
|
||||
.map(modFiles -> modFiles.get(0))
|
||||
.toList();
|
||||
|
||||
if (!dupedLibs.isEmpty()) {
|
||||
final List<EarlyLoadingException.ExceptionData> duplicateLibErrors = dupedLibs.stream()
|
||||
.map(dm -> new EarlyLoadingException.ExceptionData("fml.modloading.dupedlib.versioned", dm, Objects.toString(dm)))
|
||||
.toList();
|
||||
throw new EarlyLoadingException("Duplicate plugins or libraries found", null, duplicateLibErrors);
|
||||
final List<String> dupedLibErrors = versionedLibIds.values().stream()
|
||||
.filter(modFiles -> modFiles.size() > 1)
|
||||
.map(mods -> String.format("\tLibrary: '%s' from files: %s",
|
||||
getModId(mods.get(0)),
|
||||
mods.stream()
|
||||
.map(modFile -> modFile.getFileName()).collect(joining(", "))
|
||||
)).toList();
|
||||
|
||||
if (!dupedLibErrors.isEmpty()) {
|
||||
LOGGER.error(LOADING, "Found duplicate plugins or libraries:\n{}", dupedLibErrors.stream().collect(joining("\n")));
|
||||
throw new EarlyLoadingException("Duplicate plugins or libraries found", null, dupedLibErrors.stream()
|
||||
.map(s -> new EarlyLoadingException.ExceptionData(s))
|
||||
.toList());
|
||||
}
|
||||
|
||||
// Collect unique mod files by module name. This will be used for deduping purposes
|
||||
|
||||
@@ -12,6 +12,15 @@
|
||||
private static final Set<String> f_90811_ = Sets.newHashSet();
|
||||
public static final String f_167805_ = "key.categories.movement";
|
||||
public static final String f_167806_ = "key.categories.misc";
|
||||
@@ -41,7 +_,7 @@
|
||||
private int f_90818_;
|
||||
|
||||
public static void m_90835_(InputConstants.Key p_90836_) {
|
||||
- KeyMapping keymapping = f_90810_.get(p_90836_);
|
||||
+ for (KeyMapping keymapping : f_90810_.getAll(p_90836_))
|
||||
if (keymapping != null) {
|
||||
++keymapping.f_90818_;
|
||||
}
|
||||
@@ -49,7 +_,7 @@
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/client/gui/screens/ConnectScreen.java
|
||||
+++ b/net/minecraft/client/gui/screens/ConnectScreen.java
|
||||
@@ -72,6 +_,8 @@
|
||||
}
|
||||
|
||||
if (!optional.isPresent()) {
|
||||
+ ConnectScreen.f_95683_.error("Couldn't connect to server: Unknown host \"{}\"", p_249536_.m_171863_());
|
||||
+ net.minecraftforge.network.DualStackUtils.logInitialPreferences();
|
||||
p_251955_.execute(() -> {
|
||||
p_251955_.m_91152_(new DisconnectedScreen(ConnectScreen.this.f_95686_, CommonComponents.f_130661_, ConnectScreen.f_169260_));
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/minecraft/client/gui/screens/PauseScreen.java
|
||||
+++ b/net/minecraft/client/gui/screens/PauseScreen.java
|
||||
@@ -83,6 +_,7 @@
|
||||
} else {
|
||||
gridlayout$rowhelper.m_264139_(this.m_262456_(f_262254_, SocialInteractionsScreen::new));
|
||||
}
|
||||
+ gridlayout$rowhelper.m_264108_(Button.m_253074_(Component.m_237115_("fml.menu.mods"), button -> this.f_96541_.m_91152_(new net.minecraftforge.client.gui.ModListScreen(this))).m_252780_(f_262268_).m_253136_(), 2);
|
||||
|
||||
Component component = this.f_96541_.m_91090_() ? f_262217_ : f_262246_;
|
||||
this.f_252482_ = gridlayout$rowhelper.m_264108_(Button.m_253074_(component, (p_275864_) -> {
|
||||
+5
-1
@@ -34,7 +34,7 @@
|
||||
this.f_97334_.m_104401_(advancementtab.m_97182_(), true);
|
||||
break;
|
||||
}
|
||||
@@ -98,6 +_,11 @@
|
||||
@@ -98,9 +_,15 @@
|
||||
int i = (this.f_96543_ - 252) / 2;
|
||||
int j = (this.f_96544_ - 140) / 2;
|
||||
this.m_7333_(p_97361_);
|
||||
@@ -46,6 +46,10 @@
|
||||
this.m_97373_(p_97361_, p_97362_, p_97363_, i, j);
|
||||
this.m_97356_(p_97361_, i, j);
|
||||
this.m_97381_(p_97361_, p_97362_, p_97363_, i, j);
|
||||
+ super.m_86412_(p_97361_, p_97362_, p_97363_, p_97364_);
|
||||
}
|
||||
|
||||
public boolean m_7979_(double p_97347_, double p_97348_, int p_97349_, double p_97350_, double p_97351_) {
|
||||
@@ -138,10 +_,12 @@
|
||||
RenderSystem.m_157456_(0, f_97330_);
|
||||
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
--- a/net/minecraft/client/particle/TerrainParticle.java
|
||||
+++ b/net/minecraft/client/particle/TerrainParticle.java
|
||||
@@ -28,7 +_,7 @@
|
||||
this.f_107227_ = 0.6F;
|
||||
this.f_107228_ = 0.6F;
|
||||
this.f_107229_ = 0.6F;
|
||||
- if (!p_172458_.m_60713_(Blocks.f_50440_)) {
|
||||
+ if (net.minecraftforge.client.extensions.common.IClientBlockExtensions.of(p_172458_).areBreakingParticlesTinted(p_172458_, p_172451_, p_172459_)) {
|
||||
int i = Minecraft.m_91087_().m_91298_().m_92577_(p_172458_, p_172451_, p_172459_, 0);
|
||||
this.f_107227_ *= (float)(i >> 16 & 255) / 255.0F;
|
||||
this.f_107228_ *= (float)(i >> 8 & 255) / 255.0F;
|
||||
@@ -69,7 +_,13 @@
|
||||
public static class Provider implements ParticleProvider<BlockParticleOption> {
|
||||
public Particle m_6966_(BlockParticleOption p_108304_, ClientLevel p_108305_, double p_108306_, double p_108307_, double p_108308_, double p_108309_, double p_108310_, double p_108311_) {
|
||||
|
||||
@@ -9,21 +9,13 @@
|
||||
this.f_120088_.setSoTimeout(5000);
|
||||
this.f_120088_.joinGroup(this.f_120087_);
|
||||
}
|
||||
@@ -87,7 +_,19 @@
|
||||
@@ -87,7 +_,11 @@
|
||||
String s = LanServerPinger.m_120111_(p_120097_);
|
||||
String s1 = LanServerPinger.m_120116_(p_120097_);
|
||||
if (s1 != null) {
|
||||
- s1 = p_120098_.getHostAddress() + ":" + s1;
|
||||
+ if (net.minecraftforge.network.DualStackUtils.checkIPv6(p_120098_)) {
|
||||
+ final String ip;
|
||||
+
|
||||
+ // compress to short form if enabled in config
|
||||
+ if (net.minecraftforge.common.ForgeConfig.CLIENT.compressLanIPv6Addresses.get())
|
||||
+ ip = com.google.common.net.InetAddresses.toAddrString(p_120098_);
|
||||
+ else
|
||||
+ ip = p_120098_.getHostAddress();
|
||||
+
|
||||
+ s1 = "[" + ip + "]:" + s1;
|
||||
+ s1 = "[" + com.google.common.net.InetAddresses.toAddrString(p_120098_) + "]:" + s1;
|
||||
+ } else {
|
||||
+ s1 = p_120098_.getHostAddress() + ":" + s1;
|
||||
+ }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/net/minecraft/client/server/LanServerPinger.java
|
||||
+++ b/net/minecraft/client/server/LanServerPinger.java
|
||||
@@ -16,7 +_,7 @@
|
||||
@@ -13,7 +_,7 @@
|
||||
public class LanServerPinger extends Thread {
|
||||
private static final AtomicInteger f_120101_ = new AtomicInteger(0);
|
||||
private static final Logger f_120102_ = LogUtils.getLogger();
|
||||
@@ -9,7 +9,7 @@
|
||||
public static final int f_174975_ = 4445;
|
||||
private static final long f_174976_ = 1500L;
|
||||
private final String f_120103_;
|
||||
@@ -39,7 +_,7 @@
|
||||
@@ -36,7 +_,7 @@
|
||||
|
||||
while(!this.isInterrupted() && this.f_120105_) {
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
--- a/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -68,6 +_,8 @@
|
||||
private MinecraftServerGui f_139605_;
|
||||
@Nullable
|
||||
private final TextFilterClient f_139606_;
|
||||
+ @Nullable
|
||||
+ private net.minecraft.client.server.LanServerPinger dediLanPinger;
|
||||
|
||||
public DedicatedServer(Thread p_214789_, LevelStorageSource.LevelStorageAccess p_214790_, PackRepository p_214791_, WorldStem p_214792_, DedicatedServerSettings p_214793_, DataFixer p_214794_, Services p_214795_, ChunkProgressListenerFactory p_214796_) {
|
||||
super(p_214789_, p_214790_, p_214791_, p_214792_, Proxy.NO_PROXY, p_214794_, p_214795_, p_214796_);
|
||||
@@ -79,6 +_,7 @@
|
||||
public boolean m_7038_() throws IOException {
|
||||
Thread thread = new Thread("Server console handler") {
|
||||
@@ -22,7 +31,7 @@
|
||||
if (dedicatedserverproperties.f_139744_ != null) {
|
||||
this.m_129900_().m_46170_(GameRules.f_46153_).m_46246_(dedicatedserverproperties.f_139744_, this);
|
||||
}
|
||||
@@ -183,12 +_,13 @@
|
||||
@@ -183,12 +_,18 @@
|
||||
thread1.start();
|
||||
}
|
||||
|
||||
@@ -33,7 +42,34 @@
|
||||
}
|
||||
|
||||
- return true;
|
||||
+ if (net.minecraftforge.common.ForgeConfig.SERVER.advertiseDedicatedServerToLan.get()) {
|
||||
+ this.dediLanPinger = new net.minecraft.client.server.LanServerPinger(this.m_129916_(), String.valueOf(this.m_7448_()));
|
||||
+ this.dediLanPinger.start();
|
||||
+ }
|
||||
+
|
||||
+ return net.minecraftforge.server.ServerLifecycleHooks.handleServerStarting(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,6 +_,10 @@
|
||||
this.f_139601_.m_7530_();
|
||||
}
|
||||
|
||||
+ if (this.dediLanPinger != null) {
|
||||
+ this.dediLanPinger.interrupt();
|
||||
+ this.dediLanPinger = null;
|
||||
+ }
|
||||
}
|
||||
|
||||
public void m_5703_(BooleanSupplier p_139661_) {
|
||||
@@ -492,6 +_,10 @@
|
||||
|
||||
public void m_7041_() {
|
||||
super.m_7041_();
|
||||
+ if (this.dediLanPinger != null) {
|
||||
+ this.dediLanPinger.interrupt();
|
||||
+ this.dediLanPinger = null;
|
||||
+ }
|
||||
Util.m_137580_();
|
||||
SkullBlockEntity.m_196704_();
|
||||
}
|
||||
+10
@@ -15,6 +15,16 @@
|
||||
this.m_10055_();
|
||||
} else if (this.f_10019_ == ServerLoginPacketListenerImpl.State.DELAY_ACCEPT) {
|
||||
ServerPlayer serverplayer = this.f_10018_.m_6846_().m_11259_(this.f_10021_.getId());
|
||||
@@ -138,7 +_,8 @@
|
||||
}
|
||||
|
||||
public String m_10056_() {
|
||||
- return this.f_10021_ != null ? this.f_10021_ + " (" + this.f_10013_.m_129523_() + ")" : String.valueOf((Object)this.f_10013_.m_129523_());
|
||||
+ final String addressString = net.minecraftforge.network.DualStackUtils.getAddressString(this.f_10013_.m_129523_());
|
||||
+ return this.f_10021_ != null ? this.f_10021_ + " (" + addressString + ")" : addressString;
|
||||
}
|
||||
|
||||
public void m_5990_(ServerboundHelloPacket p_10047_) {
|
||||
@@ -147,14 +_,14 @@
|
||||
GameProfile gameprofile = this.f_10018_.m_236731_();
|
||||
if (gameprofile != null && p_10047_.f_238040_().equalsIgnoreCase(gameprofile.getName())) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
public PackRepository(RepositorySource... p_251886_) {
|
||||
- this.f_10497_ = ImmutableSet.copyOf(p_251886_);
|
||||
+ this.f_10497_ = new java.util.HashSet<>(List.of(p_251886_)); //Forge: This needs to be a mutable set, so that we can add to it later on.
|
||||
+ this.f_10497_ = new java.util.LinkedHashSet<>(List.of(p_251886_)); //Forge: This needs to be a mutable set, so that we can add to it later on.
|
||||
}
|
||||
|
||||
public void m_10506_() {
|
||||
|
||||
@@ -8,7 +8,15 @@
|
||||
|
||||
public PlayerList(MinecraftServer p_203842_, LayeredRegistryAccess<RegistryLayer> p_251844_, PlayerDataStorage p_203844_, int p_203845_) {
|
||||
this.f_11195_ = p_203842_;
|
||||
@@ -168,6 +_,7 @@
|
||||
@@ -161,13 +_,14 @@
|
||||
p_11263_.m_143425_(serverlevel1);
|
||||
String s1 = "local";
|
||||
if (p_11262_.m_129523_() != null) {
|
||||
- s1 = p_11262_.m_129523_().toString();
|
||||
+ s1 = net.minecraftforge.network.DualStackUtils.getAddressString(p_11262_.m_129523_());
|
||||
}
|
||||
|
||||
f_11188_.info("{}[{}] logged in with entity id {} at ({}, {}, {})", p_11263_.m_7755_().getString(), s1, p_11263_.m_19879_(), p_11263_.m_20185_(), p_11263_.m_20186_(), p_11263_.m_20189_());
|
||||
LevelData leveldata = serverlevel1.m_6106_();
|
||||
p_11263_.m_143427_(compoundtag);
|
||||
ServerGamePacketListenerImpl servergamepacketlistenerimpl = new ServerGamePacketListenerImpl(this.f_11195_, p_11262_, p_11263_);
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
this.m_5496_(soundtype.m_56776_(), soundtype.m_56773_() * 0.15F, soundtype.m_56774_());
|
||||
}
|
||||
}
|
||||
@@ -1074,19 +_,22 @@
|
||||
@@ -1074,31 +_,38 @@
|
||||
|
||||
public void m_5844_() {
|
||||
if (this.m_6069_()) {
|
||||
@@ -188,6 +188,24 @@
|
||||
}
|
||||
|
||||
void m_20074_() {
|
||||
Entity entity = this.m_20202_();
|
||||
+ java.util.function.BooleanSupplier updateFluidHeight = () -> this.m_204031_(FluidTags.f_13131_, 0.014D);
|
||||
if (entity instanceof Boat boat) {
|
||||
if (!boat.m_5842_()) {
|
||||
this.f_19798_ = false;
|
||||
- return;
|
||||
+ updateFluidHeight = () -> {
|
||||
+ this.updateFluidHeightAndDoFluidPushing(state -> this.shouldUpdateFluidWhileBoating(state, boat));
|
||||
+ return false;
|
||||
+ };
|
||||
}
|
||||
}
|
||||
|
||||
- if (this.m_204031_(FluidTags.f_13131_, 0.014D)) {
|
||||
+ if (updateFluidHeight.getAsBoolean()) {
|
||||
if (!this.f_19798_ && !this.f_19803_) {
|
||||
this.m_5841_();
|
||||
}
|
||||
@@ -1115,6 +_,7 @@
|
||||
private void m_20323_() {
|
||||
this.f_19800_ = this.m_204029_(FluidTags.f_13131_);
|
||||
@@ -414,19 +432,24 @@
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2861,9 +_,17 @@
|
||||
@@ -2861,9 +_,22 @@
|
||||
this.f_19859_ = this.m_146908_();
|
||||
}
|
||||
|
||||
+ @Deprecated // Forge: Use no parameter version instead, only for vanilla Tags
|
||||
+ @Deprecated // Forge: Use predicate version instead, only for vanilla Tags
|
||||
public boolean m_204031_(TagKey<Fluid> p_204032_, double p_204033_) {
|
||||
+ this.updateFluidHeightAndDoFluidPushing();
|
||||
+ this.updateFluidHeightAndDoFluidPushing(com.google.common.base.Predicates.alwaysTrue());
|
||||
+ if(p_204032_ == FluidTags.f_13131_) return this.isInFluidType(net.minecraftforge.common.ForgeMod.WATER_TYPE.get());
|
||||
+ else if (p_204032_ == FluidTags.f_13132_) return this.isInFluidType(net.minecraftforge.common.ForgeMod.LAVA_TYPE.get());
|
||||
+ else return false;
|
||||
+ }
|
||||
+
|
||||
+ @Deprecated(forRemoval = true, since = "1.19.4")
|
||||
+ public void updateFluidHeightAndDoFluidPushing() {
|
||||
+ this.updateFluidHeightAndDoFluidPushing(com.google.common.base.Predicates.alwaysTrue());
|
||||
+ }
|
||||
+
|
||||
+ public void updateFluidHeightAndDoFluidPushing(Predicate<FluidState> shouldUpdate) {
|
||||
if (this.m_146899_()) {
|
||||
- return false;
|
||||
+ return;
|
||||
@@ -446,7 +469,7 @@
|
||||
FluidState fluidstate = this.f_19853_.m_6425_(blockpos$mutableblockpos);
|
||||
- if (fluidstate.m_205070_(p_204032_)) {
|
||||
+ net.minecraftforge.fluids.FluidType fluidType = fluidstate.getFluidType();
|
||||
+ if (!fluidType.isAir()) {
|
||||
+ if (!fluidType.isAir() && shouldUpdate.test(fluidstate)) {
|
||||
double d1 = (double)((float)i2 + fluidstate.m_76155_(this.f_19853_, blockpos$mutableblockpos));
|
||||
if (d1 >= aabb.f_82289_) {
|
||||
flag1 = true;
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
--- a/net/minecraft/world/level/levelgen/feature/treedecorators/AlterGroundDecorator.java
|
||||
+++ b/net/minecraft/world/level/levelgen/feature/treedecorators/AlterGroundDecorator.java
|
||||
@@ -72,7 +_,7 @@
|
||||
for(int i = 2; i >= -3; --i) {
|
||||
BlockPos blockpos = p_225975_.m_6630_(i);
|
||||
if (Feature.m_65788_(p_225974_.m_226058_(), blockpos)) {
|
||||
- p_225974_.m_226061_(blockpos, this.f_69303_.m_213972_(p_225974_.m_226067_(), p_225975_));
|
||||
+ p_225974_.m_226061_(blockpos, net.minecraftforge.event.ForgeEventFactory.alterGround(p_225974_.m_226058_(), p_225974_.m_226067_(), blockpos, this.f_69303_.m_213972_(p_225974_.m_226067_(), p_225975_)));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"textures": [
|
||||
"minecraft:generic_7",
|
||||
"minecraft:generic_6",
|
||||
"minecraft:generic_5",
|
||||
"minecraft:generic_4",
|
||||
"minecraft:generic_3",
|
||||
"minecraft:generic_2",
|
||||
"minecraft:generic_1",
|
||||
"minecraft:generic_0"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"textures": [
|
||||
"minecraft:drip_hang"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"textures": [
|
||||
"minecraft:sga_a",
|
||||
"minecraft:sga_b",
|
||||
"minecraft:sga_c",
|
||||
"minecraft:sga_d",
|
||||
"minecraft:sga_e",
|
||||
"minecraft:sga_f",
|
||||
"minecraft:sga_g",
|
||||
"minecraft:sga_h",
|
||||
"minecraft:sga_i",
|
||||
"minecraft:sga_j",
|
||||
"minecraft:sga_k",
|
||||
"minecraft:sga_l",
|
||||
"minecraft:sga_m",
|
||||
"minecraft:sga_n",
|
||||
"minecraft:sga_o",
|
||||
"minecraft:sga_p",
|
||||
"minecraft:sga_q",
|
||||
"minecraft:sga_r",
|
||||
"minecraft:sga_s",
|
||||
"minecraft:sga_t",
|
||||
"minecraft:sga_u",
|
||||
"minecraft:sga_v",
|
||||
"minecraft:sga_w",
|
||||
"minecraft:sga_x",
|
||||
"minecraft:sga_y",
|
||||
"minecraft:sga_z"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"textures": [
|
||||
"minecraft:splash_0",
|
||||
"minecraft:splash_1",
|
||||
"minecraft:splash_2",
|
||||
"minecraft:splash_3"
|
||||
]
|
||||
}
|
||||
+9
-9
@@ -67,15 +67,15 @@ public interface IForgeDimensionSpecialEffects
|
||||
* Allows for manipulating the coloring of the lightmap texture.
|
||||
* Will be called for each 16*16 combination of sky/block light values.
|
||||
*
|
||||
* @param level The current level (client-side).
|
||||
* @param partialTicks Progress between ticks.
|
||||
* @param skyDarken Current darkness of the sky.
|
||||
* @param skyLight Sky light brightness factor.
|
||||
* @param blockLight Block light brightness factor.
|
||||
* @param pixelX X-coordinate of the lightmap texture.
|
||||
* @param pixelY Y-coordinate of the lightmap texture.
|
||||
* @param colors The color values that will be used: [r, g, b].
|
||||
* @param level The current level (client-side).
|
||||
* @param partialTicks Progress between ticks.
|
||||
* @param skyDarken Current darkness of the sky (can be used to calculate sky light).
|
||||
* @param blockLightRedFlicker Block light flicker factor (red color) (can be used to calculate block light).
|
||||
* @param skyLight Sky light brightness (accounting for sky darkness).
|
||||
* @param pixelX X-coordinate of the lightmap texture (block).
|
||||
* @param pixelY Y-coordinate of the lightmap texture (sky).
|
||||
* @param colors The color values that will be used: [r, g, b].
|
||||
* @see LightTexture#updateLightTexture(float)
|
||||
*/
|
||||
default void adjustLightmapColors(ClientLevel level, float partialTicks, float skyDarken, float skyLight, float blockLight, int pixelX, int pixelY, Vector3f colors) {}
|
||||
default void adjustLightmapColors(ClientLevel level, float partialTicks, float skyDarken, float blockLightRedFlicker, float skyLight, int pixelX, int pixelY, Vector3f colors) {}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package net.minecraftforge.client.extensions.common;
|
||||
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.particle.ParticleEngine;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
@@ -14,6 +15,7 @@ import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.Material;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
@@ -111,4 +113,17 @@ public interface IClientBlockExtensions
|
||||
}
|
||||
return originalColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the breaking particles created from the {@link BlockState} passed should be tinted with biome colors.
|
||||
*
|
||||
* @param state The state of this block
|
||||
* @param level The level the particles are spawning in
|
||||
* @param pos The position of the block
|
||||
* @return {@code true} if the particles should be tinted.
|
||||
*/
|
||||
default boolean areBreakingParticlesTinted(BlockState state, ClientLevel level, BlockPos pos)
|
||||
{
|
||||
return !state.is(Blocks.GRASS_BLOCK);
|
||||
}
|
||||
}
|
||||
@@ -496,6 +496,13 @@ public class ForgeGui extends Gui
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache()
|
||||
{
|
||||
super.clearCache();
|
||||
this.debugOverlay.clearChunkCache();
|
||||
}
|
||||
|
||||
protected void renderRecordOverlay(int width, int height, float partialTick, PoseStack pStack)
|
||||
{
|
||||
if (overlayMessageTime > 0)
|
||||
|
||||
@@ -27,6 +27,7 @@ public class KeyMappingLookup
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
@Nullable
|
||||
public KeyMapping get(InputConstants.Key keyCode)
|
||||
{
|
||||
@@ -59,6 +60,16 @@ public class KeyMappingLookup
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all active keys associated with the given key code and the active
|
||||
* modifiers and conflict context.
|
||||
*
|
||||
* This could probably be renamed to something like getActiveMappings, but
|
||||
* that would be a breaking change.
|
||||
*
|
||||
* @param keyCode the key being pressed
|
||||
* @return the list of key mappings
|
||||
*/
|
||||
public List<KeyMapping> getAll(InputConstants.Key keyCode)
|
||||
{
|
||||
List<KeyMapping> matchingBindings = new ArrayList<KeyMapping>();
|
||||
@@ -67,7 +78,11 @@ public class KeyMappingLookup
|
||||
Collection<KeyMapping> bindings = bindingsMap.get(keyCode);
|
||||
if (bindings != null)
|
||||
{
|
||||
matchingBindings.addAll(bindings);
|
||||
for (KeyMapping binding : bindings)
|
||||
{
|
||||
if (binding.isConflictContextAndModifierActive())
|
||||
matchingBindings.add(binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchingBindings;
|
||||
|
||||
@@ -30,6 +30,8 @@ public class ForgeConfig {
|
||||
|
||||
public final ConfigValue<String> permissionHandler;
|
||||
|
||||
public final BooleanValue advertiseDedicatedServerToLan;
|
||||
|
||||
Server(ForgeConfigSpec.Builder builder) {
|
||||
builder.comment("Server configuration settings")
|
||||
.push("server");
|
||||
@@ -69,6 +71,11 @@ public class ForgeConfig {
|
||||
.translation("forge.configgui.permissionHandler")
|
||||
.define("permissionHandler", "forge:default_handler");
|
||||
|
||||
advertiseDedicatedServerToLan = builder
|
||||
.comment("Set this to true to enable advertising the dedicated server to local LAN clients so that it shows up in the Multiplayer screen automatically.")
|
||||
.translation("forge.configgui.advertiseDedicatedServerToLan")
|
||||
.define("advertiseDedicatedServerToLan", true);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
@@ -123,6 +130,7 @@ public class ForgeConfig {
|
||||
|
||||
public final BooleanValue useCombinedDepthStencilAttachment;
|
||||
|
||||
@Deprecated(since = "1.20.1", forRemoval = true) // Config option ignored.
|
||||
public final BooleanValue compressLanIPv6Addresses;
|
||||
|
||||
Client(ForgeConfigSpec.Builder builder) {
|
||||
@@ -152,7 +160,7 @@ public class ForgeConfig {
|
||||
.define("useCombinedDepthStencilAttachment", false);
|
||||
|
||||
compressLanIPv6Addresses = builder
|
||||
.comment("When enabled, Forge will convert discovered 'Open to LAN' IPv6 addresses to their more compact, compressed representation")
|
||||
.comment("[DEPRECATED] [NOW IN COMMON CONFIG] When enabled, Forge will convert discovered 'Open to LAN' IPv6 addresses to their more compact, compressed representation")
|
||||
.translation("forge.configgui.compressLanIPv6Addresses")
|
||||
.define("compressLanIPv6Addresses", true);
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import net.minecraftforge.client.ClientCommandHandler;
|
||||
import net.minecraftforge.eventbus.api.BusBuilder;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
import net.minecraftforge.network.DualStackUtils;
|
||||
import net.minecraftforge.versions.forge.ForgeVersion;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -42,6 +43,7 @@ public class MinecraftForge
|
||||
UsernameCache.load();
|
||||
TierSortingRegistry.init();
|
||||
if (FMLEnvironment.dist == Dist.CLIENT) ClientCommandHandler.init();
|
||||
DualStackUtils.initialise();
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.data;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.particle.ParticleEngine;
|
||||
import net.minecraft.client.particle.ParticleProvider;
|
||||
import net.minecraft.core.particles.ParticleType;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A data provider for {@link net.minecraft.client.particle.ParticleDescription}s.
|
||||
*
|
||||
* <p>To use this provider, extend this class and implement {@link #addDescriptions()}.
|
||||
* Then, register an instance using {@link net.minecraft.data.DataGenerator#addProvider(boolean, Factory)}
|
||||
* via the {@link net.minecraftforge.data.event.GatherDataEvent} on the {@linkplain FMLJavaModLoadingContext#getModEventBus() mod event bus}.
|
||||
*
|
||||
* <p>A description can be added to a {@link ParticleType} which uses a {@linkplain #sprite(ParticleType, ResourceLocation) sprite}
|
||||
* or {@linkplain #spriteSet(ParticleType, Iterable) sprite set}.
|
||||
*
|
||||
* <pre>{@code
|
||||
* @Override
|
||||
* protected void addDescriptions() {
|
||||
* // Single sprite
|
||||
* this.sprite(ParticleTypes.DRIPPING_LAVA, new ResourceLocation("drip_hang"));
|
||||
*
|
||||
* // Multiple sprites
|
||||
* this.spriteSet(ParticleTypes.CLOUD, new ResourceLocation("generic"), 8, true);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>A particle description holds a list of textures used when rendering the
|
||||
* particle to the screen. All registered particle descriptions are stitched
|
||||
* together into a texture atlas called {@link net.minecraft.client.renderer.texture.TextureAtlas#LOCATION_PARTICLES}.
|
||||
* A {@link ParticleType} whose particle uses the texture atlas, typically via
|
||||
* the {@link net.minecraft.client.particle.ParticleRenderType}, can then reference
|
||||
* the necessary texture during rendering.
|
||||
*
|
||||
* <p>Particles with a particle description must have their particle providers
|
||||
* attached to a {@link ParticleType} as a {@linkplain net.minecraftforge.client.event.RegisterParticleProvidersEvent#registerSprite(ParticleType, ParticleProvider.Sprite) sprite}
|
||||
* or {@linkplain net.minecraftforge.client.event.RegisterParticleProvidersEvent#registerSpriteSet(ParticleType, ParticleEngine.SpriteParticleRegistration) sprite set}
|
||||
* consumer.
|
||||
*
|
||||
* @see DataProvider
|
||||
* @see net.minecraft.client.particle.ParticleDescription
|
||||
*/
|
||||
public abstract class ParticleDescriptionProvider implements DataProvider {
|
||||
|
||||
private final PackOutput.PathProvider particlesPath;
|
||||
@VisibleForTesting
|
||||
protected final ExistingFileHelper fileHelper;
|
||||
@VisibleForTesting
|
||||
protected final Map<ResourceLocation, List<String>> descriptions;
|
||||
|
||||
/**
|
||||
* Creates an instance of the data provider.
|
||||
*
|
||||
* @param output the expected root directory the data generator outputs to
|
||||
* @param fileHelper the helper used to validate a texture's existence
|
||||
*/
|
||||
protected ParticleDescriptionProvider(PackOutput output, ExistingFileHelper fileHelper) {
|
||||
this.particlesPath = output.createPathProvider(PackOutput.Target.RESOURCE_PACK, "particles");
|
||||
this.fileHelper = fileHelper;
|
||||
this.descriptions = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the particle descriptions generated by {@link #sprite(ParticleType, ResourceLocation)}
|
||||
* or one of the {@link #spriteSet(ParticleType, Iterable) sprite set methods}.
|
||||
*/
|
||||
protected abstract void addDescriptions();
|
||||
|
||||
/**
|
||||
* Creates a new particle description that contains a single texture for the
|
||||
* associated {@link ParticleType}.
|
||||
*
|
||||
* <p>Particle types with this description should be attached to a particle provider
|
||||
* via {@link net.minecraftforge.client.event.RegisterParticleProvidersEvent#registerSprite(ParticleType, ParticleProvider.Sprite)}.
|
||||
*
|
||||
* @param type the particle type the textures are applied
|
||||
* for
|
||||
* @param texture the texture to render for the particle type
|
||||
* @throws NullPointerException if the particle type is not registered
|
||||
* @throws IllegalArgumentException if a texture does not have an associated PNG
|
||||
* file, or the particle type has already been
|
||||
* provided
|
||||
*/
|
||||
protected void sprite(ParticleType<?> type, ResourceLocation texture) {
|
||||
this.spriteSet(type, texture);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new particle description that contains multiple textures for the
|
||||
* associated {@link ParticleType}. The textures are generated from a common
|
||||
* name and appended with a number representing the state of the animation.
|
||||
*
|
||||
* <pre>{@code
|
||||
* minecraft:generic_0
|
||||
* minecraft:generic_1
|
||||
* minecraft:generic_2
|
||||
* // ...
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Particle types with this description should be attached to a particle provider
|
||||
* via {@link net.minecraftforge.client.event.RegisterParticleProvidersEvent#registerSpriteSet(ParticleType, ParticleEngine.SpriteParticleRegistration)}.
|
||||
*
|
||||
* @param type the particle type the textures are applied
|
||||
* for
|
||||
* @param baseName the common name of all the textures
|
||||
* @param numOfTextures the number of textures within the set
|
||||
* @param reverse when {@code true}, the textures will be
|
||||
* listed in descending order
|
||||
* @throws NullPointerException if the particle type is not registered
|
||||
* @throws IllegalArgumentException if a texture does not have an associated PNG
|
||||
* file, or the particle type has already been
|
||||
* provided
|
||||
*/
|
||||
protected void spriteSet(ParticleType<?> type, ResourceLocation baseName, int numOfTextures, boolean reverse) {
|
||||
Preconditions.checkArgument(numOfTextures > 0, "The number of textures to generate must be positive");
|
||||
this.spriteSet(type, () -> new Iterator<>() {
|
||||
|
||||
private int counter = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.counter < numOfTextures;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation next() {
|
||||
var texture = baseName.withSuffix("_" + (reverse ? numOfTextures - this.counter - 1 : this.counter));
|
||||
this.counter++;
|
||||
return texture;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new particle description that contains multiple textures for the
|
||||
* associated {@link ParticleType}. The textures are passed as varargs with
|
||||
* at least one texture present.
|
||||
*
|
||||
* <p>Particle types with this description should be attached to a particle provider
|
||||
* via {@link net.minecraftforge.client.event.RegisterParticleProvidersEvent#registerSpriteSet(ParticleType, ParticleEngine.SpriteParticleRegistration)}.
|
||||
*
|
||||
* @param type the particle type the textures are applied
|
||||
* for
|
||||
* @param texture the first texture in the description
|
||||
* @param textures a list of subsequent textures to render for
|
||||
* the particle type
|
||||
* @throws NullPointerException if the particle type is not registered
|
||||
* @throws IllegalArgumentException if a texture does not have an associated PNG
|
||||
* file, or the particle type has already been
|
||||
* provided
|
||||
*/
|
||||
protected void spriteSet(ParticleType<?> type, ResourceLocation texture, ResourceLocation... textures) {
|
||||
this.spriteSet(type, Stream.concat(Stream.of(texture), Arrays.stream(textures))::iterator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new particle description that contains multiple textures for the
|
||||
* associated {@link ParticleType}. The textures are passed as an iterable.
|
||||
*
|
||||
* <p>Particle types with this description should be attached to a particle provider
|
||||
* via {@link net.minecraftforge.client.event.RegisterParticleProvidersEvent#registerSpriteSet(ParticleType, ParticleEngine.SpriteParticleRegistration)}.
|
||||
*
|
||||
* @param type the particle type the textures are applied
|
||||
* for
|
||||
* @param textures a list of textures to render for the
|
||||
* particle type
|
||||
* @throws NullPointerException if the particle type is not registered
|
||||
* @throws IllegalArgumentException if there are no textures provided, a texture
|
||||
* does not have an associated PNG file, or
|
||||
* the particle type has already been provided
|
||||
*/
|
||||
protected void spriteSet(ParticleType<?> type, Iterable<ResourceLocation> textures) {
|
||||
// Make sure particle type is registered
|
||||
var particle = Preconditions.checkNotNull(ForgeRegistries.PARTICLE_TYPES.getKey(type), "The particle type is not registered");
|
||||
|
||||
// Validate textures
|
||||
List<String> desc = new ArrayList<>();
|
||||
for (var texture : textures) {
|
||||
Preconditions.checkArgument(this.fileHelper.exists(texture, PackType.CLIENT_RESOURCES, ".png", "textures/particle"),
|
||||
"Texture '%s' does not exist in any known resource pack", texture);
|
||||
desc.add(texture.toString());
|
||||
}
|
||||
Preconditions.checkArgument(desc.size() > 0, "The particle type '%s' must have one texture", particle);
|
||||
|
||||
// Insert into map
|
||||
if (this.descriptions.putIfAbsent(particle, desc) != null) {
|
||||
throw new IllegalArgumentException(String.format("The particle type '%s' already has a description associated with it", particle));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> run(CachedOutput cache) {
|
||||
this.addDescriptions();
|
||||
|
||||
return CompletableFuture.allOf(
|
||||
this.descriptions.entrySet().stream().map(entry -> {
|
||||
// Map entries to the description format
|
||||
var textures = new JsonArray();
|
||||
entry.getValue().forEach(textures::add);
|
||||
return DataProvider.saveStable(cache,
|
||||
Util.make(new JsonObject(), obj -> obj.add("textures", textures)),
|
||||
this.particlesPath.json(entry.getKey())
|
||||
);
|
||||
}).toArray(CompletableFuture[]::new)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Particle Descriptions";
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package net.minecraftforge.common.extensions;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.vehicle.Boat;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraftforge.fluids.FluidType;
|
||||
@@ -37,4 +38,17 @@ public interface IForgeBoat
|
||||
{
|
||||
return type.supportsBoating(self());
|
||||
}
|
||||
|
||||
/**
|
||||
* When {@code false}, the fluid will no longer update its height value while
|
||||
* within a boat while it is not within a fluid ({@link Boat#isUnderWater()}.
|
||||
*
|
||||
* @param state the state of the fluid the rider is within
|
||||
* @param rider the rider of the boat
|
||||
* @return {@code true} if the fluid height should be updated, {@code false} otherwise
|
||||
*/
|
||||
default boolean shouldUpdateFluidWhileRiding(FluidState state, Entity rider)
|
||||
{
|
||||
return state.shouldUpdateWhileBoating(self(), rider);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
|
||||
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.vehicle.Boat;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
@@ -424,4 +425,17 @@ public interface IForgeEntity extends ICapabilitySerializable<CompoundTag>
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* When {@code false}, the fluid will no longer update its height value while
|
||||
* within a boat while it is not within a fluid ({@link Boat#isUnderWater()}.
|
||||
*
|
||||
* @param state the state of the fluid the rider is within
|
||||
* @param boat the boat the rider is within that is not inside a fluid
|
||||
* @return {@code true} if the fluid height should be updated, {@code false} otherwise
|
||||
*/
|
||||
default boolean shouldUpdateFluidWhileBoating(FluidState state, Boat boat)
|
||||
{
|
||||
return boat.shouldUpdateFluidWhileRiding(state, self());
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
package net.minecraftforge.common.extensions;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.vehicle.Boat;
|
||||
@@ -94,6 +95,20 @@ public interface IForgeFluid
|
||||
return getFluidType().supportsBoating(state, boat);
|
||||
}
|
||||
|
||||
/**
|
||||
* When {@code false}, the fluid will no longer update its height value while
|
||||
* within a boat while it is not within a fluid ({@link Boat#isUnderWater()}.
|
||||
*
|
||||
* @param state the state of the fluid the rider is within
|
||||
* @param boat the boat the rider is within that is not inside a fluid
|
||||
* @param rider the rider of the boat
|
||||
* @return {@code true} if the fluid height should be updated, {@code false} otherwise
|
||||
*/
|
||||
default boolean shouldUpdateWhileBoating(FluidState state, Boat boat, Entity rider)
|
||||
{
|
||||
return getFluidType().shouldUpdateWhileBoating(state, boat, rider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path type of this fluid when an entity is pathfinding. When
|
||||
* {@code null}, uses vanilla behavior.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package net.minecraftforge.common.extensions;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.vehicle.Boat;
|
||||
@@ -88,6 +89,19 @@ public interface IForgeFluidState
|
||||
return self().getType().supportsBoating(self(), boat);
|
||||
}
|
||||
|
||||
/**
|
||||
* When {@code false}, the fluid will no longer update its height value while
|
||||
* within a boat while it is not within a fluid ({@link Boat#isUnderWater()}.
|
||||
*
|
||||
* @param boat the boat the rider is within that is not inside a fluid
|
||||
* @param rider the rider of the boat
|
||||
* @return {@code true} if the fluid height should be updated, {@code false} otherwise
|
||||
*/
|
||||
default boolean shouldUpdateWhileBoating(Boat boat, Entity rider)
|
||||
{
|
||||
return self().getType().shouldUpdateWhileBoating(self(), boat, rider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path type of this fluid when an entity is pathfinding. When
|
||||
* {@code null}, uses vanilla behavior.
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.energy;
|
||||
|
||||
/**
|
||||
* Implementation of {@link IEnergyStorage} that cannot store, receive, or provide energy.
|
||||
* Use the {@link #INSTANCE}, don't instantiate. Example:
|
||||
* <pre>{@code
|
||||
* ItemStack stack = ...;
|
||||
* IEnergyStorage storage = stack.getCapability(ForgeCapabilities.ENERGY).orElse(EmptyEnergyStorage.INSTANCE);
|
||||
* // Use storage without checking whether it's present.
|
||||
* }</pre>
|
||||
*/
|
||||
public class EmptyEnergyStorage implements IEnergyStorage
|
||||
{
|
||||
public static final EmptyEnergyStorage INSTANCE = new EmptyEnergyStorage();
|
||||
|
||||
protected EmptyEnergyStorage() {}
|
||||
|
||||
@Override
|
||||
public int receiveEnergy(int maxReceive, boolean simulate)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int extractEnergy(int maxExtract, boolean simulate)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEnergyStored()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxEnergyStored()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canExtract()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReceive()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.entity.projectile.Projectile;
|
||||
import net.minecraft.world.entity.projectile.ThrownEnderpearl;
|
||||
import net.minecraft.world.level.LevelSimulatedReader;
|
||||
import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
|
||||
@@ -125,6 +126,7 @@ import net.minecraftforge.event.entity.player.PlayerWakeUpEvent;
|
||||
import net.minecraftforge.event.entity.player.SleepingLocationCheckEvent;
|
||||
import net.minecraftforge.event.entity.player.SleepingTimeCheckEvent;
|
||||
import net.minecraftforge.event.furnace.FurnaceFuelBurnTimeEvent;
|
||||
import net.minecraftforge.event.level.AlterGroundEvent;
|
||||
import net.minecraftforge.event.level.BlockEvent;
|
||||
import net.minecraftforge.event.level.BlockEvent.BlockToolModificationEvent;
|
||||
import net.minecraftforge.event.level.BlockEvent.CreateFluidSourceEvent;
|
||||
@@ -680,6 +682,13 @@ public class ForgeEventFactory
|
||||
return event;
|
||||
}
|
||||
|
||||
public static BlockState alterGround(LevelSimulatedReader level, RandomSource random, BlockPos pos, BlockState altered)
|
||||
{
|
||||
AlterGroundEvent event = new AlterGroundEvent(level, random, pos, altered);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
return event.getNewAlteredState();
|
||||
}
|
||||
|
||||
public static void fireChunkTicketLevelUpdated(ServerLevel level, long chunkPos, int oldTicketLevel, int newTicketLevel, @Nullable ChunkHolder chunkHolder)
|
||||
{
|
||||
if (oldTicketLevel != newTicketLevel)
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.event.level;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.LevelSimulatedReader;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.feature.treedecorators.TreeDecorator;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.Cancelable;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* This event is fired when {@link net.minecraft.world.level.levelgen.feature.treedecorators.AlterGroundDecorator#placeBlockAt(TreeDecorator.Context, BlockPos)}
|
||||
* attempts to alter a ground block when generating a feature. An example of this would be large spruce trees converting grass blocks into podzol.
|
||||
* <p>
|
||||
* This event is not {@linkplain Cancelable cancellable}.
|
||||
* <p>
|
||||
* This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus}
|
||||
* only on the {@linkplain net.minecraftforge.fml.LogicalSide#SERVER logical server}.
|
||||
*/
|
||||
public class AlterGroundEvent extends Event {
|
||||
private final LevelSimulatedReader level;
|
||||
private final RandomSource random;
|
||||
private final BlockPos pos;
|
||||
private final BlockState originalAltered;
|
||||
private BlockState newAltered;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public AlterGroundEvent(LevelSimulatedReader level, RandomSource random, BlockPos pos, BlockState altered) {
|
||||
super();
|
||||
this.level = level;
|
||||
this.random = random;
|
||||
this.pos = pos;
|
||||
this.originalAltered = altered;
|
||||
this.newAltered = altered;
|
||||
}
|
||||
|
||||
public LevelSimulatedReader getLevel() {
|
||||
return this.level;
|
||||
}
|
||||
|
||||
public RandomSource getRandom() {
|
||||
return this.random;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the position of the block that will be altered}
|
||||
*/
|
||||
public BlockPos getPos() {
|
||||
return this.pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the original block state that would be placed by the ground decorator}
|
||||
*/
|
||||
public BlockState getOriginalAlteredState() {
|
||||
return this.originalAltered;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the new block state to be placed by the ground decorator}
|
||||
*/
|
||||
public BlockState getNewAlteredState() {
|
||||
return this.newAltered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newAltered the new block state to be placed by the ground decorator
|
||||
*/
|
||||
public void setNewAlteredState(BlockState newAltered) {
|
||||
this.newAltered = newAltered;
|
||||
}
|
||||
}
|
||||
@@ -355,6 +355,20 @@ public class FluidType
|
||||
return this.supportsBoating(boat);
|
||||
}
|
||||
|
||||
/**
|
||||
* When {@code false}, the fluid will no longer update its height value while
|
||||
* within a boat while it is not within a fluid ({@link Boat#isUnderWater()}.
|
||||
*
|
||||
* @param state the state of the fluid the rider is within
|
||||
* @param boat the boat the rider is within that is not inside a fluid
|
||||
* @param rider the rider of the boat
|
||||
* @return {@code true} if the fluid height should be updated, {@code false} otherwise
|
||||
*/
|
||||
public boolean shouldUpdateWhileBoating(FluidState state, Boat boat, Entity rider)
|
||||
{
|
||||
return !this.supportsBoating(state, boat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the entity can ride in this vehicle under the fluid.
|
||||
*
|
||||
|
||||
@@ -147,7 +147,9 @@ public class FluidUtil
|
||||
}
|
||||
else
|
||||
{
|
||||
containerFluidHandler.fill(simulatedTransfer, IFluidHandler.FluidAction.SIMULATE);
|
||||
// We are acting on a COPY of the stack, so performing changes on the source is acceptable even if we are simulating.
|
||||
// We need to perform the change otherwise the call to getContainer() will be incorrect.
|
||||
containerFluidHandler.fill(simulatedTransfer, IFluidHandler.FluidAction.EXECUTE);
|
||||
}
|
||||
|
||||
ItemStack resultContainer = containerFluidHandler.getContainer();
|
||||
|
||||
@@ -15,8 +15,6 @@ import net.minecraftforge.fluids.FluidType;
|
||||
import net.minecraftforge.fluids.FluidUtil;
|
||||
import net.minecraftforge.fluids.capability.templates.VoidFluidHandler;
|
||||
|
||||
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
|
||||
|
||||
/**
|
||||
* Wrapper around any block, only accounts for fluid placement, otherwise the block acts a void.
|
||||
* If the block in question inherits from the Forge implementations,
|
||||
@@ -73,11 +71,11 @@ public class BlockWrapper extends VoidFluidHandler
|
||||
BlockState state = world.getBlockState(blockPos);
|
||||
if (liquidContainer.canPlaceLiquid(world, blockPos, state, resource.getFluid()))
|
||||
{
|
||||
//If we are executing try to actually fill the container, if it failed return that we failed
|
||||
if (action.simulate() || liquidContainer.placeLiquid(world, blockPos, state, resource.getFluid().getFluidType().getStateForPlacement(world, blockPos, resource)))
|
||||
if (action.execute())
|
||||
{
|
||||
return FluidType.BUCKET_VOLUME;
|
||||
liquidContainer.placeLiquid(world, blockPos, state, resource.getFluid().getFluidType().getStateForPlacement(world, blockPos, resource));
|
||||
}
|
||||
return FluidType.BUCKET_VOLUME;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -5,12 +5,21 @@
|
||||
|
||||
package net.minecraftforge.network;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import net.minecraft.client.multiplayer.resolver.ResolvedServerAddress;
|
||||
import net.minecraft.client.multiplayer.resolver.ServerAddress;
|
||||
import net.minecraft.client.multiplayer.resolver.ServerNameResolver;
|
||||
import net.minecraft.util.HttpUtil;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.net.Inet4Address;
|
||||
import net.minecraftforge.common.ForgeConfig;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -19,6 +28,19 @@ import java.util.Optional;
|
||||
|
||||
public class DualStackUtils
|
||||
{
|
||||
private static final String INITIAL_PREFER_IPv4_STACK = System.getProperty("java.net.preferIPv4Stack") == null ? "false" : System.getProperty("java.net.preferIPv4Stack");
|
||||
private static final String INITIAL_PREFER_IPv6_ADDRESSES = System.getProperty("java.net.preferIPv6Addresses") == null ? "false" : System.getProperty("java.net.preferIPv6Addresses");
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
/**
|
||||
* Called by {@link net.minecraftforge.common.MinecraftForge} to load this class so that the initial network
|
||||
* property constants are set before any of the other methods in this class are called. This is so we can
|
||||
* distinguish what Java's read once on JVM start vs what we've set for Netty.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public static void initialise() {}
|
||||
|
||||
/**
|
||||
* Resolve the address and see if Java and the OS return an IPv6 or IPv4 one, then let Netty know
|
||||
* accordingly (it doesn't understand the {@code java.net.preferIPv6Addresses=system} property).
|
||||
@@ -45,14 +67,49 @@ public class DualStackUtils
|
||||
*/
|
||||
public static boolean checkIPv6(final InetAddress inetAddress)
|
||||
{
|
||||
if (inetAddress instanceof Inet6Address)
|
||||
// only log debug messages if we're not in the server pinger thread, as otherwise it's unclear which IP
|
||||
// corresponds to which server as soon as you have more than one server in the multiplayer server list
|
||||
final String currentThreadName = Thread.currentThread().getName();
|
||||
final boolean shouldLogDebug = !currentThreadName.contains("Server Pinger #");
|
||||
|
||||
if (inetAddress instanceof Inet6Address addr)
|
||||
{
|
||||
if (shouldLogDebug)
|
||||
LOGGER.debug("Detected IPv6 address: \"" + addr.getHostAddress() + "\"");
|
||||
|
||||
System.setProperty("java.net.preferIPv4Stack", "false");
|
||||
System.setProperty("java.net.preferIPv6Addresses", "true");
|
||||
return true;
|
||||
}
|
||||
else if (inetAddress instanceof Inet4Address addr)
|
||||
{
|
||||
if (shouldLogDebug)
|
||||
LOGGER.debug("Detected IPv4 address: \"" + addr.getHostAddress() + "\"");
|
||||
|
||||
System.setProperty("java.net.preferIPv4Stack", "true");
|
||||
System.setProperty("java.net.preferIPv6Addresses", "false");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (shouldLogDebug) {
|
||||
final String addr = inetAddress == null ? "null" : "\"" + inetAddress.getHostAddress() + "\"";
|
||||
LOGGER.debug("Unable to determine IP version of address: " + addr);
|
||||
}
|
||||
|
||||
if (INITIAL_PREFER_IPv4_STACK.equalsIgnoreCase("false") && INITIAL_PREFER_IPv6_ADDRESSES.equalsIgnoreCase("true"))
|
||||
{
|
||||
if (shouldLogDebug)
|
||||
LOGGER.debug("Assuming IPv6 as Java was explicitly told to prefer it...");
|
||||
|
||||
System.setProperty("java.net.preferIPv4Stack", "false");
|
||||
System.setProperty("java.net.preferIPv6Addresses", "true");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shouldLogDebug)
|
||||
LOGGER.debug("Assuming IPv4...");
|
||||
|
||||
System.setProperty("java.net.preferIPv4Stack", "true");
|
||||
System.setProperty("java.net.preferIPv6Addresses", "false");
|
||||
return false;
|
||||
@@ -81,8 +138,43 @@ public class DualStackUtils
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for the "Open to LAN" feature.
|
||||
* @return The multicast group to use for LAN discovery - IPv6 if available, IPv4 otherwise.
|
||||
*/
|
||||
public static String getMulticastGroup() {
|
||||
if (checkIPv6(getLocalAddress())) return "FF75:230::60";
|
||||
else return "224.0.2.60";
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the initial values of the {@code java.net.preferIPv4Stack} and {@code java.net.preferIPv6Addresses} system
|
||||
* properties that Java has read on JVM start. Useful for debugging hostname lookup failures.
|
||||
*/
|
||||
public static void logInitialPreferences() {
|
||||
LOGGER.debug("Initial IPv4 stack preference: " + INITIAL_PREFER_IPv4_STACK);
|
||||
LOGGER.debug("Initial IPv6 addresses preference: " + INITIAL_PREFER_IPv6_ADDRESSES);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link SocketAddress#toString()} but with IPv6 address compression support
|
||||
*/
|
||||
public static String getAddressString(final SocketAddress address) {
|
||||
if (address instanceof final InetSocketAddress inetAddress) {
|
||||
String formatted;
|
||||
if (inetAddress.isUnresolved()) {
|
||||
formatted = inetAddress.getHostName() + "/<unresolved>";
|
||||
} else {
|
||||
formatted = InetAddresses.toAddrString(inetAddress.getAddress());
|
||||
if (inetAddress.getAddress() instanceof Inet6Address)
|
||||
formatted = '[' + formatted + ']';
|
||||
|
||||
formatted = '/' + formatted;
|
||||
}
|
||||
|
||||
return formatted + ':' + inetAddress.getPort();
|
||||
}
|
||||
|
||||
return address.toString();
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public class DelegatingPackResources extends AbstractPackResources
|
||||
{
|
||||
IoSupplier<InputStream> ioSupplier = pack.getResource(type, location);
|
||||
if (ioSupplier != null)
|
||||
return pack.getResource(type, location);
|
||||
return ioSupplier;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -4,4 +4,6 @@
|
||||
# checkAndFixSAS will populate all overrides as nessasary. Prefixing added lines with \t
|
||||
# checkAndFixSAS also supports names using . so simplest way to find the correct line to add is to get the AT line from the bot
|
||||
# and remove the access modifier.
|
||||
#==================================================================================================================================
|
||||
# You need to run the setup task again after running checkAndFixSAS for genPatches to pick up the changes.
|
||||
#==================================================================================================================================
|
||||
net/minecraft/client/server/LanServerPinger
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.debug;
|
||||
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraftforge.event.level.AlterGroundEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
@Mod("alter_ground_event_test")
|
||||
@Mod.EventBusSubscriber
|
||||
public class AlterGroundEventTest {
|
||||
public static final boolean ENABLE = true;
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onAlterGround(AlterGroundEvent event)
|
||||
{
|
||||
if (ENABLE) {
|
||||
if (event.getOriginalAlteredState().is(Blocks.PODZOL)) {
|
||||
event.setNewAlteredState(Blocks.REDSTONE_BLOCK.defaultBlockState());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -34,6 +35,7 @@ import net.minecraft.DetectedVersion;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.PackOutput;
|
||||
@@ -169,6 +171,7 @@ public class DataGeneratorTest
|
||||
gen.addProvider(event.includeClient(), itemModels);
|
||||
gen.addProvider(event.includeClient(), new BlockStates(packOutput, itemModels.existingFileHelper));
|
||||
gen.addProvider(event.includeClient(), new SoundDefinitions(packOutput, event.getExistingFileHelper()));
|
||||
gen.addProvider(event.includeClient(), new ParticleDescriptions(packOutput, event.getExistingFileHelper()));
|
||||
|
||||
gen.addProvider(event.includeServer(), new Recipes(packOutput));
|
||||
gen.addProvider(event.includeServer(), new Tags(packOutput, lookupProvider, event.getExistingFileHelper()));
|
||||
@@ -1120,6 +1123,80 @@ public class DataGeneratorTest
|
||||
}
|
||||
}
|
||||
|
||||
private static class ParticleDescriptions extends ParticleDescriptionProvider {
|
||||
|
||||
public ParticleDescriptions(PackOutput output, ExistingFileHelper fileHelper) {
|
||||
super(output, fileHelper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addDescriptions() {
|
||||
this.sprite(ParticleTypes.DRIPPING_LAVA, new ResourceLocation("drip_hang"));
|
||||
|
||||
this.spriteSet(ParticleTypes.CLOUD, new ResourceLocation("generic"), 8, true);
|
||||
|
||||
this.spriteSet(ParticleTypes.FISHING,
|
||||
new ResourceLocation("splash_0"),
|
||||
new ResourceLocation("splash_1"),
|
||||
new ResourceLocation("splash_2"),
|
||||
new ResourceLocation("splash_3")
|
||||
);
|
||||
|
||||
this.spriteSet(ParticleTypes.ENCHANT, () -> new Iterator<>() {
|
||||
|
||||
private final ResourceLocation base = new ResourceLocation("sga");
|
||||
private char suffix = 'a';
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.suffix <= 'z';
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation next() {
|
||||
return this.base.withSuffix("_" + this.suffix++);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> run(CachedOutput cache) {
|
||||
return super.run(cache).thenRun(this::validateResults);
|
||||
}
|
||||
|
||||
private void validateResults() {
|
||||
var errors = Stream.of(ParticleTypes.DRIPPING_LAVA, ParticleTypes.CLOUD, ParticleTypes.FISHING, ParticleTypes.ENCHANT)
|
||||
.map(ForgeRegistries.PARTICLE_TYPES::getKey).map(particle -> {
|
||||
try (var resource = this.fileHelper.getResource(particle, PackType.CLIENT_RESOURCES, ".json", "particles").openAsReader()) {
|
||||
var existingTextures = GSON.fromJson(resource, JsonObject.class).get("textures").getAsJsonArray();
|
||||
var generatedTextures = this.descriptions.get(particle);
|
||||
|
||||
// Check texture size
|
||||
if (existingTextures.size() != generatedTextures.size()) {
|
||||
LOGGER.error("%s had a different number of sprites, expected %s, actual %s", particle, existingTextures.size(), generatedTextures.size());
|
||||
return particle;
|
||||
}
|
||||
|
||||
boolean error = false;
|
||||
for (int i = 0; i < generatedTextures.size(); ++i) {
|
||||
if (!existingTextures.get(i).getAsString().equals(generatedTextures.get(i))) {
|
||||
LOGGER.error("%s index %s: expected %s, actual %s", particle, i, existingTextures.get(i).getAsString(), generatedTextures.get(i));
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
return error ? particle : null;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).filter(Objects::nonNull).toList();
|
||||
|
||||
if (!errors.isEmpty()) {
|
||||
throw new AssertionError(String.format("Validation errors found in %s; see above for details", errors.stream().reduce("", (str, rl) -> str + ", " + rl, (str1, str2) -> str1 + ", " + str2)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends ModelBuilder<T>> List<String> testModelResults(Map<ResourceLocation, T> models, ExistingFileHelper existingFileHelper, Set<ResourceLocation> toIgnore) {
|
||||
List<String> ret = new ArrayList<>();
|
||||
models.forEach((loc, model) -> {
|
||||
|
||||
@@ -38,6 +38,7 @@ public class FluidUtilTest
|
||||
private static void runTests(FMLCommonSetupEvent commonSetupEvent)
|
||||
{
|
||||
test_tryEmptyContainer();
|
||||
test_tryFillContainer();
|
||||
test_tryEmptyContainerAndStow_stackable();
|
||||
|
||||
LogManager.getLogger().info("FluidUtilTest ok!");
|
||||
@@ -70,6 +71,35 @@ public class FluidUtilTest
|
||||
checkItemStack(sourceStack, Items.WATER_BUCKET, 2); // Apparently the stack is not supposed to be modified
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that tryFillContainer doesn't change the target fluid handler when simulating.
|
||||
* Ant that the result of the simulated transfver is valid.
|
||||
* Regression test for the root cause of <a href="https://github.com/MinecraftForge/MinecraftForge/issues/6796">issue #6796</a>.
|
||||
*/
|
||||
private static void test_tryFillContainer()
|
||||
{
|
||||
var targetStack = new ItemStack(Items.BUCKET, 2);
|
||||
var sourceTank = new FluidTank(10000);
|
||||
sourceTank.setFluid(new FluidStack(Fluids.WATER, 5000));
|
||||
|
||||
// Simulate is not supposed to modify anything
|
||||
var simulateResult = FluidUtil.tryFillContainer(targetStack, sourceTank, 1000, null, false);
|
||||
if (!simulateResult.isSuccess())
|
||||
throw new AssertionError("Failed to transfer.");
|
||||
checkItemStack(simulateResult.getResult(), Items.WATER_BUCKET, 1);
|
||||
// Tank and stack shouldn't be modified
|
||||
checkItemStack(targetStack, Items.BUCKET, 2);
|
||||
checkFluidStack(sourceTank.getFluid(), Fluids.WATER, 5000);
|
||||
|
||||
// Execute should modify
|
||||
var executeResult = FluidUtil.tryFillContainer(targetStack, sourceTank, 1000, null, true);
|
||||
if (!executeResult.isSuccess())
|
||||
throw new AssertionError("Failed to transfer.");
|
||||
checkItemStack(executeResult.getResult(), Items.WATER_BUCKET, 1);
|
||||
checkFluidStack(sourceTank.getFluid(), Fluids.WATER, 4000);
|
||||
checkItemStack(targetStack, Items.BUCKET, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that tryEmptyContainerAndStow doesn't duplicate fluids in the target when the container is stackable.
|
||||
* Regression test for <a href="https://github.com/MinecraftForge/MinecraftForge/issues/6796">issue #6796</a>.
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.debug;
|
||||
|
||||
import com.mojang.blaze3d.platform.InputConstants;
|
||||
import net.minecraft.client.KeyMapping;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.RegisterKeyMappingsEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent;
|
||||
|
||||
@Mod("keymapping_test")
|
||||
public class KeyMappingTest
|
||||
{
|
||||
@Mod.EventBusSubscriber(modid = "keymapping_test", value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
|
||||
public static class ClientStuff {
|
||||
// these are two separate keys to stand in for keys added by different
|
||||
// mods that each do something similar with a held item from the
|
||||
// respective mod, so the user wants them on the same physical key.
|
||||
static KeyMapping stickKey = new KeyMapping("stick_key", InputConstants.KEY_BACKSLASH, KeyMapping.CATEGORY_MISC);
|
||||
static KeyMapping rockKey = new KeyMapping("rock_key", InputConstants.KEY_BACKSLASH, KeyMapping.CATEGORY_MISC);
|
||||
@SubscribeEvent
|
||||
public static void init(FMLConstructModEvent event) {
|
||||
MinecraftForge.EVENT_BUS.addListener(ClientStuff::tick);
|
||||
}
|
||||
@SubscribeEvent
|
||||
public static void initKeys(RegisterKeyMappingsEvent event) {
|
||||
event.register(stickKey);
|
||||
event.register(rockKey);
|
||||
}
|
||||
public static void tick(TickEvent.ClientTickEvent event) {
|
||||
if(event.phase != TickEvent.Phase.START) return;
|
||||
if(stickKey.consumeClick()) {
|
||||
Player player = Minecraft.getInstance().player;
|
||||
if(player != null && player.getMainHandItem().is(Items.STICK))
|
||||
player.sendSystemMessage(Component.literal("stick found!"));
|
||||
}
|
||||
if(rockKey.consumeClick()) {
|
||||
Player player = Minecraft.getInstance().player;
|
||||
if(player != null && player.getMainHandItem().is(Items.COBBLESTONE))
|
||||
player.sendSystemMessage(Component.literal("rock found!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.debug.item;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResultHolder;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.CreativeModeTabs;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.ClipContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.event.CreativeModeTabEvent;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fluids.FluidActionResult;
|
||||
import net.minecraftforge.fluids.FluidStack;
|
||||
import net.minecraftforge.fluids.FluidType;
|
||||
import net.minecraftforge.fluids.FluidUtil;
|
||||
import net.minecraftforge.fluids.capability.templates.FluidHandlerItemStackSimple;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
import net.minecraftforge.registries.RegistryObject;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@Mod(CustomFluidContainerTest.MODID)
|
||||
public class CustomFluidContainerTest
|
||||
{
|
||||
public static final String MODID = "custom_fluid_container_test";
|
||||
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MODID);
|
||||
|
||||
public static final boolean ENABLED = true;
|
||||
|
||||
public static final RegistryObject<Item> CUSTOM_FLUID_CONTAINER = ITEMS.register("custom_fluid_container", () -> new CustomFluidContainer((new Item.Properties()).stacksTo(1)));
|
||||
|
||||
public CustomFluidContainerTest()
|
||||
{
|
||||
if (ENABLED)
|
||||
{
|
||||
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
|
||||
ITEMS.register(modEventBus);
|
||||
modEventBus.addListener(this::addCreative);
|
||||
}
|
||||
}
|
||||
|
||||
private void addCreative(CreativeModeTabEvent.BuildContents event)
|
||||
{
|
||||
if (event.getTab() == CreativeModeTabs.INGREDIENTS)
|
||||
event.accept(CUSTOM_FLUID_CONTAINER);
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom fluid container item with a capacity of a vanilla bucket which uses the FluidUtil functionalities to pickup and place fluids.
|
||||
*/
|
||||
private static class CustomFluidContainer extends Item
|
||||
{
|
||||
|
||||
public CustomFluidContainer(Properties properties)
|
||||
{
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public Component getName(@Nonnull ItemStack itemStack) {
|
||||
AtomicReference<String> name = new AtomicReference<>("Custom Fluid Container");
|
||||
FluidUtil.getFluidHandler(itemStack).ifPresent(fluidHandler ->
|
||||
{
|
||||
FluidStack fluidStack = fluidHandler.getFluidInTank(0);
|
||||
if (fluidStack.isEmpty())
|
||||
{
|
||||
name.set(name.get() + " (empty)");
|
||||
}
|
||||
else
|
||||
{
|
||||
name.set(name.get() + " (" + fluidStack.getFluid().getFluidType().getDescription().getString() + ")");
|
||||
}
|
||||
});
|
||||
return Component.literal(name.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand)
|
||||
{
|
||||
var itemStack = player.getItemInHand(hand);
|
||||
var result = new AtomicReference<FluidActionResult>();
|
||||
FluidUtil.getFluidHandler(itemStack).ifPresent(fluidHandler ->
|
||||
{
|
||||
var fluidStack = fluidHandler.getFluidInTank(0);
|
||||
if (fluidStack.isEmpty())
|
||||
{
|
||||
var blockHitResult = getPlayerPOVHitResult(level, player, ClipContext.Fluid.SOURCE_ONLY);
|
||||
result.set(FluidUtil.tryPickUpFluid(itemStack, player, level, blockHitResult.getBlockPos(), blockHitResult.getDirection()));
|
||||
}
|
||||
else
|
||||
{
|
||||
var blockHitResult = getPlayerPOVHitResult(level, player, ClipContext.Fluid.NONE);
|
||||
//try to place fluid in hit block (waterlogging, fill tank, ...). When no success try the block on the hit side.
|
||||
for (BlockPos pos : Arrays.asList(blockHitResult.getBlockPos(), blockHitResult.getBlockPos().relative(blockHitResult.getDirection())))
|
||||
{
|
||||
result.set(FluidUtil.tryPlaceFluid(player, level, hand, pos, itemStack, fluidStack));
|
||||
if (result.get().isSuccess())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (result.get() != null && result.get().isSuccess())
|
||||
{
|
||||
return InteractionResultHolder.sidedSuccess(result.get().getResult(), level.isClientSide());
|
||||
}
|
||||
return super.use(level, player, hand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICapabilityProvider initCapabilities(@Nonnull ItemStack stack, @Nullable CompoundTag nbt)
|
||||
{
|
||||
return new FluidHandlerItemStackSimple(stack, FluidType.BUCKET_VOLUME);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -293,6 +293,12 @@ modId="render_level_stages_test"
|
||||
modId="trade_with_villager_event_test"
|
||||
[[mods]]
|
||||
modId="chunk_event_load_new_chunk_test"
|
||||
[[mods]]
|
||||
modId="alter_ground_event_test"
|
||||
[[mods]]
|
||||
modId="custom_fluid_container_test"
|
||||
[[mods]]
|
||||
modId="keymapping_test"
|
||||
|
||||
|
||||
# ADD ABOVE THIS LINE
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"parent": "item/generated",
|
||||
"textures": {
|
||||
"layer0": "item/bucket"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user